#include "gns_proxy_proto.h"
#include "gns.h"
+/** SSL **/
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+#include <time.h>
+
#define GNUNET_GNS_PROXY_PORT 7777
/* MHD/cURL defines */
/* regexp */
#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
+#define RE_A_HREF "<a href=\"https?://(([A-Za-z]+[.])+)([+]|zkey)"
#define RE_N_MATCHES 4
/* The usual suspects */
#define HTTPS_PORT 443
+/**
+ * A structure for CA cert/key
+ */
+struct ProxyCA
+{
+ /* The certificate */
+ gnutls_x509_crt_t cert;
+
+ /* The private key */
+ gnutls_x509_privkey_t key;
+};
+
+
+/**
+ * Structure for GNS certificates
+ */
+struct ProxyGNSCertificate
+{
+ /* The certificate as PEM */
+ char cert[10 * 1024];
+
+ /* The private key as PEM */
+ char key[10 * 1024];
+};
+
/**
* A structure for socks requests
/* Optional header replacements for curl (LEHO) */
struct curl_slist *headers;
+ /* Optional resolver replacements for curl (LEHO) */
+ struct curl_slist *resolver;
+
/* The URL to fetch */
char url[2048];
};
/* The port the proxy is running on (default 7777) */
-unsigned long port = GNUNET_GNS_PROXY_PORT;
+static unsigned long port = GNUNET_GNS_PROXY_PORT;
+
+/* The CA file (pem) to use for the proxy CA */
+static char* cafile;
/* The listen socket of the proxy */
static struct GNUNET_NETWORK_Handle *lsock;
static regex_t re_dotplus;
/* The users local GNS zone hash */
-struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
+static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
+
+/* The CA for SSL certificate generation */
+static struct ProxyCA proxy_ca;
/**
* Checks if name is in tld
}
memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
- memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
+
+ /* If .+ extend with authority */
+ if (0 == strcmp (ctask->buffer_ptr+m[1].rm_eo, "+"))
+ {
+ memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
+ strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
+ ctask->authority);
+ }
+ /* If .zkey simply copy the name */
+ else
+ {
+ memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_TLD_ZKEY)));
+ }
ctask->is_postprocessing = GNUNET_YES;
ctask->pp_finished = GNUNET_NO;
+
+ GNUNET_GNS_shorten (gns_handle,
+ ctask->pp_buf,
+ &process_shorten,
+ ctask);
//postprocess_name(ctask, NULL);
- ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
+ //ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
return 0;
}
int i;
CURLcode ret;
CURLMcode mret;
+ struct hostent *phost;
+ char *ssl_ip;
+ char resolvename[512];
+ char curlurl[512];
ctask->headers = NULL;
}
+ if (ctask->mhd->is_ssl)
+ {
+ phost = (struct hostent*)gethostbyname (ctask->host);
+ ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "SSL target server: %s\n", ssl_ip);
+ sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
+ ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
+ curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
+ sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
+ curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
+ }
+
if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
/* no need to abort if the above failed */
- sprintf (curlurl, "http://%s%s", host, url);
+ if (GNUNET_NO == ctask->mhd->is_ssl)
+ sprintf (curlurl, "http://%s%s", host, url);
strcpy (ctask->host, host);
- strcpy (ctask->url, curlurl);
+ strcpy (ctask->url, url);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Adding new curl task for %s\n", curlurl);
return buffer;
}
+/** SSL stuff **/
+
+/**
+ * Load PEM key from file
+ *
+ * @param key where to store the data
+ * @param keyfile path to the PEM file
+ */
+static void
+load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
+{
+ gnutls_datum_t key_data;
+ int ret;
+
+ key_data.data = (unsigned char*)load_file (keyfile);
+ key_data.size = strlen ((char*)key_data.data);
+
+ ret = gnutls_x509_privkey_import (key, &key_data,
+ GNUTLS_X509_FMT_PEM);
+
+ if (GNUTLS_E_SUCCESS != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unable to import private key %s\n", keyfile);
+ GNUNET_break (0);
+ }
+}
+
+/**
+ * Load cert from file
+ *
+ * @param crt struct to store data in
+ * @param certfile path to pem file
+ */
+static void
+load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
+{
+ gnutls_datum_t cert_data;
+ int ret;
+
+ cert_data.data = (unsigned char*)load_file (certfile);
+ cert_data.size = strlen ((char*)cert_data.data);
+
+ ret = gnutls_x509_crt_import (crt, &cert_data,
+ GNUTLS_X509_FMT_PEM);
+ if (GNUTLS_E_SUCCESS != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unable to import certificate %s\n", certfile);
+ GNUNET_break (0);
+ }
+
+}
+
+
+/**
+ * Generate new certificate for specific name
+ *
+ * @param name the subject name to generate a cert for
+ * @return a struct holding the PEM data
+ */
+static struct ProxyGNSCertificate *
+generate_gns_certificate (const char *name)
+{
+
+ int ret;
+ unsigned int serial;
+ unsigned int bits;
+ size_t key_buf_size;
+ size_t cert_buf_size;
+ gnutls_x509_crt_t request;
+ gnutls_x509_privkey_t rsa;
+ time_t etime;
+ struct tm *tm_data;
+
+ ret = gnutls_x509_crt_init (&request);
+
+ if (GNUTLS_E_SUCCESS != ret)
+ {
+ GNUNET_break (0);
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating key\n");
+ gnutls_x509_privkey_init (&rsa);
+ bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_NORMAL);
+ ret = gnutls_x509_privkey_generate (rsa, GNUTLS_PK_RSA, bits, 0);
+
+ if (GNUTLS_E_SUCCESS != ret)
+ {
+ GNUNET_break (0);
+ }
+
+ ret = gnutls_x509_crt_set_key (request, rsa);
+
+ if (GNUTLS_E_SUCCESS != ret)
+ {
+ GNUNET_break (0);
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
+
+ struct ProxyGNSCertificate *pgc =
+ GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
+
+ gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
+ 0, "DE", 2);
+
+ gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
+ 0, "GNUnet", 6);
+
+ gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
+ 0, name, strlen (name));
+
+ ret = gnutls_x509_crt_set_version (request, 3);
+
+ ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
+
+ etime = time (NULL);
+ tm_data = localtime (&etime);
+
+
+ ret = gnutls_x509_crt_set_serial (request,
+ &serial,
+ sizeof (serial));
+
+ ret = gnutls_x509_crt_set_activation_time (request,
+ etime);
+ tm_data->tm_year++;
+ etime = mktime (tm_data);
+
+ if (-1 == etime)
+ {
+ GNUNET_break (0);
+ }
+
+ ret = gnutls_x509_crt_set_expiration_time (request,
+ etime);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
+
+ ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
+
+ key_buf_size = sizeof (pgc->key);
+ cert_buf_size = sizeof (pgc->cert);
+
+ gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
+ pgc->cert, &cert_buf_size);
+
+ gnutls_x509_privkey_export (rsa, GNUTLS_X509_FMT_PEM,
+ pgc->key, &key_buf_size);
+
+
+ gnutls_x509_crt_deinit (request);
+ gnutls_x509_privkey_deinit (rsa);
+
+ return pgc;
+
+}
+
+
/**
* Adds a socket to an SSL MHD instance
* It is important the the domain name is
add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
{
struct MhdHttpList *hd = NULL;
-
- static char *key_pem;
- static char *cert_pem;
-
- key_pem = load_file ("server.key");
- cert_pem = load_file ("server.pem");
+ struct ProxyGNSCertificate *pgc;
for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
{
if (NULL == hd)
{
/* Start new MHD */
- /* TODO: create cert, start SSL MHD */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "No previous SSL instance found... starting new one\n");
+ "No previous SSL instance found... starting new one for %s\n",
+ domain);
+
+ pgc = generate_gns_certificate (domain);
+
hd = GNUNET_malloc (sizeof (struct MhdHttpList));
hd->is_ssl = GNUNET_YES;
strcpy (hd->domain, domain);
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
MHD_OPTION_NOTIFY_COMPLETED,
NULL, NULL,
- MHD_OPTION_HTTPS_MEM_KEY, key_pem,
- MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+ MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
+ MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
MHD_OPTION_END);
hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Cleaning up cURL task\n");
-
+
if (ctask->curl != NULL)
curl_easy_cleanup (ctask->curl);
ctask->curl = NULL;
struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
- "ZONEKEY", &keyfile))
+ "ZONEKEY", &keyfile))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unable to load zone key config value!\n");
key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
GNUNET_CRYPTO_short_hash(&pkey,
- sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
- &local_gns_zone);
+ sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+ &local_gns_zone);
zone = &local_gns_zone;
GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
return GNUNET_YES;
}
+
+
/**
* Main function that will be run
*
struct sockaddr_in sa;
struct MhdHttpList *hd;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Loading CA\n");
+
+ gnutls_global_init ();
+
+ load_cert_from_file (proxy_ca.cert, cafile);
+ load_key_from_file (proxy_ca.key, cafile);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Loading Template\n");
+
compile_regex (&re_dotplus, (char*) RE_DOTPLUS);
gns_handle = GNUNET_GNS_connect (cfg);
{'p', "port", NULL,
gettext_noop ("listen on specified port"), 1,
&GNUNET_GETOPT_set_string, &port},
+ {'a', "authority", NULL,
+ gettext_noop ("pem file to use as CA"), 1,
+ &GNUNET_GETOPT_set_string, &cafile},
GNUNET_GETOPT_OPTION_END
};
int ret;
+ if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+ return 2;
+
GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
ret =
(GNUNET_OK ==