*
* TODO:
* - double-check queueing logic
- * - actually check SSL certificates (#3038)
*/
#include "platform.h"
#include <microhttpd.h>
#include <gnutls/x509.h>
#include <gnutls/abstract.h>
#include <gnutls/crypto.h>
+#if HAVE_GNUTLS_DANE
+#include <gnutls/dane.h>
+#endif
#include <regex.h>
#include "gnunet_util_lib.h"
#include "gnunet_gns_service.h"
*/
char *leho;
+ /**
+ * Payload of the (last) DANE record encountered.
+ */
+ char *dane_data;
+
/**
* The URL to fetch
*/
*/
unsigned int response_code;
+ /**
+ * Number of bytes in @e dane_data.
+ */
+ size_t dane_data_len;
+
/**
* Number of bytes already in read buffer
*/
static char *cafile_opt;
/**
- * The listen socket of the proxy
+ * The listen socket of the proxy for IPv4
+ */
+static struct GNUNET_NETWORK_Handle *lsock4;
+
+/**
+ * The listen socket of the proxy for IPv6
+ */
+static struct GNUNET_NETWORK_Handle *lsock6;
+
+/**
+ * The listen task ID for IPv4
*/
-static struct GNUNET_NETWORK_Handle *lsock;
+static GNUNET_SCHEDULER_TaskIdentifier ltask4;
/**
- * The listen task ID
+ * The listen task ID for IPv6
*/
-static GNUNET_SCHEDULER_TaskIdentifier ltask;
+static GNUNET_SCHEDULER_TaskIdentifier ltask6;
/**
* The cURL download task (curl multi API).
GNUNET_free_non_null (s5r->domain);
GNUNET_free_non_null (s5r->leho);
GNUNET_free_non_null (s5r->url);
+ GNUNET_free_non_null (s5r->dane_data);
GNUNET_free (s5r);
}
static int
check_ssl_certificate (struct Socks5Request *s5r)
{
- unsigned int i;
- union {
- gnutls_session_t session;
- struct curl_slist * to_slist;
- } gptr;
unsigned int cert_list_size;
const gnutls_datum_t *chainp;
+ const struct curl_tlssessioninfo *tlsinfo;
+ char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
+ size_t size;
+ gnutls_x509_crt_t x509_cert;
+ int rc;
+ const char *name;
- gptr.to_slist = NULL;
if (CURLE_OK !=
curl_easy_getinfo (s5r->curl,
- CURLINFO_GNUTLS_SESSION,
- &gptr))
+ CURLINFO_TLS_SESSION,
+ (struct curl_slist **) &tlsinfo))
+ return GNUNET_SYSERR;
+ if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Unsupported CURL SSL backend %d\n"),
+ tlsinfo->backend);
+ return GNUNET_SYSERR;
+ }
+ chainp = gnutls_certificate_get_peers (tlsinfo->internals, &cert_list_size);
+ if ( (! chainp) || (0 == cert_list_size) )
return GNUNET_SYSERR;
- chainp = gnutls_certificate_get_peers(gptr.session, &cert_list_size);
- if(!chainp)
+ size = sizeof (certdn);
+ /* initialize an X.509 certificate structure. */
+ gnutls_x509_crt_init (&x509_cert);
+ gnutls_x509_crt_import (x509_cert,
+ chainp,
+ GNUTLS_X509_FMT_DER);
+
+ if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
+ GNUTLS_OID_X520_COMMON_NAME,
+ 0, /* the first and only one */
+ 0 /* no DER encoding */,
+ certdn,
+ &size)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to fetch CN from cert: %s\n"),
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit (x509_cert);
return GNUNET_SYSERR;
+ }
+ /* check for TLSA/DANE records */
+#if HAVE_GNUTLS_DANE
+ if (NULL != s5r->dane_data)
+ {
+ char *dd[] = { s5r->dane_data, NULL };
+ int dlen[] = { s5r->dane_data_len, 0};
+ dane_state_t dane_state;
+ dane_query_t dane_query;
+ unsigned int verify;
- for(i=0;i<cert_list_size;i++) {
- gnutls_x509_crt_t cert;
- gnutls_datum_t dn;
-
- if(GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&cert)) {
- if((GNUTLS_E_SUCCESS ==
- gnutls_x509_crt_import (cert, &chainp[i],
- GNUTLS_X509_FMT_DER)) &&
- (GNUTLS_E_SUCCESS ==
- gnutls_x509_crt_print (cert,
- GNUTLS_CRT_PRINT_FULL,
- &dn))) {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Certificate #%d: %.*s", i, dn.size, dn.data);
- gnutls_free (dn.data);
- gnutls_x509_crt_deinit (cert);
+ /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
+ if (0 != (rc = dane_state_init (&dane_state,
+#ifdef DANE_F_IGNORE_DNSSEC
+ DANE_F_IGNORE_DNSSEC |
+#endif
+ DANE_F_IGNORE_LOCAL_RESOLVER)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to initialize DANE: %s\n"),
+ dane_strerror(rc));
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != (rc = dane_raw_tlsa (dane_state,
+ &dane_query,
+ dd,
+ dlen,
+ GNUNET_YES,
+ GNUNET_NO)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to parse DANE record: %s\n"),
+ dane_strerror(rc));
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != (rc = dane_verify_crt_raw (dane_state,
+ chainp,
+ cert_list_size,
+ gnutls_certificate_type_get (tlsinfo->internals),
+ dane_query,
+ 0, 0,
+ &verify)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to verify TLS connection using DANE: %s\n"),
+ dane_strerror(rc));
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != verify)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed DANE verification failed with GnuTLS verify status code: %u\n"),
+ verify);
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ /* success! */
+ }
+ else
+#endif
+ {
+ /* try LEHO or ordinary domain name X509 verification */
+ name = s5r->domain;
+ if (NULL != s5r->leho)
+ name = s5r->leho;
+ if (NULL != name)
+ {
+ if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
+ name)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("SSL certificate subject name (%s) does not match `%s'\n"),
+ certdn,
+ name);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
}
}
+ else
+ {
+ /* we did not even have the domain name!? */
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
}
+ gnutls_x509_crt_deinit (x509_cert);
return GNUNET_OK;
}
GNUNET_break (CURLE_OK ==
curl_easy_getinfo (msg->easy_handle,
CURLINFO_PRIVATE,
- &s5r));
+ (char **) &s5r ));
if (NULL == s5r)
{
GNUNET_break (0);
"Suspending cURL multi loop, no more events pending\n");
return; /* nothing more in progress */
}
- curl_download_prepare();
+ curl_download_prepare ();
}
size_t *upload_data_size,
void **con_cls)
{
- /* struct MhdHttpList* hd = cls; */
struct Socks5Request *s5r = *con_cls;
char *curlurl;
char ipstring[INET6_ADDRSTRLEN];
curl_easy_setopt (s5r->curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
- curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 0); // FIXME: remove later
+ curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 0);
GNUNET_asprintf (&curlurl,
(HTTPS_PORT != s5r->port)
? "http://%s:%d%s"
return s5r;
}
}
+ GNUNET_break (0);
return NULL;
}
int ret;
key_data.data = load_file (keyfile, &key_data.size);
+ if (NULL == key_data.data)
+ return GNUNET_SYSERR;
ret = gnutls_x509_privkey_import (key, &key_data,
GNUTLS_X509_FMT_PEM);
if (GNUTLS_E_SUCCESS != ret)
int ret;
cert_data.data = load_file (certfile, &cert_data.size);
+ if (NULL == cert_data.data)
+ return GNUNET_SYSERR;
ret = gnutls_x509_crt_import (crt, &cert_data,
GNUTLS_X509_FMT_PEM);
if (GNUTLS_E_SUCCESS != ret)
GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
pgc = GNUNET_new (struct ProxyGNSCertificate);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
- 0, "TNR", 2);
+ 0, "ZZ", 2);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
0, "GNU Name System", 4);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
}
+/**
+ * Function called by MHD with errors, suppresses them all.
+ *
+ * @param cls closure
+ * @param fm format string (`printf()`-style)
+ * @param ap arguments to @a fm
+ */
+static void
+mhd_error_log_callback (void *cls,
+ const char *fm,
+ va_list ap)
+{
+ /* do nothing */
+}
+
+
/**
* Lookup (or create) an SSL MHD instance for a particular domain.
*
* @param domain the domain the SSL daemon has to serve
- * @return NULL on errro
+ * @return NULL on error
*/
static struct MhdHttpList *
lookup_ssl_httpd (const char* domain)
struct MhdHttpList *hd;
struct ProxyGNSCertificate *pgc;
+ if (NULL == domain)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
if ( (NULL != hd->domain) &&
(0 == strcmp (hd->domain, domain)) )
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
+ MHD_OPTION_EXTERNAL_LOGGER, &mhd_error_log_callback, NULL,
MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
MHD_OPTION_END);
static void
handle_gns_result (void *cls,
uint32_t rd_count,
- const struct GNUNET_NAMESTORE_RecordData *rd)
+ const struct GNUNET_GNSRECORD_Data *rd)
{
struct Socks5Request *s5r = cls;
uint32_t i;
- const struct GNUNET_NAMESTORE_RecordData *r;
+ const struct GNUNET_GNSRECORD_Data *r;
int got_ip;
s5r->gns_lookup = NULL;
#endif
}
break;
- case GNUNET_NAMESTORE_TYPE_VPN:
+ case GNUNET_GNSRECORD_TYPE_VPN:
GNUNET_break (0); /* should have been translated within GNS */
break;
- case GNUNET_NAMESTORE_TYPE_LEHO:
+ case GNUNET_GNSRECORD_TYPE_LEHO:
GNUNET_free_non_null (s5r->leho);
s5r->leho = GNUNET_strndup (r->data,
r->data_size);
break;
+ case GNUNET_DNSPARSER_TYPE_TLSA:
+ GNUNET_free_non_null (s5r->dane_data);
+ s5r->dane_data_len = r->data_size;
+ s5r->dane_data = GNUNET_malloc (r->data_size);
+ memcpy (s5r->dane_data,
+ r->data,
+ r->data_size);
+ break;
default:
/* don't care */
break;
struct sockaddr_in *in;
s5r->port = ntohs (*port);
+ if (HTTPS_PORT == s5r->port)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("SSL connection to plain IPv4 address requested\n"));
+ signal_socks_failure (s5r,
+ SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+ return;
+ }
alen = sizeof (struct in_addr);
if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
alen + sizeof (uint16_t))
struct sockaddr_in6 *in;
s5r->port = ntohs (*port);
+ if (HTTPS_PORT == s5r->port)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("SSL connection to plain IPv4 address requested\n"));
+ signal_socks_failure (s5r,
+ SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+ return;
+ }
alen = sizeof (struct in6_addr);
if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
alen + sizeof (uint16_t))
/**
* Accept new incoming connections
*
- * @param cls the closure
+ * @param cls the closure with the lsock4 or lsock6
* @param tc the scheduler context
*/
static void
do_accept (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ struct GNUNET_NETWORK_Handle *lsock = cls;
struct GNUNET_NETWORK_Handle *s;
struct Socks5Request *s5r;
- ltask = GNUNET_SCHEDULER_NO_TASK;
+ if (lsock == lsock4)
+ ltask4 = GNUNET_SCHEDULER_NO_TASK;
+ else
+ ltask6 = GNUNET_SCHEDULER_NO_TASK;
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
return;
- ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- lsock,
- &do_accept, NULL);
+ if (lsock == lsock4)
+ ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock,
+ &do_accept, lsock);
+ else
+ ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock,
+ &do_accept, lsock);
s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
if (NULL == s)
{
kill_httpd (mhd_httpd_head);
while (NULL != s5r_head)
cleanup_s5r (s5r_head);
- if (NULL != lsock)
+ if (NULL != lsock4)
{
- GNUNET_NETWORK_socket_close (lsock);
- lsock = NULL;
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+ if (NULL != lsock6)
+ {
+ GNUNET_NETWORK_socket_close (lsock6);
+ lsock6 = NULL;
}
if (NULL != id_op)
{
GNUNET_SCHEDULER_cancel (curl_download_task);
curl_download_task = GNUNET_SCHEDULER_NO_TASK;
}
- if (GNUNET_SCHEDULER_NO_TASK != ltask)
+ if (GNUNET_SCHEDULER_NO_TASK != ltask4)
+ {
+ GNUNET_SCHEDULER_cancel (ltask4);
+ ltask4 = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != ltask6)
{
- GNUNET_SCHEDULER_cancel (ltask);
- ltask = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_cancel (ltask6);
+ ltask6 = GNUNET_SCHEDULER_NO_TASK;
}
gnutls_x509_crt_deinit (proxy_ca.cert);
gnutls_x509_privkey_deinit (proxy_ca.key);
struct MhdHttpList *hd;
/* Open listen socket for socks proxy */
- lsock = bind_v6 ();
- if (NULL == lsock)
- lsock = bind_v4 ();
- if (NULL == lsock)
+ lsock6 = bind_v6 ();
+ if (NULL == lsock6)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+ else
{
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_NETWORK_socket_close (lsock6);
+ lsock6 = NULL;
+ }
+ else
+ {
+ ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock6, &do_accept, lsock6);
+ }
+ }
+ lsock4 = bind_v4 ();
+ if (NULL == lsock4)
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
- GNUNET_SCHEDULER_shutdown ();
- return;
+ else
+ {
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+ else
+ {
+ ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock4, &do_accept, lsock4);
+ }
}
- if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
+ if ( (NULL == lsock4) &&
+ (NULL == lsock6) )
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
GNUNET_SCHEDULER_shutdown ();
return;
}
- ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- lsock, &do_accept, NULL);
-
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
char* cafile;
cfg = c;
+
if (NULL == (curl_multi = curl_multi_init ()))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,