diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index d3f89bcef..7fa42829d 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -62,6 +62,7 @@ dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ ocsp.c ocsp.h validate.c validate.h \ dns-stuff.c dns-stuff.h \ http.c http.h \ + http-ntbtls.c \ ks-action.c ks-action.h ks-engine.h \ ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index bb07656a5..718296d0f 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -1468,6 +1468,7 @@ dirmngr_exit (int rc) void dirmngr_init_default_ctrl (ctrl_t ctrl) { + ctrl->magic = SERVER_CONTROL_MAGIC; if (opt.http_proxy) ctrl->http_proxy = xstrdup (opt.http_proxy); } @@ -1478,6 +1479,8 @@ dirmngr_deinit_default_ctrl (ctrl_t ctrl) { if (!ctrl) return; + ctrl->magic = 0xdeadbeef; + xfree (ctrl->http_proxy); ctrl->http_proxy = NULL; } diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 19d2303ca..57e3372a0 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -168,12 +168,19 @@ typedef struct cert_ref_s *cert_ref_t; /* Forward references; access only through server.c. */ struct server_local_s; +#if SIZEOF_UNSIGNED_LONG == 8 +# define SERVER_CONTROL_MAGIC 0x6469726d6e677220 +#else +# define SERVER_CONTROL_MAGIC 0x6469726d +#endif + /* Connection control structure. */ struct server_control_s { - int refcount; /* Count additional references to this object. */ - int no_server; /* We are not running under server control. */ - int status_fd; /* Only for non-server mode. */ + unsigned long magic;/* Always has SERVER_CONTROL_MAGIC. */ + int refcount; /* Count additional references to this object. */ + int no_server; /* We are not running under server control. */ + int status_fd; /* Only for non-server mode. */ struct server_local_s *server_local; int force_crl_refresh; /* Always load a fresh CRL. */ @@ -213,6 +220,15 @@ gpg_error_t dirmngr_status (ctrl_t ctrl, const char *keyword, ...); gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text); gpg_error_t dirmngr_tick (ctrl_t ctrl); +/*-- http-ntbtls.c --*/ +/* Note that we don't use a callback for gnutls. */ + +gpg_error_t gnupg_http_tls_verify_cb (void *opaque, + http_t http, + http_session_t session, + unsigned int flags, + void *tls_context); + /*-- loadswdb.c --*/ gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force); diff --git a/dirmngr/http-ntbtls.c b/dirmngr/http-ntbtls.c new file mode 100644 index 000000000..5686877ec --- /dev/null +++ b/dirmngr/http-ntbtls.c @@ -0,0 +1,109 @@ +/* http-ntbtls.c - Support for using NTBTLS with http.c + * Copyright (C) 2017 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +#include +#include +#include + +#include "dirmngr.h" +#include "certcache.h" +#include "validate.h" + +#ifdef HTTP_USE_NTBTLS +# include + + + +/* The callback used to verify the peer's certificate. */ +gpg_error_t +gnupg_http_tls_verify_cb (void *opaque, + http_t http, + http_session_t session, + unsigned int http_flags, + void *tls_context) +{ + ctrl_t ctrl = opaque; + gpg_error_t err; + int idx; + ksba_cert_t cert; + ksba_cert_t hostcert = NULL; + unsigned int validate_flags; + + (void)http; + (void)session; + + log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC); + + /* Get the peer's certs fron ntbtls. */ + for (idx = 0; + (cert = ntbtls_x509_get_peer_cert (tls_context, idx)); idx++) + { + if (!idx) + hostcert = cert; + else + { + /* Quick hack to make verification work by inserting the supplied + * certs into the cache. FIXME! */ + cache_cert (cert); + ksba_cert_release (cert); + } + } + if (!idx) + { + err = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + validate_flags = VALIDATE_FLAG_TLS; + /* if ((http_flags & HTTP_FLAG_TRUST_DEF)) */ + /* validate_flags |= VALIDATE_FLAG_??; */ + if ((http_flags & HTTP_FLAG_TRUST_SYS)) + validate_flags |= VALIDATE_FLAG_SYSTRUST; + + /* FIXME: For now we don't use CRLs. */ + validate_flags |= VALIDATE_FLAG_NOCRLCHECK; + + err = validate_cert_chain (ctrl, hostcert, NULL, validate_flags, NULL); + + leave: + ksba_cert_release (hostcert); + return err; +} + + +#else /*!HTTP_USE_NTBTLS*/ + +/* Dummy function used when not build without ntbtls support. */ +gpg_error_t +gnupg_http_tls_verify_cb (void *opaque, + http_t http, + http_session_t session, + unsigned int flags, + void *tls_context) +{ + (void)opaque; + (void)http; + (void)session; + (void)flags; + (void)tls_context; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} +#endif /*!HTTP_USE_NTBTLS*/ diff --git a/dirmngr/http.c b/dirmngr/http.c index 51aec7e7c..e7c6d4224 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -413,6 +413,21 @@ my_gnutls_write (gnutls_transport_ptr_t ptr, const void *buffer, size_t size) #endif /*HTTP_USE_GNUTLS*/ +#ifdef HTTP_USE_NTBTLS +/* Connect the ntbls callback to our generic callback. */ +static gpg_error_t +my_ntbtls_verify_cb (void *opaque, ntbtls_t tls, unsigned int verify_flags) +{ + http_t hd = opaque; + log_assert (hd && hd->session && hd->session->verify_cb); + return hd->session->verify_cb (hd->session->verify_cb_value, + hd, hd->session, + (hd->flags | hd->session->flags), + tls); +} +#endif /*HTTP_USE_NTBTLS*/ + + /* This notification function is called by estream whenever stream is @@ -632,91 +647,16 @@ http_session_new (http_session_t *r_session, #if HTTP_USE_NTBTLS { - x509_cert_t ca_chain; - char line[256]; - estream_t fp, mem_p; - size_t nread, nbytes; - struct b64state state; - void *buf; - size_t buflen; - char *pemname; - - pemname = make_filename_try (gnupg_datadir (), - "sks-keyservers.netCA.pem", NULL); - if (!pemname) - { - err = gpg_error_from_syserror (); - log_error ("setting CA from file '%s' failed: %s\n", - pemname, gpg_strerror (err)); - goto leave; - } - - fp = es_fopen (pemname, "r"); - if (!fp) - { - err = gpg_error_from_syserror (); - log_error ("can't open '%s': %s\n", pemname, gpg_strerror (err)); - xfree (pemname); - goto leave; - } - xfree (pemname); - - mem_p = es_fopenmem (0, "r+b"); - err = b64dec_start (&state, "CERTIFICATE"); - if (err) - { - log_error ("b64dec failure: %s\n", gpg_strerror (err)); - goto leave; - } - - while ( (nread = es_fread (line, 1, DIM (line), fp)) ) - { - err = b64dec_proc (&state, line, nread, &nbytes); - if (err) - { - if (gpg_err_code (err) == GPG_ERR_EOF) - break; - - log_error ("b64dec failure: %s\n", gpg_strerror (err)); - es_fclose (fp); - es_fclose (mem_p); - goto leave; - } - else if (nbytes) - es_fwrite (line, 1, nbytes, mem_p); - } - err = b64dec_finish (&state); - if (err) - { - log_error ("b64dec failure: %s\n", gpg_strerror (err)); - es_fclose (fp); - es_fclose (mem_p); - goto leave; - } - - es_fclose_snatch (mem_p, &buf, &buflen); - es_fclose (fp); - - err = ntbtls_x509_cert_new (&ca_chain); - if (err) - { - log_error ("ntbtls_x509_new failed: %s\n", gpg_strerror (err)); - xfree (buf); - goto leave; - } - - err = ntbtls_x509_append_cert (ca_chain, buf, buflen); - xfree (buf); + (void)intended_hostname; /* Not needed because we do not preload + * certificates. */ err = ntbtls_new (&sess->tls_session, NTBTLS_CLIENT); if (err) { log_error ("ntbtls_new failed: %s\n", gpg_strerror (err)); - ntbtls_x509_cert_release (ca_chain); goto leave; } - err = ntbtls_set_ca_chain (sess->tls_session, ca_chain, NULL); } #elif HTTP_USE_GNUTLS { @@ -1819,6 +1759,21 @@ send_request (http_t hd, const char *httphost, const char *auth, return err; } +#ifdef HTTP_USE_NTBTLS + if (hd->session->verify_cb) + { + err = ntbtls_set_verify_cb (hd->session->tls_session, + my_ntbtls_verify_cb, hd); + if (err) + { + log_error ("ntbtls_set_verify_cb failed: %s\n", + gpg_strerror (err)); + xfree (proxy_authstr); + return err; + } + } +#endif /*HTTP_USE_NTBTLS*/ + while ((err = ntbtls_handshake (hd->session->tls_session))) { switch (err) @@ -1833,12 +1788,18 @@ send_request (http_t hd, const char *httphost, const char *auth, hd->session->verify.done = 0; - /* Try the available verify callbacks until one returns success - * or a real error. */ + * or a real error. Note that NTBTLS does the verification + * during the handshake via */ +#ifdef HTTP_USE_NTBTLS + err = 0; /* Fixme check that the CB has been called. */ +#else err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif - if (hd->session->verify_cb) + if (hd->session->verify_cb + && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR + && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) err = hd->session->verify_cb (hd->session->verify_cb_value, hd, hd->session, (hd->flags | hd->session->flags), diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index b342f0968..4ca1e0025 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -1124,7 +1124,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, *r_fp = NULL; err = http_session_new (&session, httphost, HTTP_FLAG_TRUST_DEF, - NULL, ctrl); + gnupg_http_tls_verify_cb, ctrl); if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index f070019c1..9352a0f18 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -77,7 +77,7 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) /* Note that we only use the system provided certificates with the * fetch command. */ err = http_session_new (&session, NULL, HTTP_FLAG_TRUST_SYS, - NULL, ctrl); + gnupg_http_tls_verify_cb, ctrl); if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c index 8b1d89ac0..464112198 100644 --- a/dirmngr/t-http.c +++ b/dirmngr/t-http.c @@ -42,7 +42,6 @@ #include "logging.h" #include "http.h" - #if HTTP_USE_NTBTLS # include #elif HTTP_USE_GNUTLS @@ -118,6 +117,56 @@ my_gnutls_log (int level, const char *text) } #endif + +static gpg_error_t +my_http_tls_verify_cb (void *opaque, + http_t http, + http_session_t session, + unsigned int http_flags, + void *tls_context) +{ + gpg_error_t err; + int idx; + ksba_cert_t cert; + ksba_cert_t hostcert = NULL; + + (void)opaque; + (void)http; + (void)session; + + + /* Get the peer's certs fron ntbtls. */ + for (idx = 0; + (cert = ntbtls_x509_get_peer_cert (tls_context, idx)); idx++) + { + if (!idx) + { + log_info ("Received host certificate\n"); + hostcert = cert; + } + else + { + + log_info ("Received additional certificate\n"); + ksba_cert_release (cert); + } + } + if (!idx) + { + err = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + err = 0; + + leave: + ksba_cert_release (hostcert); + log_info ("my_http_tls_verify_cb returns: %s\n", gpg_strerror (err)); + return err; +} + + + /* Prepend FNAME with the srcdir environment variable's value and return an allocated filename. */ static char * @@ -142,8 +191,7 @@ main (int argc, char **argv) { int last_argc = -1; gpg_error_t err; - int rc; - parsed_uri_t uri; + int rc; parsed_uri_t uri; uri_tuple_t r; http_t hd; int c; @@ -171,7 +219,7 @@ main (int argc, char **argv) "Options:\n" " --verbose print timings etc.\n" " --debug flyswatter\n" - " --gnutls-debug N use GNUTLS debug level N\n" + " --tls-debug N use TLS debug level N\n" " --cacert FNAME expect CA certificate in file FNAME\n" " --no-verify do not verify the certificate\n" " --force-tls use HTTP_FLAG_FORCE_TLS\n" @@ -191,7 +239,7 @@ main (int argc, char **argv) debug++; argc--; argv++; } - else if (!strcmp (*argv, "--gnutls-debug")) + else if (!strcmp (*argv, "--tls-debug")) { argc--; argv++; if (argc) @@ -248,9 +296,11 @@ main (int argc, char **argv) assuan_sock_init (); #if HTTP_USE_NTBTLS - - (void)err; - + log_info ("new session.\n"); + err = http_session_new (&session, NULL, HTTP_FLAG_TRUST_DEF, + my_http_tls_verify_cb, NULL); + if (err) + log_error ("http_session_new failed: %s\n", gpg_strerror (err)); ntbtls_set_debug (tls_dbg, NULL, NULL); #elif HTTP_USE_GNUTLS