diff options
Diffstat (limited to 'dirmngr')
-rw-r--r-- | dirmngr/ChangeLog-2011 | 4 | ||||
-rw-r--r-- | dirmngr/Makefile.am | 7 | ||||
-rw-r--r-- | dirmngr/certcache.c | 597 | ||||
-rw-r--r-- | dirmngr/certcache.h | 32 | ||||
-rw-r--r-- | dirmngr/crlcache.c | 6 | ||||
-rw-r--r-- | dirmngr/crlfetch.c | 31 | ||||
-rw-r--r-- | dirmngr/dirmngr.c | 101 | ||||
-rw-r--r-- | dirmngr/dirmngr.h | 34 | ||||
-rw-r--r-- | dirmngr/dns-stuff.c | 81 | ||||
-rw-r--r-- | dirmngr/dns-stuff.h | 5 | ||||
-rw-r--r-- | dirmngr/http-ntbtls.c | 124 | ||||
-rw-r--r-- | dirmngr/http.c | 433 | ||||
-rw-r--r-- | dirmngr/http.h | 17 | ||||
-rw-r--r-- | dirmngr/ks-engine-finger.c | 4 | ||||
-rw-r--r-- | dirmngr/ks-engine-hkp.c | 207 | ||||
-rw-r--r-- | dirmngr/ks-engine-http.c | 8 | ||||
-rw-r--r-- | dirmngr/ks-engine-ldap.c | 8 | ||||
-rw-r--r-- | dirmngr/ldap.c | 22 | ||||
-rw-r--r-- | dirmngr/loadswdb.c | 7 | ||||
-rw-r--r-- | dirmngr/misc.c | 2 | ||||
-rw-r--r-- | dirmngr/ocsp.c | 5 | ||||
-rw-r--r-- | dirmngr/server.c | 166 | ||||
-rw-r--r-- | dirmngr/t-http.c | 84 | ||||
-rw-r--r-- | dirmngr/validate.c | 235 | ||||
-rw-r--r-- | dirmngr/validate.h | 43 |
25 files changed, 1683 insertions, 580 deletions
diff --git a/dirmngr/ChangeLog-2011 b/dirmngr/ChangeLog-2011 index a793a33..243f2b5 100644 --- a/dirmngr/ChangeLog-2011 +++ b/dirmngr/ChangeLog-2011 @@ -1497,7 +1497,7 @@ * dirmngr-client.c (inq_cert): Ignore "SENDCERT" and "SENDISSUERCERT". - * server.c (do_get_cert_local): Limit the length of a retruned + * server.c (do_get_cert_local): Limit the length of a returned certificate. Return NULL without an error if an empry value has been received. @@ -1897,7 +1897,7 @@ corrupted CRL files. (open_dir): Read the new dbfile hash field. - * src/crlfetch.c (crl_fetch, crl_fetch_default): Changed to retrun + * src/crlfetch.c (crl_fetch, crl_fetch_default): Changed to return a stream. (fun_reader, fun_closer, setup_funopen): New. * src/server.c (inquire_cert): Changed to use the new stream interface diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index d3f89bc..8d22cc4 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 @@ -138,12 +139,14 @@ endif # http tests +# We need to add the KSBA flags in case we are building against GNUTLS. +# In that case NTBTLS flags are empty, but we need ksba anyway. t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \ $(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \ - $(GPG_ERROR_CFLAGS) + $(GPG_ERROR_CFLAGS) $(KSBA_CFLAGS) t_http_LDADD = $(t_common_ldadd) \ - $(NTBTLS_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) + $(NTBTLS_LIBS) $(KSBA_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS) t_ldap_parse_uri_SOURCES = \ t-ldap-parse-uri.c ldap-parse-uri.c ldap-parse-uri.h \ diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index ad85d99..3284ff2 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -1,5 +1,5 @@ /* certcache.c - Certificate caching - * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH + * Copyright (C) 2004, 2005, 2007, 2008, 2017 g10 Code GmbH * * This file is part of DirMngr. * @@ -29,11 +29,11 @@ #include "dirmngr.h" #include "misc.h" +#include "../common/ksba-io-support.h" #include "crlfetch.h" #include "certcache.h" - -#define MAX_EXTRA_CACHED_CERTS 1000 +#define MAX_NONPERM_CACHED_CERTS 1000 /* Constants used to classify search patterns. */ enum pattern_class @@ -66,11 +66,14 @@ struct cert_item_s char *issuer_dn; /* The malloced issuer DN. */ ksba_sexp_t sn; /* The malloced serial number */ char *subject_dn; /* The malloced subject DN - maybe NULL. */ - struct - { - unsigned int loaded:1; /* It has been explicitly loaded. */ - unsigned int trusted:1; /* This is a trusted root certificate. */ - } flags; + + /* If this field is set the certificate has been taken from some + * configuration and shall not be flushed from the cache. */ + unsigned int permanent:1; + + /* If this field is set the certificate is trusted. The actual + * value is a (possible) combination of CERTTRUST_CLASS values. */ + unsigned int trustclasses:4; }; typedef struct cert_item_s *cert_item_t; @@ -88,10 +91,21 @@ static npth_rwlock_t cert_cache_lock; /* Flag to track whether the cache has been initialized. */ static int initialization_done; -/* Total number of certificates loaded during initialization and - cached during operation. */ -static unsigned int total_loaded_certificates; -static unsigned int total_extra_certificates; +/* Total number of non-permanent certificates. */ +static unsigned int total_nonperm_certificates; + + +#ifdef HAVE_W32_SYSTEM +/* We load some functions dynamically. Provide typedefs for tehse + * fucntions. */ +typedef HCERTSTORE (WINAPI *CERTOPENSYSTEMSTORE) + (HCRYPTPROV hProv, LPCSTR szSubsystemProtocol); +typedef PCCERT_CONTEXT (WINAPI *CERTENUMCERTIFICATESINSTORE) + (HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext); +typedef WINBOOL (WINAPI *CERTCLOSESTORE) + (HCERTSTORE hCertStore,DWORD dwFlags); +#endif /*HAVE_W32_SYSTEM*/ + @@ -154,8 +168,8 @@ compare_serialno (ksba_sexp_t serial1, ksba_sexp_t serial2 ) /* Return a malloced canonical S-Expression with the serial number - converted from the hex string HEXSN. Return NULL on memory - error. */ + * converted from the hex string HEXSN. Return NULL on memory + * error. */ ksba_sexp_t hexsn_to_sexp (const char *hexsn) { @@ -205,6 +219,7 @@ cert_compute_fpr (ksba_cert_t cert, unsigned char *digest) } + /* Cleanup one slot. This releases all resourses but keeps the actual slot in the cache marked for reuse. */ static void @@ -224,18 +239,29 @@ clean_cache_slot (cert_item_t ci) cert = ci->cert; ci->cert = NULL; + ci->permanent = 0; + ci->trustclasses = 0; + ksba_cert_release (cert); } /* Put the certificate CERT into the cache. It is assumed that the - cache is locked while this function is called. If FPR_BUFFER is not - NULL the fingerprint of the certificate will be stored there. - FPR_BUFFER neds to point to a buffer of at least 20 bytes. The - fingerprint will be stored on success or when the function returns - gpg_err_code(GPG_ERR_DUP_VALUE). */ + * cache is locked while this function is called. + * + * FROM_CONFIG indicates that CERT is a permanent certificate and + * should stay in the cache. IS_TRUSTED requests that the trusted + * flag is set for the certificate; a value of 1 indicates the + * cert is trusted due to GnuPG mechanisms, a value of 2 indicates + * that it is trusted because it has been taken from the system's + * store of trusted certificates. If FPR_BUFFER is not NULL the + * fingerprint of the certificate will be stored there. FPR_BUFFER + * needs to point to a buffer of at least 20 bytes. The fingerprint + * will be stored on success or when the function returns + * GPG_ERR_DUP_VALUE. */ static gpg_error_t -put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) +put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass, + void *fpr_buffer) { unsigned char help_fpr_buffer[20], *fpr; cert_item_t ci; @@ -243,24 +269,24 @@ put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer; /* If we already reached the caching limit, drop a couple of certs - from the cache. Our dropping strategy is simple: We keep a - static index counter and use this to start looking for - certificates, then we drop 5 percent of the oldest certificates - starting at that index. For a large cache this is a fair way of - removing items. An LRU strategy would be better of course. - Because we append new entries to the head of the list and we want - to remove old ones first, we need to do this from the tail. The - implementation is not very efficient but compared to the long - time it takes to retrieve a certifciate from an external resource - it seems to be reasonable. */ - if (!is_loaded && total_extra_certificates >= MAX_EXTRA_CACHED_CERTS) + * from the cache. Our dropping strategy is simple: We keep a + * static index counter and use this to start looking for + * certificates, then we drop 5 percent of the oldest certificates + * starting at that index. For a large cache this is a fair way of + * removing items. An LRU strategy would be better of course. + * Because we append new entries to the head of the list and we want + * to remove old ones first, we need to do this from the tail. The + * implementation is not very efficient but compared to the long + * time it takes to retrieve a certificate from an external resource + * it seems to be reasonable. */ + if (!permanent && total_nonperm_certificates >= MAX_NONPERM_CACHED_CERTS) { static int idx; cert_item_t ci_mark; int i; unsigned int drop_count; - drop_count = MAX_EXTRA_CACHED_CERTS / 20; + drop_count = MAX_NONPERM_CACHED_CERTS / 20; if (drop_count < 2) drop_count = 2; @@ -270,13 +296,13 @@ put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) { ci_mark = NULL; for (ci = cert_cache[i]; ci; ci = ci->next) - if (ci->cert && !ci->flags.loaded) + if (ci->cert && !ci->permanent) ci_mark = ci; if (ci_mark) { clean_cache_slot (ci_mark); drop_count--; - total_extra_certificates--; + total_nonperm_certificates--; } } if (i==idx) @@ -302,8 +328,6 @@ put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) ci->next = cert_cache[*fpr]; cert_cache[*fpr] = ci; } - else - memset (&ci->flags, 0, sizeof ci->flags); ksba_cert_ref (cert); ci->cert = cert; @@ -316,13 +340,11 @@ put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) return gpg_error (GPG_ERR_INV_CERT_OBJ); } ci->subject_dn = ksba_cert_get_subject (cert, 0); - ci->flags.loaded = !!is_loaded; - ci->flags.trusted = !!is_trusted; + ci->permanent = !!permanent; + ci->trustclasses = trustclass; - if (is_loaded) - total_loaded_certificates++; - else - total_extra_certificates++; + if (!permanent) + total_nonperm_certificates++; return 0; } @@ -330,10 +352,10 @@ put_cert (ksba_cert_t cert, int is_loaded, int is_trusted, void *fpr_buffer) /* Load certificates from the directory DIRNAME. All certificates matching the pattern "*.crt" or "*.der" are loaded. We assume that - certificates are DER encoded and not PEM encapsulated. The cache + certificates are DER encoded and not PEM encapsulated. The cache should be in a locked state when calling this function. */ static gpg_error_t -load_certs_from_dir (const char *dirname, int are_trusted) +load_certs_from_dir (const char *dirname, unsigned int trustclass) { gpg_error_t err; DIR *dir; @@ -390,12 +412,12 @@ load_certs_from_dir (const char *dirname, int are_trusted) continue; } - err = put_cert (cert, 1, are_trusted, NULL); + err = put_cert (cert, 1, trustclass, NULL); if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) log_info (_("certificate '%s' already cached\n"), fname); else if (!err) { - if (are_trusted) + if (trustclass) log_info (_("trusted certificate '%s' loaded\n"), fname); else log_info (_("certificate '%s' loaded\n"), fname); @@ -421,24 +443,280 @@ load_certs_from_dir (const char *dirname, int are_trusted) } +/* Load certificates from FILE. The certificates are expected to be + * PEM encoded so that it is possible to load several certificates. + * TRUSTCLASSES is used to mark the certificates as trusted. The + * cache should be in a locked state when calling this function. + * NO_ERROR repalces an error message when FNAME was not found by an + * information message. */ +static gpg_error_t +load_certs_from_file (const char *fname, unsigned int trustclasses, + int no_error) +{ + gpg_error_t err; + estream_t fp = NULL; + gnupg_ksba_io_t ioctx = NULL; + ksba_reader_t reader; + ksba_cert_t cert = NULL; + + fp = es_fopen (fname, "rb"); + if (!fp) + { + err = gpg_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_ENONET && no_error) + log_info (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); + else + log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); + goto leave; + } + + err = gnupg_ksba_create_reader (&ioctx, + (GNUPG_KSBA_IO_AUTODETECT + | GNUPG_KSBA_IO_MULTIPEM), + fp, &reader); + if (err) + { + log_error ("can't create reader: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Loop to read all certificates from the file. */ + do + { + ksba_cert_release (cert); + cert = NULL; + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_read_der (cert, reader); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + else + log_error (_("can't parse certificate '%s': %s\n"), + fname, gpg_strerror (err)); + goto leave; + } + + err = put_cert (cert, 1, trustclasses, NULL); + if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) + log_info (_("certificate '%s' already cached\n"), fname); + else if (err) + log_error (_("error loading certificate '%s': %s\n"), + fname, gpg_strerror (err)); + else if (opt.verbose > 1) + { + char *p; + + log_info (_("trusted certificate '%s' loaded\n"), fname); + p = get_fingerprint_hexstring_colon (cert); + log_info (_(" SHA1 fingerprint = %s\n"), p); + xfree (p); + + cert_log_name (_(" issuer ="), cert); + cert_log_subject (_(" subject ="), cert); + } + + ksba_reader_clear (reader, NULL, NULL); + } + while (!gnupg_ksba_reader_eof_seen (ioctx)); + + leave: + ksba_cert_release (cert); + gnupg_ksba_destroy_reader (ioctx); + es_fclose (fp); + + return err; +} + + +#ifdef HAVE_W32_SYSTEM +/* Load all certificates from the Windows store named STORENAME. All + * certificates are considered to be system provided trusted + * certificates. The cache should be in a locked state when calling + * this function. */ +static void +load_certs_from_w32_store (const char *storename) +{ + static int init_done; + static CERTOPENSYSTEMSTORE pCertOpenSystemStore; + static CERTENUMCERTIFICATESINSTORE pCertEnumCertificatesInStore; + static CERTCLOSESTORE pCertCloseStore; + gpg_error_t err; + HCERTSTORE w32store; + const CERT_CONTEXT *w32cert; + ksba_cert_t cert = NULL; + unsigned int count = 0; + + /* Initialize on the first use. */ + if (!init_done) + { + static HANDLE hCrypt32; + + init_done = 1; + + hCrypt32 = LoadLibrary ("Crypt32.dll"); + if (!hCrypt32) + { + log_error ("can't load Crypt32.dll: %s\n", w32_strerror (-1)); + return; + } + + pCertOpenSystemStore = (CERTOPENSYSTEMSTORE) + GetProcAddress (hCrypt32, "CertOpenSystemStoreA"); + pCertEnumCertificatesInStore = (CERTENUMCERTIFICATESINSTORE) + GetProcAddress (hCrypt32, "CertEnumCertificatesInStore"); + pCertCloseStore = (CERTCLOSESTORE) + GetProcAddress (hCrypt32, "CertCloseStore"); + if ( !pCertOpenSystemStore + || !pCertEnumCertificatesInStore + || !pCertCloseStore) + { + log_error ("can't load crypt32.dll: %s\n", "missing function"); + pCertOpenSystemStore = NULL; + } + } + + if (!pCertOpenSystemStore) + return; /* Not initialized. */ + + + w32store = pCertOpenSystemStore (0, storename); + if (!w32store) + { + log_error ("can't open certificate store '%s': %s\n", + storename, w32_strerror (-1)); + return; + } + + w32cert = NULL; + while ((w32cert = pCertEnumCertificatesInStore (w32store, w32cert))) + { + if (w32cert->dwCertEncodingType == X509_ASN_ENCODING) + { + ksba_cert_release (cert); + cert = NULL; + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_init_from_mem (cert, + w32cert->pbCertEncoded, + w32cert->cbCertEncoded); + if (err) + { + log_error (_("can't parse certificate '%s': %s\n"), + storename, gpg_strerror (err)); + break; + } + + err = put_cert (cert, 1, CERTTRUST_CLASS_SYSTEM, NULL); + if (!err) + count++; + if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) + log_info (_("certificate '%s' already cached\n"), storename); + else if (err) + log_error (_("error loading certificate '%s': %s\n"), + storename, gpg_strerror (err)); + else if (opt.verbose > 1) + { + char *p; + + log_info (_("trusted certificate '%s' loaded\n"), storename); + p = get_fingerprint_hexstring_colon (cert); + log_info (_(" SHA1 fingerprint = %s\n"), p); + xfree (p); + + cert_log_name (_(" issuer ="), cert); + cert_log_subject (_(" subject ="), cert); + } + } + } + + ksba_cert_release (cert); + pCertCloseStore (w32store, 0); + + if (DBG_X509) + log_debug ("number of certs loaded from store '%s': %u\n", + storename, count); + +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Load the trusted certificates provided by the system. */ +static gpg_error_t +load_certs_from_system (void) +{ +#ifdef HAVE_W32_SYSTEM + + load_certs_from_w32_store ("ROOT"); + load_certs_from_w32_store ("CA"); + + return 0; + +#else /*!HAVE_W32_SYSTEM*/ + + /* A list of certificate bundles to try. */ + static struct { + const char *name; + } table[] = { +#ifdef DEFAULT_TRUST_STORE_FILE + { DEFAULT_TRUST_STORE_FILE } +#else + { "/etc/ssl/ca-bundle.pem" }, + { "/etc/ssl/certs/ca-certificates.crt" }, + { "/etc/pki/tls/cert.pem" }, + { "/usr/local/share/certs/ca-root-nss.crt" }, + { "/etc/ssl/cert.pem" } +#endif /*!DEFAULT_TRUST_STORE_FILE*/ + }; + int idx; + gpg_error_t err = 0; + + for (idx=0; idx < DIM (table); idx++) + if (!access (table[idx].name, F_OK)) + { + /* Take the first available bundle. */ + err = load_certs_from_file (table[idx].name, CERTTRUST_CLASS_SYSTEM, 0); + break; + } + + return err; +#endif /*!HAVE_W32_SYSTEM*/ +} + + /* Initialize the certificate cache if not yet done. */ void -cert_cache_init (void) +cert_cache_init (strlist_t hkp_cacerts) { - char *dname; + char *fname; + strlist_t sl; if (initialization_done) return; init_cache_lock (); acquire_cache_write_lock (); - dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL); - load_certs_from_dir (dname, 1); - xfree (dname); + load_certs_from_system (); + + fname = make_filename_try (gnupg_sysconfdir (), "trusted-certs", NULL); + if (fname) + load_certs_from_dir (fname, CERTTRUST_CLASS_CONFIG); + xfree (fname); + + fname = make_filename_try (gnupg_sysconfdir (), "extra-certs", NULL); + if (fname) + load_certs_from_dir (fname, 0); + xfree (fname); + + fname = make_filename_try (gnupg_datadir (), + "sks-keyservers.netCA.pem", NULL); + if (fname) + load_certs_from_file (fname, CERTTRUST_CLASS_HKPSPOOL, 1); + xfree (fname); - dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL); - load_certs_from_dir (dname, 0); - xfree (dname); + for (sl = hkp_cacerts; sl; sl = sl->next) + load_certs_from_file (sl->d, CERTTRUST_CLASS_HKP, 0); initialization_done = 1; release_cache_lock (); @@ -476,8 +754,7 @@ cert_cache_deinit (int full) } } - total_loaded_certificates = 0; - total_extra_certificates = 0; + total_nonperm_certificates = 0; initialization_done = 0; release_cache_lock (); } @@ -486,10 +763,51 @@ cert_cache_deinit (int full) void cert_cache_print_stats (void) { + cert_item_t ci; + int idx; + unsigned int n_nonperm = 0; + unsigned int n_permanent = 0; + unsigned int n_trusted = 0; + unsigned int n_trustclass_system = 0; + unsigned int n_trustclass_config = 0; + unsigned int n_trustclass_hkp = 0; + unsigned int n_trustclass_hkpspool = 0; + + acquire_cache_read_lock (); + for (idx = 0; idx < 256; idx++) + for (ci=cert_cache[idx]; ci; ci = ci->next) + if (ci->cert) + { + if (ci->permanent) + n_permanent++; + else + n_nonperm++; + if (ci->trustclasses) + { + n_trusted++; + if ((ci->trustclasses & CERTTRUST_CLASS_SYSTEM)) + n_trustclass_system++; + if ((ci->trustclasses & CERTTRUST_CLASS_CONFIG)) + n_trustclass_config++; + if ((ci->trustclasses & CERTTRUST_CLASS_HKP)) + n_trustclass_hkp++; + if ((ci->trustclasses & CERTTRUST_CLASS_HKPSPOOL)) + n_trustclass_hkpspool++; + } + } + + release_cache_lock (); + log_info (_("permanently loaded certificates: %u\n"), - total_loaded_certificates); + n_permanent); log_info (_(" runtime cached certificates: %u\n"), - total_extra_certificates); + n_nonperm); + log_info (_(" trusted certificates: %u (%u,%u,%u,%u)\n"), + n_trusted, + n_trustclass_system, + n_trustclass_config, + n_trustclass_hkp, + n_trustclass_hkpspool); } @@ -684,7 +1002,7 @@ get_cert_bysubject (const char *subject_dn, unsigned int seq) -/* Return a value describing the the class of PATTERN. The offset of +/* Return a value describing the class of PATTERN. The offset of the actual string to be used for the comparison is stored at R_OFFSET. The offset of the serialnumer is stored at R_SN_OFFSET. */ static enum pattern_class @@ -981,7 +1299,7 @@ get_certs_bypattern (const char *pattern, /* Return the certificate matching ISSUER_DN and SERIALNO; if it is - not already in the cache, try to find it from other resources. */ + * not already in the cache, try to find it from other resources. */ ksba_cert_t find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno) { @@ -996,23 +1314,23 @@ find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno) return cert; /* Ask back to the service requester to return the certificate. - This is because we can assume that he already used the - certificate while checking for the CRL. */ + * This is because we can assume that he already used the + * certificate while checking for the CRL. */ hexsn = serial_hex (serialno); if (!hexsn) { log_error ("serial_hex() failed\n"); return NULL; } - buf = xtrymalloc (1 + strlen (hexsn) + 1 + strlen (issuer_dn) + 1); + buf = strconcat ("#", hexsn, "/", issuer_dn, NULL); if (!buf) { log_error ("can't allocate enough memory: %s\n", strerror (errno)); xfree (hexsn); return NULL; } - strcpy (stpcpy (stpcpy (stpcpy (buf, "#"), hexsn),"/"), issuer_dn); xfree (hexsn); + cert = get_cert_local (ctrl, buf); xfree (buf); if (cert) @@ -1093,10 +1411,10 @@ find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno) /* Return the certificate matching SUBJECT_DN and (if not NULL) - KEYID. If it is not already in the cache, try to find it from other - resources. Note, that the external search does not work for user - certificates because the LDAP lookup is on the caCertificate - attribute. For our purposes this is just fine. */ + * KEYID. If it is not already in the cache, try to find it from other + * resources. Note, that the external search does not work for user + * certificates because the LDAP lookup is on the caCertificate + * attribute. For our purposes this is just fine. */ ksba_cert_t find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) { @@ -1107,11 +1425,11 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) ksba_sexp_t subj; /* If we have certificates from an OCSP request we first try to use - them. This is because these certificates will really be the - required ones and thus even in the case that they can't be - uniquely located by the following code we can use them. This is - for example required by Telesec certificates where a keyId is - used but the issuer certificate comes without a subject keyId! */ + * them. This is because these certificates will really be the + * required ones and thus even in the case that they can't be + * uniquely located by the following code we can use them. This is + * for example required by Telesec certificates where a keyId is + * used but the issuer certificate comes without a subject keyId! */ if (ctrl->ocsp_certs && subject_dn) { cert_item_t ci; @@ -1136,8 +1454,7 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n"); } - - /* First we check whether the certificate is cached. */ + /* No check whether the certificate is cached. */ for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++) { if (!keyid) @@ -1158,24 +1475,23 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) log_debug ("find_cert_bysubject: certificate not in cache\n"); /* Ask back to the service requester to return the certificate. - This is because we can assume that he already used the - certificate while checking for the CRL. */ + * This is because we can assume that he already used the + * certificate while checking for the CRL. */ if (keyid) cert = get_cert_local_ski (ctrl, subject_dn, keyid); else { /* In contrast to get_cert_local_ski, get_cert_local uses any - passed pattern, so we need to make sure that an exact subject - search is done. */ + * passed pattern, so we need to make sure that an exact subject + * search is done. */ char *buf; - buf = xtrymalloc (1 + strlen (subject_dn) + 1); + buf = strconcat ("/", subject_dn, NULL); if (!buf) { log_error ("can't allocate enough memory: %s\n", strerror (errno)); return NULL; } - strcpy (stpcpy (buf, "/"), subject_dn); cert = get_cert_local (ctrl, buf); xfree (buf); } @@ -1264,12 +1580,12 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) } - /* Return 0 if the certificate is a trusted certificate. Returns - GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in - case of systems errors. */ + * GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in + * case of systems errors. TRUSTCLASSES are the bitwise ORed + * CERTTRUST_CLASS values to use for the check. */ gpg_error_t -is_trusted_cert (ksba_cert_t cert) +is_trusted_cert (ksba_cert_t cert, unsigned int trustclasses) { unsigned char fpr[20]; cert_item_t ci; @@ -1280,8 +1596,10 @@ is_trusted_cert (ksba_cert_t cert) for (ci=cert_cache[*fpr]; ci; ci = ci->next) if (ci->cert && !memcmp (ci->fpr, fpr, 20)) { - if (ci->flags.trusted) + if ((ci->trustclasses & trustclasses)) { + /* The certificate is trusted in one of the given + * TRUSTCLASSES. */ release_cache_lock (); return 0; /* Yes, it is trusted. */ } @@ -1295,8 +1613,8 @@ is_trusted_cert (ksba_cert_t cert) /* Given the certificate CERT locate the issuer for this certificate - and return it at R_CERT. Returns 0 on success or - GPG_ERR_NOT_FOUND. */ + * and return it at R_CERT. Returns 0 on success or + * GPG_ERR_NOT_FOUND. */ gpg_error_t find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert) { @@ -1332,16 +1650,18 @@ find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert) { issuer_cert = find_cert_bysn (ctrl, s, authidno); } + if (!issuer_cert && keyid) { /* Not found by issuer+s/n. Now that we have an AKI - keyIdentifier look for a certificate with a matching - SKI. */ + * keyIdentifier look for a certificate with a matching + * SKI. */ issuer_cert = find_cert_bysubject (ctrl, issuer_dn, keyid); } + /* Print a note so that the user does not feel too helpless when - an issuer certificate was found and gpgsm prints BAD - signature because it is not the correct one. */ + * an issuer certificate was found and gpgsm prints BAD + * signature because it is not the correct one. */ if (!issuer_cert) { log_info ("issuer certificate "); @@ -1367,8 +1687,8 @@ find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert) } /* If this did not work, try just with the issuer's name and assume - that there is only one such certificate. We only look into our - cache then. */ + * that there is only one such certificate. We only look into our + * cache then. */ if (err || !issuer_cert) { issuer_cert = get_cert_bysubject (issuer_dn, 0); @@ -1389,3 +1709,92 @@ find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert) return err; } + + + +/* Read a list of certificates in PEM format from stream FP and store + * them on success at R_CERTLIST. On error NULL is stored at R_CERT + * list and an error code returned. Note that even on success an + * empty list of certificates can be returned (i.e. NULL stored at + * R_CERTLIST) iff the input stream has no certificates. */ +gpg_error_t +read_certlist_from_stream (certlist_t *r_certlist, estream_t fp) +{ + gpg_error_t err; + gnupg_ksba_io_t ioctx = NULL; + ksba_reader_t reader; + ksba_cert_t cert = NULL; + certlist_t certlist = NULL; + certlist_t cl, *cltail; + + *r_certlist = NULL; + + err = gnupg_ksba_create_reader (&ioctx, + (GNUPG_KSBA_IO_PEM | GNUPG_KSBA_IO_MULTIPEM), + fp, &reader); + if (err) + goto leave; + + /* Loop to read all certificates from the stream. */ + cltail = &certlist; + do + { + ksba_cert_release (cert); + cert = NULL; + err = ksba_cert_new (&cert); + if (!err) + err = ksba_cert_read_der (cert, reader); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + goto leave; + } + + /* Append the certificate to the list. We also store the + * fingerprint and check whether we have a cached certificate; + * in that case the cached certificate is put into the list to + * take advantage of a validation result which might be stored + * in the cached certificate. */ + cl = xtrycalloc (1, sizeof *cl); + if (!cl) + { + err = gpg_error_from_syserror (); + goto leave; + } + cert_compute_fpr (cert, cl->fpr); + cl->cert = get_cert_byfpr (cl->fpr); + if (!cl->cert) + { + cl->cert = cert; + cert = NULL; + } + *cltail = cl; + cltail = &cl->next; + ksba_reader_clear (reader, NULL, NULL); + } + while (!gnupg_ksba_reader_eof_seen (ioctx)); + + leave: + ksba_cert_release (cert); + gnupg_ksba_destroy_reader (ioctx); + if (err) + release_certlist (certlist); + else + *r_certlist = certlist; + + return err; +} + + +/* Release the certificate list CL. */ +void +release_certlist (certlist_t cl) +{ + while (cl) + { + certlist_t next = cl->next; + ksba_cert_release (cl->cert); + cl = next; + } +} diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h index 9986f15..92529bf 100644 --- a/dirmngr/certcache.h +++ b/dirmngr/certcache.h @@ -21,8 +21,17 @@ #ifndef CERTCACHE_H #define CERTCACHE_H +/* The origin of the trusted root certificates. */ +enum { + CERTTRUST_CLASS_SYSTEM = 1, /* From the system's list of trusted certs. */ + CERTTRUST_CLASS_CONFIG = 2, /* From dirmngr's config files. */ + CERTTRUST_CLASS_HKP = 4, /* From --hkp-cacert */ + CERTTRUST_CLASS_HKPSPOOL= 8, /* The one and only from sks-keyservers */ +}; + + /* First time initialization of the certificate cache. */ -void cert_cache_init (void); +void cert_cache_init (strlist_t hkp_cacerts); /* Deinitialize the certificate cache. */ void cert_cache_deinit (int full); @@ -41,10 +50,10 @@ gpg_error_t cache_cert (ksba_cert_t cert); gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer); /* Return 0 if the certificate is a trusted certificate. Returns - GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in - case of systems errors. */ -gpg_error_t is_trusted_cert (ksba_cert_t cert); - + * GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in + * case of systems errors. TRUSTCLASSES are the bitwise ORed + * CERTTRUST_CLASS values to use for the check. */ +gpg_error_t is_trusted_cert (ksba_cert_t cert, unsigned trustclasses); /* Return a certificate object for the given fingerprint. FPR is expected to be a 20 byte binary SHA-1 fingerprint. If no matching @@ -99,5 +108,18 @@ gpg_error_t find_issuing_cert (ctrl_t ctrl, +/* A simple list of certificates. */ +struct certlist_s +{ + struct certlist_s *next; + ksba_cert_t cert; + unsigned char fpr[20]; /* of the certificate. */ +}; +typedef struct certlist_s *certlist_t; + +gpg_error_t read_certlist_from_stream (certlist_t *r_certlist, estream_t fp); +void release_certlist (certlist_t cl); + + #endif /*CERTCACHE_H*/ diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 2e471cb..248ad9a 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -44,7 +44,7 @@ Field 1: Constant "v" Field 2: Version number of this file. Must be 1. - This record must be the first non-comment record record and + This record must be the first non-comment record and there shall only exist one record of this type. 1.3. CRL cache record @@ -1851,7 +1851,9 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, md = NULL; err = validate_cert_chain (ctrl, crlissuer_cert, NULL, - VALIDATE_MODE_CRL_RECURSIVE, + (VALIDATE_FLAG_TRUST_CONFIG + | VALIDATE_FLAG_CRL + | VALIDATE_FLAG_RECURSIVE), r_trust_anchor); if (err) { diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c index 8fe6e0b..f7a23ff 100644 --- a/dirmngr/crlfetch.c +++ b/dirmngr/crlfetch.c @@ -167,10 +167,11 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) http_release_parsed_uri (uri); if (err && !strncmp (url, "https:", 6)) { - /* Our HTTP code does not support TLS, thus we can't use this - scheme and it is frankly not useful for CRL retrieval anyway. - We resort to using http, assuming that the server also - provides plain http access. */ + /* FIXME: We now support https. + * Our HTTP code does not support TLS, thus we can't use this + * scheme and it is frankly not useful for CRL retrieval anyway. + * We resort to using http, assuming that the server also + * provides plain http access. */ free_this = xtrymalloc (strlen (url) + 1); if (free_this) { @@ -198,7 +199,9 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) err = http_open_document (&hd, url, NULL, ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) |(DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0) - |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + |(dirmngr_use_tor()? HTTP_FLAG_FORCE_TOR:0) + |(opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4:0) + ), ctrl->http_proxy, NULL, NULL, NULL); switch ( err? 99999 : http_get_status_code (hd) ) @@ -290,7 +293,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) "LDAP"); err = gpg_error (GPG_ERR_NOT_SUPPORTED); } - else if (opt.use_tor) + else if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("CRL access not possible due to Tor mode\n")); @@ -316,7 +319,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) gpg_error_t crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader) { - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("CRL access not possible due to Tor mode\n")); @@ -341,14 +344,14 @@ crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader) } -/* Fetch a CA certificate for DN using the default server. This - function only initiates the fetch; fetch_next_cert must be used to - actually read the certificate; end_cert_fetch to end the - operation. */ +/* Fetch a CA certificate for DN using the default server. This + * function only initiates the fetch; fetch_next_cert must be used to + * actually read the certificate; end_cert_fetch to end the + * operation. */ gpg_error_t ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn) { - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("CRL access not possible due to Tor mode\n")); @@ -375,7 +378,7 @@ gpg_error_t start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, strlist_t patterns, const ldap_server_t server) { - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("CRL access not possible due to Tor mode\n")); @@ -415,7 +418,7 @@ fetch_next_cert (cert_fetch_context_t context, /* Fetch the next data from CONTEXT, assuming it is a certificate and return - it as a cert object in R_CERT. */ + * it as a cert object in R_CERT. */ gpg_error_t fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert) { diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 061cfc3..c877a9b 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -111,6 +111,7 @@ enum cmd_and_opt_values { oBatch, oDisableHTTP, oDisableLDAP, + oDisableIPv4, oIgnoreLDAPDP, oIgnoreHTTPDP, oIgnoreOCSPSvcUrl, @@ -137,6 +138,7 @@ enum cmd_and_opt_values { oHTTPWrapperProgram, oIgnoreCertExtension, oUseTor, + oNoUseTor, oKeyServer, oNameServer, oDisableCheckOwnSocket, @@ -223,6 +225,9 @@ static ARGPARSE_OPTS opts[] = { N_("|FILE|use the CA certificates in FILE for HKP over TLS")), ARGPARSE_s_n (oUseTor, "use-tor", N_("route all network traffic via Tor")), + ARGPARSE_s_n (oNoUseTor, "no-use-tor", "@"), + + ARGPARSE_s_n (oDisableIPv4, "disable-ipv4", "@"), ARGPARSE_s_s (oSocketName, "socket-name", "@"), /* Only for debugging. */ @@ -262,6 +267,7 @@ static struct debug_flags_s debug_flags [] = { DBG_DNS_VALUE , "dns" }, { DBG_NETWORK_VALUE, "network" }, { DBG_LOOKUP_VALUE , "lookup" }, + { DBG_EXTPROG_VALUE, "extprog" }, { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; @@ -297,6 +303,16 @@ static volatile int shutdown_pending; /* Flags to indicate that we shall not watch our own socket. */ static int disable_check_own_socket; +/* Flag to control the Tor mode. */ +static enum + { TOR_MODE_AUTO = 0, /* Switch to NO or YES */ + TOR_MODE_NEVER, /* Never use Tor. */ + TOR_MODE_NO, /* Do not use Tor */ + TOR_MODE_YES, /* Use Tor */ + TOR_MODE_FORCE /* Force using Tor */ + } tor_mode; + + /* Counter for the active connections. */ static int active_connections; @@ -304,6 +320,10 @@ static int active_connections; * thread to run background network tasks. */ static int network_activity_seen; +/* A list of filenames registred with --hkp-cacert. */ +static strlist_t hkp_cacert_filenames; + + /* The timer tick used for housekeeping stuff. */ #define TIMERTICK_INTERVAL (60) @@ -479,7 +499,7 @@ set_debug (void) static void set_tor_mode (void) { - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* Enable Tor mode and when called again force a new curcuit * (e.g. on SIGHUP). */ @@ -490,6 +510,26 @@ set_tor_mode (void) log_info ("(is your Libassuan recent enough?)\n"); } } + else + disable_dns_tormode (); +} + + +/* Return true if Tor shall be used. */ +int +dirmngr_use_tor (void) +{ + if (tor_mode == TOR_MODE_AUTO) + { + /* FIXME: Figure out whether Tor is running. */ + } + + if (tor_mode == TOR_MODE_FORCE) + return 2; /* Use Tor (using 2 to indicate force mode) */ + else if (tor_mode == TOR_MODE_YES) + return 1; /* Use Tor */ + else + return 0; /* Do not use Tor. */ } @@ -551,8 +591,11 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) } FREE_STRLIST (opt.ignored_cert_extensions); http_register_tls_ca (NULL); + FREE_STRLIST (hkp_cacert_filenames); FREE_STRLIST (opt.keyserver); - /* Note: We do not allow resetting of opt.use_tor at runtime. */ + /* Note: We do not allow resetting of TOR_MODE_FORCE at runtime. */ + if (tor_mode != TOR_MODE_FORCE) + tor_mode = TOR_MODE_AUTO; disable_check_own_socket = 0; enable_standard_resolver (0); set_dns_timeout (0); @@ -593,6 +636,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oDisableHTTP: opt.disable_http = 1; break; case oDisableLDAP: opt.disable_ldap = 1; break; + case oDisableIPv4: opt.disable_ipv4 = 1; break; case oHonorHTTPProxy: opt.honor_http_proxy = 1; break; case oHTTPProxy: opt.http_proxy = pargs->r.ret_str; break; case oLDAPProxy: opt.ldap_proxy = pargs->r.ret_str; break; @@ -615,11 +659,14 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) case oHkpCaCert: { + /* We need to register the filenames with gnutls (http.c) and + * also for our own cert cache. */ char *tmpname; /* Do tilde expansion and make path absolute. */ tmpname = make_absfilename (pargs->r.ret_str, NULL); http_register_tls_ca (tmpname); + add_to_strlist (&hkp_cacert_filenames, pargs->r.ret_str); xfree (tmpname); } break; @@ -628,7 +675,13 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) add_to_strlist (&opt.ignored_cert_extensions, pargs->r.ret_str); break; - case oUseTor: opt.use_tor = 1; break; + case oUseTor: + tor_mode = TOR_MODE_FORCE; + break; + case oNoUseTor: + if (tor_mode != TOR_MODE_FORCE) + tor_mode = TOR_MODE_NEVER; + break; case oStandardResolver: enable_standard_resolver (1); break; case oRecursiveResolver: enable_recursive_resolver (1); break; @@ -652,6 +705,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) set_dns_verbose (opt.verbose, !!DBG_DNS); http_set_verbose (opt.verbose, !!DBG_NETWORK); + set_dns_disable_ipv4 (opt.disable_ipv4); return 1; /* Handled. */ } @@ -670,6 +724,23 @@ pid_suffix_callback (unsigned long *r_suffix) } #endif /*!HAVE_W32_SYSTEM*/ +#if HTTP_USE_NTBTLS +static void +my_ntbtls_log_handler (void *opaque, int level, const char *fmt, va_list argv) +{ + (void)opaque; + + if (level == -1) + log_logv_with_prefix (GPGRT_LOG_INFO, "ntbtls: ", fmt, argv); + else + { + char prefix[10+20]; + snprintf (prefix, sizeof prefix, "ntbtls(%d): ", level); + log_logv_with_prefix (GPGRT_LOG_DEBUG, prefix, fmt, argv); + } +} +#endif + static void thread_init (void) @@ -756,6 +827,10 @@ main (int argc, char **argv) setup_libgcrypt_logging (); +#if HTTP_USE_NTBTLS + ntbtls_set_log_handler (my_ntbtls_log_handler, NULL); +#endif + /* Setup defaults. */ shell = getenv ("SHELL"); if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") ) @@ -1003,7 +1078,7 @@ main (int argc, char **argv) thread_init (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); start_command_handler (ASSUAN_INVALID_FD); @@ -1038,7 +1113,7 @@ main (int argc, char **argv) log_set_prefix (NULL, 0); thread_init (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); handle_connections (3); @@ -1245,7 +1320,7 @@ main (int argc, char **argv) #endif thread_init (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); http_register_netactivity_cb (netactivity_action); handle_connections (fd); @@ -1267,7 +1342,7 @@ main (int argc, char **argv) dirmngr_init_default_ctrl (&ctrlbuf); thread_init (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); if (!argc) rc = crl_cache_load (&ctrlbuf, NULL); @@ -1290,7 +1365,7 @@ main (int argc, char **argv) dirmngr_init_default_ctrl (&ctrlbuf); thread_init (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); rc = crl_fetch (&ctrlbuf, argv[0], &reader); if (rc) @@ -1423,8 +1498,10 @@ 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); + ctrl->http_no_crl = 1; } @@ -1433,6 +1510,8 @@ dirmngr_deinit_default_ctrl (ctrl_t ctrl) { if (!ctrl) return; + ctrl->magic = 0xdeadbeef; + xfree (ctrl->http_proxy); ctrl->http_proxy = NULL; } @@ -1699,7 +1778,7 @@ dirmngr_sighup_action (void) reread_configuration (); cert_cache_deinit (0); crl_cache_deinit (); - cert_cache_init (); + cert_cache_init (hkp_cacert_filenames); crl_cache_init (); reload_dns_stuff (0); ks_hkp_reload (); @@ -1793,7 +1872,7 @@ housekeeping_thread (void *arg) if (network_activity_seen) { network_activity_seen = 0; - if (opt.use_tor || opt.allow_version_check) + if (opt.allow_version_check) dirmngr_load_swdb (&ctrlbuf, 0); } @@ -2137,7 +2216,7 @@ handle_connections (assuan_fd_t listen_fd) close (my_inotify_fd); #endif /*HAVE_INOTIFY_INIT*/ npth_attr_destroy (&tattr); - if (listen_fd != -1) + if (listen_fd != GNUPG_INVALID_FD) assuan_sock_close (fd); cleanup (); log_info ("%s %s stopped\n", strusage(11), strusage(13)); diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 35bc000..b269865 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -91,13 +91,13 @@ struct program. */ int running_detached; /* We are running in detached mode. */ - int use_tor; /* Tor mode has been enabled. */ int allow_version_check; /* --allow-version-check is active. */ int force; /* Force loading outdated CRLs. */ int disable_http; /* Do not use HTTP at all. */ int disable_ldap; /* Do not use LDAP at all. */ + int disable_ipv4; /* Do not use leagacy IP addresses. */ int honor_http_proxy; /* Honor the http_proxy env variable. */ const char *http_proxy; /* The default HTTP proxy. */ const char *ldap_proxy; /* Use given LDAP proxy. */ @@ -144,6 +144,7 @@ struct #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_NETWORK_VALUE 2048 /* debug network I/O. */ #define DBG_LOOKUP_VALUE 8192 /* debug lookup details */ +#define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) @@ -154,8 +155,10 @@ struct #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) +#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) -/* A simple list of certificate references. */ +/* A simple list of certificate references. FIXME: Better use + certlist_t also for references (Store NULL at .cert) */ struct cert_ref_s { struct cert_ref_s *next; @@ -163,15 +166,23 @@ struct cert_ref_s }; 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. */ @@ -181,6 +192,8 @@ struct server_control_s int audit_events; /* Send audit events to client. */ char *http_proxy; /* The used http_proxy or NULL. */ + + unsigned int http_no_crl:1; /* Do not check CRLs for https. */ }; @@ -190,7 +203,7 @@ void dirmngr_init_default_ctrl (ctrl_t ctrl); void dirmngr_deinit_default_ctrl (ctrl_t ctrl); void dirmngr_sighup_action (void); const char* dirmngr_get_current_socket_name (void); - +int dirmngr_use_tor (void); /*-- Various housekeeping functions. --*/ void ks_hkp_housekeeping (time_t curtime); @@ -211,6 +224,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/dns-stuff.c b/dirmngr/dns-stuff.c index 9347196..d72d1c7 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -119,6 +119,10 @@ static int opt_debug; /* The timeout in seconds for libdns requests. */ static int opt_timeout; +/* The flag to disable IPv4 access - right now this only skips + * returned A records. */ +static int opt_disable_ipv4; + /* If set force the use of the standard resolver. */ static int standard_resolver; @@ -218,6 +222,14 @@ enable_dns_tormode (int new_circuit) } +/* Disable tor mode. */ +void +disable_dns_tormode (void) +{ + tor_mode = 0; +} + + /* Set verbosity and debug mode for this module. */ void set_dns_verbose (int verbose, int debug) @@ -227,6 +239,15 @@ set_dns_verbose (int verbose, int debug) } +/* Set the Disable-IPv4 flag so that the name resolver does not return + * A addresses. */ +void +set_dns_disable_ipv4 (int yes) +{ + opt_disable_ipv4 = !!yes; +} + + /* Set the timeout for libdns requests to SECONDS. A value of 0 sets * the default timeout and values are capped at 10 minutes. */ void @@ -477,12 +498,10 @@ libdns_init (void) (dns_nssconf_loadpath (ld.resolv_conf, fname)); if (err) { - log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err)); - /* not fatal, nsswitch.conf is not used on all systems; assume - * classic behavior instead. Our dns library states "bf" which tries - * DNS then Files, which is not classic; FreeBSD - * /usr/src/lib/libc/net/gethostnamadr.c defines default_src[] which - * is Files then DNS, which is. */ + /* This is not a fatal error: nsswitch.conf is not used on + * all systems; assume classic behavior instead. */ + if (gpg_err_code (err) != GPG_ERR_ENOENT) + log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err)); if (opt_debug) log_debug ("dns: fallback resolution order, files then DNS\n"); ld.resolv_conf->lookup[0] = 'f'; @@ -490,6 +509,23 @@ libdns_init (void) ld.resolv_conf->lookup[2] = '\0'; err = GPG_ERR_NO_ERROR; } + else if (!strchr (ld.resolv_conf->lookup, 'b')) + { + /* No DNS resulution type found in the list. This might be + * due to systemd based systems which allow for custom + * keywords which are not known to us and thus we do not + * know whether DNS is wanted or not. Becuase DNS is + * important for our infrastructure, we forcefully append + * DNS to the end of the list. */ + if (strlen (ld.resolv_conf->lookup)+2 < sizeof ld.resolv_conf->lookup) + { + if (opt_debug) + log_debug ("dns: appending DNS to resolution order\n"); + strcat (ld.resolv_conf->lookup, "b"); + } + else + log_error ("failed to append DNS to resolution order\n"); + } #endif /* Unix */ } @@ -683,6 +719,7 @@ resolve_name_libdns (const char *name, unsigned short port, struct addrinfo *ent; char portstr_[21]; char *portstr = NULL; + char *namebuf = NULL; int derr; *r_dai = NULL; @@ -695,8 +732,6 @@ resolve_name_libdns (const char *name, unsigned short port, hints.ai_flags = AI_ADDRCONFIG; if (r_canonname) hints.ai_flags |= AI_CANONNAME; - if (is_ip_address (name)) - hints.ai_flags |= AI_NUMERICHOST; if (port) { @@ -708,6 +743,25 @@ resolve_name_libdns (const char *name, unsigned short port, if (err) goto leave; + + if (is_ip_address (name)) + { + hints.ai_flags |= AI_NUMERICHOST; + /* libdns does not grok brackets - remove them. */ + if (*name == '[' && name[strlen(name)-1] == ']') + { + namebuf = xtrymalloc (strlen (name)); + if (!namebuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + strcpy (namebuf, name+1); + namebuf[strlen (namebuf)-1] = 0; + name = namebuf; + } + } + ai = dns_ai_open (name, portstr, 0, &hints, res, &derr); if (!ai) { @@ -789,6 +843,7 @@ resolve_name_libdns (const char *name, unsigned short port, else *r_dai = daihead; + xfree (namebuf); return err; } #endif /*USE_LIBDNS*/ @@ -826,7 +881,7 @@ resolve_name_standard (const char *name, unsigned short port, else *portstr = 0; - /* We can't use the the AI_IDN flag because that does the conversion + /* We can't use the AI_IDN flag because that does the conversion using the current locale. However, GnuPG always used UTF-8. To support IDN we would need to make use of the libidn API. */ ret = getaddrinfo (name, *portstr? portstr : NULL, &hints, &aibuf); @@ -873,6 +928,8 @@ resolve_name_standard (const char *name, unsigned short port, { if (ai->ai_family != AF_INET6 && ai->ai_family != AF_INET) continue; + if (opt_disable_ipv4 && ai->ai_family == AF_INET) + continue; dai = xtrymalloc (sizeof *dai + ai->ai_addrlen - 1); dai->family = ai->ai_family; @@ -1170,7 +1227,7 @@ is_ip_address (const char *name) if (*name == '[') return 6; /* yes: A legal DNS name may not contain this character; - this mut be bracketed v6 address. */ + this must be bracketed v6 address. */ if (*name == '.') return 0; /* No. A leading dot is not a valid IP address. */ @@ -1212,7 +1269,7 @@ is_ip_address (const char *name) if (*s == '.') { if (s[1] == '.') - return 0; /* No: Douple dot. */ + return 0; /* No: Double dot. */ if (atoi (s+1) > 255) return 0; /* No: Ipv4 byte value too large. */ ndots++; @@ -1623,7 +1680,7 @@ get_dns_cert_standard (const char *name, int want_certtype, found, the malloced data is returned at (R_KEY, R_KEYLEN) and the other return parameters are set to NULL/0. If an IPGP CERT record was found the fingerprint is stored as an allocated block at - R_FPR and its length at R_FPRLEN; an URL is is allocated as a + R_FPR and its length at R_FPRLEN; an URL is allocated as a string and returned at R_URL. If WANT_CERTTYPE is 0 this function returns the first CERT found with a supported type; it is expected that only one CERT record is used. If WANT_CERTTYPE is one of the diff --git a/dirmngr/dns-stuff.h b/dirmngr/dns-stuff.h index d68dd17..9b8303c 100644 --- a/dirmngr/dns-stuff.h +++ b/dirmngr/dns-stuff.h @@ -95,6 +95,10 @@ struct srventry /* Set verbosity and debug mode for this module. */ void set_dns_verbose (int verbose, int debug); +/* Set the Disable-IPv4 flag so that the name resolver does not return + * A addresses. */ +void set_dns_disable_ipv4 (int yes); + /* Set the timeout for libdns requests to SECONDS. */ void set_dns_timeout (int seconds); @@ -116,6 +120,7 @@ int recursive_resolver_p (void); /* Put this module eternally into Tor mode. When called agained with * NEW_CIRCUIT request a new TOR circuit for the next DNS query. */ void enable_dns_tormode (int new_circuit); +void disable_dns_tormode (void); /* Change the default IP address of the nameserver to IPADDR. The address needs to be a numerical IP address and will be used for the diff --git a/dirmngr/http-ntbtls.c b/dirmngr/http-ntbtls.c new file mode 100644 index 0000000..00d6a58 --- /dev/null +++ b/dirmngr/http-ntbtls.c @@ -0,0 +1,124 @@ +/* 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dirmngr.h" +#include "certcache.h" +#include "validate.h" + +#ifdef HTTP_USE_NTBTLS +# include <ntbtls.h> + + + +/* 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; + ntbtls_t tls = tls_context; + gpg_error_t err; + int idx; + ksba_cert_t cert; + ksba_cert_t hostcert = NULL; + unsigned int validate_flags; + const char *hostname; + + (void)http; + (void)session; + + log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC); + log_assert (!ntbtls_check_context (tls)); + + /* Get the peer's certs fron ntbtls. */ + for (idx = 0; + (cert = ntbtls_x509_get_peer_cert (tls, 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; + + /* Are we using the standard hkps:// pool use the dedicated + * root certificate. */ + hostname = ntbtls_get_hostname (tls); + if (hostname + && !ascii_strcasecmp (hostname, "hkps.pool.sks-keyservers.net")) + { + validate_flags |= VALIDATE_FLAG_TRUST_HKPSPOOL; + } + else /* Use the certificates as requested from the HTTP module. */ + { + if ((http_flags & HTTP_FLAG_TRUST_DEF)) + validate_flags |= VALIDATE_FLAG_TRUST_HKP; + if ((http_flags & HTTP_FLAG_TRUST_SYS)) + validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM; + } + + if ((http_flags & HTTP_FLAG_NO_CRL)) + 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 35877d2..890f5f6 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -155,16 +155,22 @@ static gpg_error_t send_request (http_t hd, const char *httphost, static char *build_rel_path (parsed_uri_t uri); static gpg_error_t parse_response (http_t hd); -static assuan_fd_t connect_server (const char *server, unsigned short port, +static gpg_error_t connect_server (const char *server, unsigned short port, unsigned int flags, const char *srvtag, - int *r_host_not_found); + assuan_fd_t *r_sock); +static gpgrt_ssize_t read_server (int sock, void *buffer, size_t size); static gpg_error_t write_server (int sock, const char *data, size_t length); static gpgrt_ssize_t cookie_read (void *cookie, void *buffer, size_t size); static gpgrt_ssize_t cookie_write (void *cookie, const void *buffer, size_t size); static int cookie_close (void *cookie); - +#ifdef HAVE_W32_SYSTEM +static gpgrt_ssize_t simple_cookie_read (void *cookie, + void *buffer, size_t size); +static gpgrt_ssize_t simple_cookie_write (void *cookie, + const void *buffer, size_t size); +#endif /* A socket object used to a allow ref counting of sockets. */ struct my_socket_s @@ -184,6 +190,7 @@ static es_cookie_io_functions_t cookie_functions = cookie_close }; + struct cookie_s { /* Socket object or NULL if already closed. */ @@ -202,9 +209,31 @@ struct cookie_s }; typedef struct cookie_s *cookie_t; + +/* Simple cookie functions. Here the cookie is an int with the + * socket. */ +#ifdef HAVE_W32_SYSTEM +static es_cookie_io_functions_t simple_cookie_functions = + { + simple_cookie_read, + simple_cookie_write, + NULL, + NULL + }; +#endif + + +#if SIZEOF_UNSIGNED_LONG == 8 +# define HTTP_SESSION_MAGIC 0x0068545470534553 /* "hTTpSES" */ +#else +# define HTTP_SESSION_MAGIC 0x68547365 /* "hTse" */ +#endif + /* The session object. */ struct http_session_s { + unsigned long magic; + int refcount; /* Number of references to this object. */ #ifdef HTTP_USE_GNUTLS gnutls_certificate_credentials_t certcred; @@ -221,6 +250,13 @@ struct http_session_s /* A callback function to log details of TLS certifciates. */ void (*cert_log_cb) (http_session_t, gpg_error_t, const char *, const void **, size_t *); + + /* The flags passed to the session object. */ + unsigned int flags; + + /* A per-session TLS verification callback. */ + http_verify_cb_t verify_cb; + void *verify_cb_value; }; @@ -234,9 +270,17 @@ struct header_s typedef struct header_s *header_t; +#if SIZEOF_UNSIGNED_LONG == 8 +# define HTTP_CONTEXT_MAGIC 0x0068545470435458 /* "hTTpCTX" */ +#else +# define HTTP_CONTEXT_MAGIC 0x68546378 /* "hTcx" */ +#endif + + /* Our handle context. */ struct http_context_s { + unsigned long magic; unsigned int status_code; my_socket_t sock; unsigned int in_data:1; @@ -406,6 +450,27 @@ 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; + + (void)verify_flags; + + log_assert (hd && hd->session && hd->session->verify_cb); + log_assert (hd->magic == HTTP_CONTEXT_MAGIC); + log_assert (hd->session->magic == HTTP_SESSION_MAGIC); + + 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 @@ -418,6 +483,7 @@ fp_onclose_notification (estream_t stream, void *opaque) { http_t hd = opaque; + log_assert (hd->magic == HTTP_CONTEXT_MAGIC); if (hd->fp_read && hd->fp_read == stream) hd->fp_read = NULL; else if (hd->fp_write && hd->fp_write == stream) @@ -577,6 +643,8 @@ session_unref (int lnr, http_session_t sess) if (!sess) return; + log_assert (sess->magic == HTTP_SESSION_MAGIC); + sess->refcount--; if (opt_debug > 1) log_debug ("http.c:%d:session_unref: sess %p ref now %d\n", @@ -588,6 +656,7 @@ session_unref (int lnr, http_session_t sess) close_tls_session (sess); #endif /*USE_TLS*/ + sess->magic = 0xdeadbeef; xfree (sess); } #define http_session_unref(a) session_unref (__LINE__, (a)) @@ -604,10 +673,12 @@ http_session_release (http_session_t sess) * Valid values for FLAGS are: * HTTP_FLAG_TRUST_DEF - Use the CAs set with http_register_tls_ca * HTTP_FLAG_TRUST_SYS - Also use the CAs defined by the system + * HTTP_FLAG_NO_CRL - Do not consult CRLs for https. */ gpg_error_t -http_session_new (http_session_t *r_session, const char *tls_priority, - const char *intended_hostname, unsigned int flags) +http_session_new (http_session_t *r_session, + const char *intended_hostname, unsigned int flags, + http_verify_cb_t verify_cb, void *verify_cb_value) { gpg_error_t err; http_session_t sess; @@ -617,97 +688,24 @@ http_session_new (http_session_t *r_session, const char *tls_priority, sess = xtrycalloc (1, sizeof *sess); if (!sess) return gpg_error_from_syserror (); + sess->magic = HTTP_SESSION_MAGIC; sess->refcount = 1; + sess->flags = flags; + sess->verify_cb = verify_cb; + sess->verify_cb_value = verify_cb_value; #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; - - (void)tls_priority; - - 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 { @@ -799,7 +797,7 @@ http_session_new (http_session_t *r_session, const char *tls_priority, gnutls_transport_set_ptr (sess->tls_session, NULL); rc = gnutls_priority_set_direct (sess->tls_session, - tls_priority? tls_priority : "NORMAL", + "NORMAL", &errpos); if (rc < 0) { @@ -818,11 +816,12 @@ http_session_new (http_session_t *r_session, const char *tls_priority, goto leave; } } -#else /*!HTTP_USE_GNUTLS*/ +#else /*!HTTP_USE_GNUTLS && !HTTP_USE_NTBTLS*/ { - (void)tls_priority; + (void)intended_hostname; + (void)flags; } -#endif /*!HTTP_USE_GNUTLS*/ +#endif /*!HTTP_USE_GNUTLS && !HTTP_USE_NTBTLS*/ if (opt_debug > 1) log_debug ("http.c:session_new: sess %p created\n", sess); @@ -890,6 +889,7 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url, hd = xtrycalloc (1, sizeof *hd); if (!hd) return gpg_error_from_syserror (); + hd->magic = HTTP_CONTEXT_MAGIC; hd->req_type = reqtype; hd->flags = flags; hd->session = http_session_ref (session); @@ -924,7 +924,6 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, gpg_error_t err = 0; http_t hd; cookie_t cookie; - int hnf; *r_hd = NULL; @@ -943,6 +942,7 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, hd = xtrycalloc (1, sizeof *hd); if (!hd) return gpg_error_from_syserror (); + hd->magic = HTTP_CONTEXT_MAGIC; hd->req_type = HTTP_REQ_OPAQUE; hd->flags = flags; @@ -950,12 +950,9 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port, { assuan_fd_t sock; - sock = connect_server (server, port, hd->flags, srvtag, &hnf); - if (sock == ASSUAN_INVALID_FD) + err = connect_server (server, port, hd->flags, srvtag, &sock); + if (err) { - err = gpg_err_make (default_errsource, - (hnf? GPG_ERR_UNKNOWN_HOST - : gpg_err_code_from_syserror ())); xfree (hd); return err; } @@ -1130,6 +1127,8 @@ http_close (http_t hd, int keep_read_stream) if (!hd) return; + log_assert (hd->magic == HTTP_CONTEXT_MAGIC); + /* First remove the close notifications for the streams. */ if (hd->fp_read) es_onclose (hd->fp_read, 0, fp_onclose_notification, hd); @@ -1143,6 +1142,7 @@ http_close (http_t hd, int keep_read_stream) if (hd->fp_write) es_fclose (hd->fp_write); http_session_unref (hd->session); + hd->magic = 0xdeadbeef; http_release_parsed_uri (hd->uri); while (hd->headers) { @@ -1177,7 +1177,7 @@ http_get_status_code (http_t hd) /* Return information pertaining to TLS. If TLS is not in use for HD, NULL is returned. WHAT is used ask for specific information: - (NULL) := Only check whether TLS is is use. Returns an + (NULL) := Only check whether TLS is in use. Returns an unspecified string if TLS is in use. That string may even be the empty string. */ @@ -1643,7 +1643,6 @@ send_request (http_t hd, const char *httphost, const char *auth, char *proxy_authstr = NULL; char *authstr = NULL; int sock; - int hnf; if (hd->uri->use_tls && !hd->session) { @@ -1713,7 +1712,6 @@ send_request (http_t hd, const char *httphost, const char *auth, && *http_proxy )) { parsed_uri_t uri; - int save_errno; if (proxy) http_proxy = proxy; @@ -1760,25 +1758,20 @@ send_request (http_t hd, const char *httphost, const char *auth, } } - sock = connect_server (*uri->host ? uri->host : "localhost", - uri->port ? uri->port : 80, - hd->flags, srvtag, &hnf); - save_errno = errno; + err = connect_server (*uri->host ? uri->host : "localhost", + uri->port ? uri->port : 80, + hd->flags, srvtag, &sock); http_release_parsed_uri (uri); - if (sock == ASSUAN_INVALID_FD) - gpg_err_set_errno (save_errno); } else { - sock = connect_server (server, port, hd->flags, srvtag, &hnf); + err = connect_server (server, port, hd->flags, srvtag, &sock); } - if (sock == ASSUAN_INVALID_FD) + if (err) { xfree (proxy_authstr); - return gpg_err_make (default_errsource, - (hnf? GPG_ERR_UNKNOWN_HOST - : gpg_err_code_from_syserror ())); + return err; } hd->sock = my_socket_new (sock); if (!hd->sock) @@ -1788,7 +1781,6 @@ send_request (http_t hd, const char *httphost, const char *auth, } - #if HTTP_USE_NTBTLS if (hd->uri->use_tls) { @@ -1796,7 +1788,14 @@ send_request (http_t hd, const char *httphost, const char *auth, my_socket_ref (hd->sock); + /* Until we support send/recv in estream under Windows we need + * to use es_fopencookie. */ +#ifdef HAVE_W32_SYSTEM + in = es_fopencookie ((void*)(unsigned int)hd->sock->fd, "rb", + simple_cookie_functions); +#else in = es_fdopen_nc (hd->sock->fd, "rb"); +#endif if (!in) { err = gpg_error_from_syserror (); @@ -1804,7 +1803,12 @@ send_request (http_t hd, const char *httphost, const char *auth, return err; } +#ifdef HAVE_W32_SYSTEM + out = es_fopencookie ((void*)(unsigned int)hd->sock->fd, "wb", + simple_cookie_functions); +#else out = es_fdopen_nc (hd->sock->fd, "wb"); +#endif if (!out) { err = gpg_error_from_syserror (); @@ -1822,6 +1826,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) @@ -1835,10 +1854,33 @@ send_request (http_t hd, const char *httphost, const char *auth, } hd->session->verify.done = 0; - if (tls_callback) + + /* Try the available verify callbacks until one returns success + * 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 + && 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), + hd->session->tls_session); + + if (tls_callback + && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR + && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) err = tls_callback (hd, hd->session, 0); - else + + if (gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR + && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) err = http_verify_server_credentials (hd->session); + if (err) { log_info ("TLS connection authentication failed: %s <%s>\n", @@ -1846,6 +1888,7 @@ send_request (http_t hd, const char *httphost, const char *auth, xfree (proxy_authstr); return err; } + } #elif HTTP_USE_GNUTLS if (hd->uri->use_tls) @@ -1954,7 +1997,7 @@ send_request (http_t hd, const char *httphost, const char *auth, { char portstr[35]; - if (port == 80) + if (port == (hd->uri->use_tls? 443 : 80)) *portstr = 0; else snprintf (portstr, sizeof portstr, ":%u", port); @@ -2162,11 +2205,10 @@ store_header (http_t hd, char *line) if (h) { /* We have already seen a line with that name. Thus we assume - it is a comma separated list and merge them. */ - p = xtrymalloc (strlen (h->value) + 1 + strlen (value)+ 1); + * it is a comma separated list and merge them. */ + p = strconcat (h->value, ",", value, NULL); if (!p) return gpg_err_code_from_syserror (); - strcpy (stpcpy (stpcpy (p, h->value), ","), value); xfree (h->value); h->value = p; return 0; @@ -2476,11 +2518,13 @@ my_sock_new_for_addr (struct sockaddr *addr, int type, int proto) } -/* Actually connect to a server. Returns the file descriptor or -1 on - error. ERRNO is set on error. */ -static assuan_fd_t +/* Actually connect to a server. On success 0 is returned and the + * file descriptor for the socket is stored at R_SOCK; on error an + * error code is returned and ASSUAN_INVALID_FD is stored at + * R_SOCK. */ +static gpg_error_t connect_server (const char *server, unsigned short port, - unsigned int flags, const char *srvtag, int *r_host_not_found) + unsigned int flags, const char *srvtag, assuan_fd_t *r_sock) { gpg_error_t err; assuan_fd_t sock = ASSUAN_INVALID_FD; @@ -2488,11 +2532,11 @@ connect_server (const char *server, unsigned short port, int hostfound = 0; int anyhostaddr = 0; int srv, connected; - int last_errno = 0; + gpg_error_t last_err = 0; struct srventry *serverlist = NULL; - int ret; - *r_host_not_found = 0; + *r_sock = ASSUAN_INVALID_FD; + #if defined(HAVE_W32_SYSTEM) && !defined(HTTP_NO_WSASTARTUP) init_sockets (); #endif /*Windows*/ @@ -2509,18 +2553,21 @@ connect_server (const char *server, unsigned short port, ASSUAN_SOCK_TOR); if (sock == ASSUAN_INVALID_FD) { - if (errno == EHOSTUNREACH) - *r_host_not_found = 1; - log_error ("can't connect to '%s': %s\n", server, strerror (errno)); + err = gpg_err_make (default_errsource, + (errno == EHOSTUNREACH)? GPG_ERR_UNKNOWN_HOST + : gpg_err_code_from_syserror ()); + log_error ("can't connect to '%s': %s\n", server, gpg_strerror (err)); + return err; } - else - notify_netactivity (); - return sock; + + notify_netactivity (); + *r_sock = sock; + return 0; #else /*!ASSUAN_SOCK_TOR*/ - gpg_err_set_errno (ENETUNREACH); - return -1; /* Out of core. */ + err = gpg_err_make (default_errsource, GPG_ERR_ENETUNREACH); + return ASSUAN_INVALID_FD; #endif /*!HASSUAN_SOCK_TOR*/ } @@ -2533,6 +2580,7 @@ connect_server (const char *server, unsigned short port, log_info ("getting '%s' SRV for '%s' failed: %s\n", srvtag, server, gpg_strerror (err)); /* Note that on error SRVCOUNT is zero. */ + err = 0; } if (!serverlist) @@ -2541,7 +2589,8 @@ connect_server (const char *server, unsigned short port, up a fake SRV record. */ serverlist = xtrycalloc (1, sizeof *serverlist); if (!serverlist) - return -1; /* Out of core. */ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + serverlist->port = port; strncpy (serverlist->target, server, DIMof (struct srventry, target)); serverlist->target[DIMof (struct srventry, target)-1] = '\0'; @@ -2562,6 +2611,7 @@ connect_server (const char *server, unsigned short port, { log_info ("resolving '%s' failed: %s\n", serverlist[srv].target, gpg_strerror (err)); + last_err = err; continue; /* Not found - try next one. */ } hostfound = 1; @@ -2578,18 +2628,20 @@ connect_server (const char *server, unsigned short port, sock = my_sock_new_for_addr (ai->addr, ai->socktype, ai->protocol); if (sock == ASSUAN_INVALID_FD) { - int save_errno = errno; - log_error ("error creating socket: %s\n", strerror (errno)); + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + log_error ("error creating socket: %s\n", gpg_strerror (err)); free_dns_addrinfo (aibuf); xfree (serverlist); - errno = save_errno; - return ASSUAN_INVALID_FD; + return err; } anyhostaddr = 1; - ret = assuan_sock_connect (sock, ai->addr, ai->addrlen); - if (ret) - last_errno = errno; + if (assuan_sock_connect (sock, ai->addr, ai->addrlen)) + { + last_err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + } else { connected = 1; @@ -2616,17 +2668,53 @@ connect_server (const char *server, unsigned short port, server, (int)WSAGetLastError()); #else log_error ("can't connect to '%s': %s\n", - server, strerror (last_errno)); + server, gpg_strerror (last_err)); #endif } - if (!hostfound || (hostfound && !anyhostaddr)) - *r_host_not_found = 1; + err = last_err? last_err : gpg_err_make (default_errsource, + GPG_ERR_UNKNOWN_HOST); if (sock != ASSUAN_INVALID_FD) assuan_sock_close (sock); - gpg_err_set_errno (last_errno); - return ASSUAN_INVALID_FD; + return err; } - return sock; + + *r_sock = sock; + return 0; +} + + +/* Helper to read from a socket. This handles npth things and + * EINTR. */ +static gpgrt_ssize_t +read_server (int sock, void *buffer, size_t size) +{ + int nread; + + do + { +#ifdef HAVE_W32_SYSTEM + /* Under Windows we need to use recv for a socket. */ +# if defined(USE_NPTH) + npth_unprotect (); +# endif + nread = recv (sock, buffer, size, 0); +# if defined(USE_NPTH) + npth_protect (); +# endif + +#else /*!HAVE_W32_SYSTEM*/ + +# ifdef USE_NPTH + nread = npth_read (sock, buffer, size); +# else + nread = read (sock, buffer, size); +# endif + +#endif /*!HAVE_W32_SYSTEM*/ + } + while (nread == -1 && errno == EINTR); + + return nread; } @@ -2745,29 +2833,7 @@ cookie_read (void *cookie, void *buffer, size_t size) else #endif /*HTTP_USE_GNUTLS*/ { - do - { -#ifdef HAVE_W32_SYSTEM - /* Under Windows we need to use recv for a socket. */ -# if defined(USE_NPTH) - npth_unprotect (); -# endif - nread = recv (c->sock->fd, buffer, size, 0); -# if defined(USE_NPTH) - npth_protect (); -# endif - -#else /*!HAVE_W32_SYSTEM*/ - -# ifdef USE_NPTH - nread = npth_read (c->sock->fd, buffer, size); -# else - nread = read (c->sock->fd, buffer, size); -# endif - -#endif /*!HAVE_W32_SYSTEM*/ - } - while (nread == -1 && errno == EINTR); + nread = read_server (c->sock->fd, buffer, size); } if (c->content_length_valid && nread > 0) @@ -2849,6 +2915,34 @@ cookie_write (void *cookie, const void *buffer_arg, size_t size) } +#ifdef HAVE_W32_SYSTEM +static gpgrt_ssize_t +simple_cookie_read (void *cookie, void *buffer, size_t size) +{ + int sock = (int)(uintptr_t)cookie; + return read_server (sock, buffer, size); +} + +static gpgrt_ssize_t +simple_cookie_write (void *cookie, const void *buffer_arg, size_t size) +{ + int sock = (int)(uintptr_t)cookie; + const char *buffer = buffer_arg; + int nwritten; + + if (write_server (sock, buffer, size)) + { + gpg_err_set_errno (EIO); + nwritten = -1; + } + else + nwritten = size; + + return (gpgrt_ssize_t)nwritten; +} +#endif /*HAVE_W32_SYSTEM*/ + + #ifdef HTTP_USE_GNUTLS /* Wrapper for gnutls_bye used by my_socket_unref. */ static void @@ -2912,10 +3006,7 @@ cookie_close (void *cookie) gpg_error_t http_verify_server_credentials (http_session_t sess) { -#if HTTP_USE_NTBTLS - (void)sess; - return 0; /* FIXME!! */ -#elif HTTP_USE_GNUTLS +#if HTTP_USE_GNUTLS static const char const errprefix[] = "TLS verification of peer failed"; int rc; unsigned int status; diff --git a/dirmngr/http.h b/dirmngr/http.h index 0b581fe..2609b9e 100644 --- a/dirmngr/http.h +++ b/dirmngr/http.h @@ -86,8 +86,9 @@ enum HTTP_FLAG_IGNORE_CL = 32, /* Ignore content-length. */ HTTP_FLAG_IGNORE_IPv4 = 64, /* Do not use IPv4. */ HTTP_FLAG_IGNORE_IPv6 = 128, /* Do not use IPv6. */ - HTTP_FLAG_TRUST_DEF = 256, /* Use the default CAs. */ - HTTP_FLAG_TRUST_SYS = 512 /* Also use the system defined CAs. */ + HTTP_FLAG_TRUST_DEF = 256, /* Use the CAs configured for HKP. */ + HTTP_FLAG_TRUST_SYS = 512, /* Also use the system defined CAs. */ + HTTP_FLAG_NO_CRL = 1024 /* Do not consult CRLs for https. */ }; @@ -97,6 +98,13 @@ typedef struct http_session_s *http_session_t; struct http_context_s; typedef struct http_context_s *http_t; +/* A TLS verify callback function. */ +typedef gpg_error_t (*http_verify_cb_t) (void *opaque, + http_t http, + http_session_t session, + unsigned int flags, + void *tls_context); + void http_set_verbose (int verbose, int debug); void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int)); @@ -105,9 +113,10 @@ void http_register_netactivity_cb (void (*cb)(void)); gpg_error_t http_session_new (http_session_t *r_session, - const char *tls_priority, const char *intended_hostname, - unsigned int flags); + unsigned int flags, + http_verify_cb_t cb, + void *cb_value); http_session_t http_session_ref (http_session_t sess); void http_session_release (http_session_t sess); diff --git a/dirmngr/ks-engine-finger.c b/dirmngr/ks-engine-finger.c index b1f02ad..811b72d 100644 --- a/dirmngr/ks-engine-finger.c +++ b/dirmngr/ks-engine-finger.c @@ -83,7 +83,9 @@ ks_finger_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp) *server++ = 0; err = http_raw_connect (&http, server, 79, - (opt.use_tor? HTTP_FLAG_FORCE_TOR : 0), NULL); + ((dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR : 0) + | (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)), + NULL); if (err) { xfree (name); diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 45965ce..b6a0675 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -82,16 +82,13 @@ struct hostinfo_s unsigned int v6:1; /* Host supports AF_INET6. */ unsigned int onion:1;/* NAME is an onion (Tor HS) address. */ unsigned int dead:1; /* Host is currently unresponsive. */ + unsigned int iporname_valid:1; /* The field IPORNAME below is valid */ + /* (but may be NULL) */ time_t died_at; /* The time the host was marked dead. If this is 0 the host has been manually marked dead. */ char *cname; /* Canonical name of the host. Only set if this is a pool or NAME has a numerical IP address. */ - char *v4addr; /* A string with the v4 IP address of the host. - NULL if NAME has a numeric IP address or no v4 - address is available. */ - char *v6addr; /* A string with the v6 IP address of the host. - NULL if NAME has a numeric IP address or no v6 - address is available. */ + char *iporname; /* Numeric IP address or name for printing. */ unsigned short port; /* The port used by the host, 0 if unknown. */ char name[1]; /* The hostname. */ }; @@ -128,10 +125,10 @@ create_new_hostinfo (const char *name) hi->v6 = 0; hi->onion = 0; hi->dead = 0; + hi->iporname_valid = 0; hi->died_at = 0; hi->cname = NULL; - hi->v4addr = NULL; - hi->v6addr = NULL; + hi->iporname = NULL; hi->port = 0; /* Add it to the hosttable. */ @@ -258,6 +255,31 @@ arecords_is_pool (dns_addrinfo_t aibuf) } +/* Print a warninng iff Tor is not running but Tor has been requested. + * Also return true if it is not running. */ +static int +tor_not_running_p (ctrl_t ctrl) +{ + assuan_fd_t sock; + + if (!dirmngr_use_tor ()) + return 0; + + sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR); + if (sock != ASSUAN_INVALID_FD) + { + assuan_sock_close (sock); + return 0; + } + + log_info ("(it seems Tor is not running)\n"); + dirmngr_status (ctrl, "WARNING", "tor_not_running 0", + "Tor is enabled but the local Tor daemon" + " seems to be down", NULL); + return 1; +} + + /* Add the host AI under the NAME into the HOSTTABLE. If PORT is not zero, it specifies which port to use to talk to the host. If NAME specifies a pool (as indicated by IS_POOL), update the given @@ -270,15 +292,20 @@ add_host (const char *name, int is_pool, gpg_error_t tmperr; char *tmphost; int idx, tmpidx; - int is_numeric = 0; int i; idx = find_hostinfo (name); - if (!is_pool && !is_ip_address (name)) + if (is_pool) { - /* This is a hostname but not a pool. Use the name - as given without going through resolve_dns_addr. */ + /* For a pool immediately convert the address to a string. */ + tmperr = resolve_dns_addr (ai->addr, ai->addrlen, + (DNS_NUMERICHOST | DNS_WITHBRACKET), &tmphost); + } + else if (!is_ip_address (name)) + { + /* This is a hostname. Use the name as given without going + * through resolve_dns_addr. */ tmphost = xtrystrdup (name); if (!tmphost) tmperr = gpg_error_from_syserror (); @@ -287,10 +314,10 @@ add_host (const char *name, int is_pool, } else { + /* Do a PTR lookup on AI. If a name was not found the function + * returns the numeric address (with brackets). */ tmperr = resolve_dns_addr (ai->addr, ai->addrlen, DNS_WITHBRACKET, &tmphost); - if (tmphost && is_ip_address (tmphost)) - is_numeric = 1; } if (tmperr) @@ -319,45 +346,21 @@ add_host (const char *name, int is_pool, if (tmpidx == -1) { - log_error ("map_host for '%s' problem: %s - '%s'" - " [ignored]\n", + log_error ("map_host for '%s' problem: %s - '%s' [ignored]\n", name, strerror (errno), tmphost); } else /* Set or update the entry. */ { - char *ipaddr = NULL; - if (port) hosttable[tmpidx]->port = port; - if (!is_numeric) - { - xfree (tmphost); - tmperr = resolve_dns_addr (ai->addr, ai->addrlen, - (DNS_NUMERICHOST - | DNS_WITHBRACKET), - &tmphost); - if (tmperr) - log_info ("resolve_dns_addr failed: %s\n", - gpg_strerror (tmperr)); - else - { - ipaddr = tmphost; - tmphost = NULL; - } - } - if (ai->family == AF_INET6) { hosttable[tmpidx]->v6 = 1; - xfree (hosttable[tmpidx]->v6addr); - hosttable[tmpidx]->v6addr = ipaddr; } else if (ai->family == AF_INET) { hosttable[tmpidx]->v4 = 1; - xfree (hosttable[tmpidx]->v4addr); - hosttable[tmpidx]->v4addr = ipaddr; } else BUG (); @@ -454,6 +457,8 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, if (err) { xfree (reftbl); + if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED) + tor_not_running_p (ctrl); return err; } @@ -505,6 +510,8 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, { if (ai->family != AF_INET && ai->family != AF_INET6) continue; + if (opt.disable_ipv4 && ai->family == AF_INET) + continue; dirmngr_tick (ctrl); add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx); @@ -585,7 +592,8 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, { for (ai = aibuf; ai; ai = ai->next) { - if (ai->family == AF_INET6 || ai->family == AF_INET) + if (ai->family == AF_INET6 + || (!opt.disable_ipv4 && ai->family == AF_INET)) { err = resolve_dns_addr (ai->addr, ai->addrlen, 0, &host); if (!err) @@ -789,6 +797,7 @@ ks_hkp_print_hosttable (ctrl_t ctrl) if (err) return err; + /* FIXME: We need a lock for the hosttable. */ curtime = gnupg_get_time (); for (idx=0; idx < hosttable_size; idx++) if ((hi=hosttable[idx])) @@ -800,16 +809,82 @@ ks_hkp_print_hosttable (ctrl_t ctrl) } else diedstr = died = NULL; - err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n", + + if (!hi->iporname_valid) + { + char *canon = NULL; + + xfree (hi->iporname); + hi->iporname = NULL; + + /* Do a lookup just for the display purpose. */ + if (hi->onion || hi->pool) + ; + else if (is_ip_address (hi->name)) + { + dns_addrinfo_t aibuf, ai; + + /* Turn the numerical IP address string into an AI and + * then do a DNS PTR lookup. */ + if (!resolve_dns_name (hi->name, 0, 0, + SOCK_STREAM, + &aibuf, &canon)) + { + if (canon && is_ip_address (canon)) + { + xfree (canon); + canon = NULL; + } + for (ai = aibuf; !canon && ai; ai = ai->next) + { + resolve_dns_addr (ai->addr, ai->addrlen, + DNS_WITHBRACKET, &canon); + if (canon && is_ip_address (canon)) + { + /* We already have the numeric IP - no need to + * display it a second time. */ + xfree (canon); + canon = NULL; + } + } + } + free_dns_addrinfo (aibuf); + } + else + { + dns_addrinfo_t aibuf, ai; + + /* Get the IP address as a string from a name. Note + * that resolve_dns_addr allocates CANON on success + * and thus terminates the loop. */ + if (!resolve_dns_name (hi->name, 0, + hi->v6? AF_INET6 : AF_INET, + SOCK_STREAM, + &aibuf, NULL)) + { + for (ai = aibuf; !canon && ai; ai = ai->next) + { + resolve_dns_addr (ai->addr, ai->addrlen, + DNS_NUMERICHOST|DNS_WITHBRACKET, + &canon); + } + } + free_dns_addrinfo (aibuf); + } + + hi->iporname = canon; + hi->iporname_valid = 1; + } + + err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s\n", idx, hi->onion? "O" : hi->v6? "6":" ", hi->v4? "4":" ", hi->dead? "d":" ", hi->name, - hi->v6addr? " v6=":"", - hi->v6addr? hi->v6addr:"", - hi->v4addr? " v4=":"", - hi->v4addr? hi->v4addr:"", + hi->iporname? " (":"", + hi->iporname? hi->iporname : "", + hi->iporname? ")":"", diedstr? " (":"", diedstr? diedstr:"", diedstr? ")":"" ); @@ -1016,6 +1091,7 @@ ks_hkp_reload (void) hi = hosttable[idx]; if (!hi) continue; + hi->iporname_valid = 0; if (!hi->dead) continue; hi->dead = 0; @@ -1047,7 +1123,10 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, *r_fp = NULL; - err = http_session_new (&session, NULL, httphost, HTTP_FLAG_TRUST_DEF); + err = http_session_new (&session, httphost, + ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) + | HTTP_FLAG_TRUST_DEF), + gnupg_http_tls_verify_cb, ctrl); if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); @@ -1060,7 +1139,8 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, /* fixme: AUTH */ NULL, (httpflags |(opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) - |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + |(dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0) + |(opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)), ctrl->http_proxy, session, NULL, @@ -1178,13 +1258,13 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, } -/* Helper to evaluate the error code ERR form a send_request() call +/* Helper to evaluate the error code ERR from a send_request() call with REQUEST. The function returns true if the caller shall try again. TRIES_LEFT points to a variable to track the number of retries; this function decrements it and won't return true if it is down to zero. */ static int -handle_send_request_error (gpg_error_t err, const char *request, +handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, unsigned int *tries_left) { int retry = 0; @@ -1195,16 +1275,9 @@ handle_send_request_error (gpg_error_t err, const char *request, switch (gpg_err_code (err)) { case GPG_ERR_ECONNREFUSED: - if (opt.use_tor) - { - assuan_fd_t sock; - - sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR); - if (sock == ASSUAN_INVALID_FD) - log_info ("(it seems Tor is not running)\n"); - else - assuan_sock_close (sock); - } + if (tor_not_running_p (ctrl)) + break; /* A retry does not make sense. */ + /* Okay: Tor is up or --use-tor is not used. */ /*FALLTHRU*/ case GPG_ERR_ENETUNREACH: case GPG_ERR_ENETDOWN: @@ -1222,6 +1295,16 @@ handle_send_request_error (gpg_error_t err, const char *request, } break; + case GPG_ERR_EACCES: + if (dirmngr_use_tor ()) + { + log_info ("(Tor configuration problem)\n"); + dirmngr_status (ctrl, "WARNING", "tor_config_problem 0", + "Please check that the \"SocksPort\" flag " + "\"IPv6Traffic\" is set in torrc", NULL); + } + break; + default: break; } @@ -1332,7 +1415,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, r_http_status); - if (handle_send_request_error (err, request, &tries)) + if (handle_send_request_error (ctrl, err, request, &tries)) { reselect = 1; goto again; @@ -1466,7 +1549,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, NULL); - if (handle_send_request_error (err, request, &tries)) + if (handle_send_request_error (ctrl, err, request, &tries)) { reselect = 1; goto again; @@ -1575,7 +1658,7 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, 0, put_post_cb, &parm, &fp, NULL); - if (handle_send_request_error (err, request, &tries)) + if (handle_send_request_error (ctrl, err, request, &tries)) { reselect = 1; goto again; diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index 858c943..d4a6c8a 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -76,7 +76,10 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) once_more: /* Note that we only use the system provided certificates with the * fetch command. */ - err = http_session_new (&session, NULL, NULL, HTTP_FLAG_TRUST_SYS); + err = http_session_new (&session, NULL, + ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) + | HTTP_FLAG_TRUST_SYS), + gnupg_http_tls_verify_cb, ctrl); if (err) goto leave; http_session_set_log_cb (session, cert_log_cb); @@ -88,7 +91,8 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) /* httphost */ NULL, /* fixme: AUTH */ NULL, ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) - | (opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + | (dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0) + | (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)), ctrl->http_proxy, session, NULL, diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index 6d520e9..f664655 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -850,7 +850,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, (void) ctrl; - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("LDAP access not possible due to Tor mode\n")); @@ -1033,7 +1033,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, (void) ctrl; - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("LDAP access not possible due to Tor mode\n")); @@ -1471,7 +1471,7 @@ modlist_dump (LDAPMod **modlist, estream_t output) for ((ptr = (*m)->mod_values), (i = 1); ptr && *ptr; ptr++, i ++) { /* Assuming terminals are about 80 characters wide, - display at most most about 10 lines of debugging + display at most about 10 lines of debugging output. If we do trim the buffer, append '...' to the end. */ const int max_len = 10 * 70; @@ -1909,7 +1909,7 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, /* Elide a warning. */ (void) ctrl; - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not support LDAP over Tor. */ log_error (_("LDAP access not possible due to Tor mode\n")); diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index 20cbbd8..a037f5d 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -445,26 +445,16 @@ make_url (char **url, const char *dn, const char *filter) xfree (u_dn); return err; } - *url = malloc ( 8 + strlen (u_dn) - + 1 + strlen (attrs) - + 5 + strlen (u_filter) + 1 ); + + *url = strconcat ("ldap:///", u_dn, "?", attrs, "?sub?", u_filter, NULL); if (!*url) - { - err = gpg_error_from_errno (errno); - xfree (u_dn); - xfree (u_filter); - return err; - } + err = gpg_error_from_syserror (); + else + err = 0; - stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (stpcpy (*url, "ldap:///"), - u_dn), - "?"), - attrs), - "?sub?"), - u_filter); xfree (u_dn); xfree (u_filter); - return 0; + return err; } diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c index 2d6bdc1..5a7778d 100644 --- a/dirmngr/loadswdb.c +++ b/dirmngr/loadswdb.c @@ -191,6 +191,9 @@ verify_status_cb (void *opaque, const char *keyword, char *args) { struct verify_status_parm_s *parm = opaque; + if (DBG_EXTPROG) + log_debug ("gpgv status: %s %s\n", keyword, args); + /* We care only about the first valid signature. */ if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid) { @@ -302,12 +305,16 @@ dirmngr_load_swdb (ctrl_t ctrl, int force) goto leave; } + if (DBG_EXTPROG) + log_debug ("starting gpgv\n"); err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV), argv, swdb, swdb_sig, NULL, verify_status_cb, &verify_status_parm); if (!err && verify_status_parm.sigtime == (time_t)(-1)) err = gpg_error (verify_status_parm.anyvalid? GPG_ERR_BAD_SIGNATURE /**/ : GPG_ERR_INV_TIME ); + if (DBG_EXTPROG) + log_debug ("gpgv finished: err=%d\n", err); if (err) goto leave; diff --git a/dirmngr/misc.c b/dirmngr/misc.c index 2ee6d82..6d7c963 100644 --- a/dirmngr/misc.c +++ b/dirmngr/misc.c @@ -62,6 +62,8 @@ hashify_data( const char* data, size_t len ) return hexify_data (buf, 20, 0); } + +/* FIXME: Replace this by hextobin. */ char* hexify_data (const unsigned char* data, size_t len, int with_prefix) { diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 9127cf7..aff8e32 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -132,7 +132,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, (void)ctrl; - if (opt.use_tor) + if (dirmngr_use_tor ()) { /* For now we do not allow OCSP via Tor due to possible privacy concerns. Needs further research. */ @@ -174,7 +174,8 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, once_more: err = http_open (&http, HTTP_REQ_POST, url, NULL, NULL, ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) - | (opt.use_tor? HTTP_FLAG_FORCE_TOR:0)), + | (dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0) + | (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)), ctrl->http_proxy, NULL, NULL, NULL); if (err) { diff --git a/dirmngr/server.c b/dirmngr/server.c index c9c4ad4..9fa8229 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -60,6 +60,10 @@ Dirmngr was a system service and not a user service. */ #define MAX_CERT_LENGTH (16*1024) +/* The limit for the CERTLIST inquiry. We allow for up to 20 + * certificates but also take PEM encoding into account. */ +#define MAX_CERTLIST_LENGTH ((MAX_CERT_LENGTH * 20 * 4)/3) + /* The same goes for OpenPGP keyblocks, but here we need to allow for much longer blocks; a 200k keyblock is not too unusual for keys with a lot of signatures (e.g. 0x5b0358a2). 9C31503C6D866396 even @@ -186,7 +190,7 @@ data_line_write (assuan_context_t ctx, const void *buffer_arg, size_t size) const char *buffer = buffer_arg; gpg_error_t err; - /* If we do not want logging, enable it it here. */ + /* If we do not want logging, enable it here. */ if (ctrl && ctrl->server_local && ctrl->server_local->inhibit_data_logging) ctrl->server_local->inhibit_data_logging_now = 1; @@ -366,17 +370,15 @@ do_get_cert_local (ctrl_t ctrl, const char *name, const char *command) char *buf; ksba_cert_t cert; - if (name) + buf = name? strconcat (command, " ", name, NULL) : xtrystrdup (command); + if (!buf) + rc = gpg_error_from_syserror (); + else { - buf = xmalloc ( strlen (command) + 1 + strlen(name) + 1); - strcpy (stpcpy (stpcpy (buf, command), " "), name); + rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, + &value, &valuelen, MAX_CERT_LENGTH); + xfree (buf); } - else - buf = xstrdup (command); - - rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, - &value, &valuelen, MAX_CERT_LENGTH); - xfree (buf); if (rc) { log_error (_("assuan_inquire(%s) failed: %s\n"), @@ -406,12 +408,11 @@ do_get_cert_local (ctrl_t ctrl, const char *name, const char *command) -/* Ask back to return a certificate for name, given as a regular - gpgsm certificate indentificates (e.g. fingerprint or one of the - other methods). Alternatively, NULL may be used for NAME to - return the current target certificate. Either return the certificate - in a KSBA object or NULL if it is not available. -*/ +/* Ask back to return a certificate for NAME, given as a regular gpgsm + * certificate identifier (e.g. fingerprint or one of the other + * methods). Alternatively, NULL may be used for NAME to return the + * current target certificate. Either return the certificate in a + * KSBA object or NULL if it is not available. */ ksba_cert_t get_cert_local (ctrl_t ctrl, const char *name) { @@ -425,13 +426,12 @@ get_cert_local (ctrl_t ctrl, const char *name) } -/* Ask back to return the issuing certificate for name, given as a - regular gpgsm certificate indentificates (e.g. fingerprint or one - of the other methods). Alternatively, NULL may be used for NAME to - return thecurrent target certificate. Either return the certificate - in a KSBA object or NULL if it is not available. -*/ +/* Ask back to return the issuing certificate for NAME, given as a + * regular gpgsm certificate identifier (e.g. fingerprint or one + * of the other methods). Alternatively, NULL may be used for NAME to + * return the current target certificate. Either return the certificate + * in a KSBA object or NULL if it is not available. */ ksba_cert_t get_issuing_cert_local (ctrl_t ctrl, const char *name) { @@ -444,8 +444,9 @@ get_issuing_cert_local (ctrl_t ctrl, const char *name) return do_get_cert_local (ctrl, name, "SENDISSUERCERT"); } + /* Ask back to return a certificate with subject NAME and a - subjectKeyIdentifier of KEYID. */ + * subjectKeyIdentifier of KEYID. */ ksba_cert_t get_cert_local_ski (ctrl_t ctrl, const char *name, ksba_sexp_t keyid) { @@ -475,15 +476,13 @@ get_cert_local_ski (ctrl_t ctrl, const char *name, ksba_sexp_t keyid) return NULL; } - buf = xtrymalloc (15 + strlen (hexkeyid) + 2 + strlen(name) + 1); + buf = strconcat ("SENDCERT_SKI ", hexkeyid, " /", name, NULL); if (!buf) { - log_error ("can't allocate enough memory: %s\n", strerror (errno)); xfree (hexkeyid); return NULL; } - strcpy (stpcpy (stpcpy (stpcpy (buf, "SENDCERT_SKI "), hexkeyid)," /"),name); xfree (hexkeyid); rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, @@ -625,9 +624,14 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) else if (!strcmp (key, "honor-keyserver-url-used")) { /* Return an error if we are running in Tor mode. */ - if (opt.use_tor) + if (dirmngr_use_tor ()) err = gpg_error (GPG_ERR_FORBIDDEN); } + else if (!strcmp (key, "http-crl")) + { + int i = *value? atoi (value) : 0; + ctrl->http_no_crl = !i; + } else err = gpg_error (GPG_ERR_UNKNOWN_OPTION); @@ -1735,7 +1739,7 @@ cmd_cachecert (assuan_context_t ctx, char *line) static const char hlp_validate[] = - "VALIDATE\n" + "VALIDATE [--systrust] [--tls] [--no-crl]\n" "\n" "Validate a certificate using the certificate validation function\n" "used internally by dirmngr. This command is only useful for\n" @@ -1745,20 +1749,40 @@ static const char hlp_validate[] = " INQUIRE TARGETCERT\n" "\n" "and the caller is expected to return the certificate for the\n" - "request as a binary blob."; + "request as a binary blob. The option --tls modifies this by asking\n" + "for list of certificates with\n" + "\n" + " INQUIRE CERTLIST\n" + "\n" + "Here the first certificate is the target certificate, the remaining\n" + "certificates are suggested intermediary certificates. All certifciates\n" + "need to be PEM encoded.\n" + "\n" + "The option --systrust changes the behaviour to include the system\n" + "provided root certificates as trust anchors. The option --no-crl\n" + "skips CRL checks"; static gpg_error_t cmd_validate (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; ksba_cert_t cert = NULL; + certlist_t certlist = NULL; unsigned char *value = NULL; size_t valuelen; + int systrust_mode, tls_mode, no_crl; - (void)line; + systrust_mode = has_option (line, "--systrust"); + tls_mode = has_option (line, "--tls"); + no_crl = has_option (line, "--no-crl"); + line = skip_options (line); - err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", - &value, &valuelen, MAX_CERT_LENGTH); + if (tls_mode) + err = assuan_inquire (ctrl->server_local->assuan_ctx, "CERTLIST", + &value, &valuelen, MAX_CERTLIST_LENGTH); + else + err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", + &value, &valuelen, MAX_CERT_LENGTH); if (err) { log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); @@ -1767,6 +1791,27 @@ cmd_validate (assuan_context_t ctx, char *line) if (!valuelen) /* No data returned; return a comprehensible error. */ err = gpg_error (GPG_ERR_MISSING_CERT); + else if (tls_mode) + { + estream_t fp; + + fp = es_fopenmem_init (0, "rb", value, valuelen); + if (!fp) + err = gpg_error_from_syserror (); + else + { + err = read_certlist_from_stream (&certlist, fp); + es_fclose (fp); + if (!err && !certlist) + err = gpg_error (GPG_ERR_MISSING_CERT); + if (!err) + { + /* Extraxt the first certificate from the list. */ + cert = certlist->cert; + ksba_cert_ref (cert); + } + } + } else { err = ksba_cert_new (&cert); @@ -1777,26 +1822,45 @@ cmd_validate (assuan_context_t ctx, char *line) if(err) goto leave; - /* If we have this certificate already in our cache, use the cached - version for validation because this will take care of any cached - results. */ - { - unsigned char fpr[20]; - ksba_cert_t tmpcert; + if (!tls_mode) + { + /* If we have this certificate already in our cache, use the + * cached version for validation because this will take care of + * any cached results. We don't need to do this in tls mode + * because this has already been done for certificate in a + * certlist_t. */ + unsigned char fpr[20]; + ksba_cert_t tmpcert; - cert_compute_fpr (cert, fpr); - tmpcert = get_cert_byfpr (fpr); - if (tmpcert) - { - ksba_cert_release (cert); - cert = tmpcert; - } - } + cert_compute_fpr (cert, fpr); + tmpcert = get_cert_byfpr (fpr); + if (tmpcert) + { + ksba_cert_release (cert); + cert = tmpcert; + } + } + + /* Quick hack to make verification work by inserting the supplied + * certs into the cache. */ + if (tls_mode && certlist) + { + certlist_t cl; - err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CERT, NULL); + for (cl = certlist->next; cl; cl = cl->next) + cache_cert (cl->cert); + } + + err = validate_cert_chain (ctrl, cert, NULL, + (VALIDATE_FLAG_TRUST_CONFIG + | (tls_mode ? VALIDATE_FLAG_TLS : 0) + | (systrust_mode ? VALIDATE_FLAG_TRUST_SYSTEM : 0) + | (no_crl ? VALIDATE_FLAG_NOCRLCHECK : 0)), + NULL); leave: ksba_cert_release (cert); + release_certlist (certlist); return leave_cmd (ctx, err); } @@ -2338,14 +2402,18 @@ cmd_getinfo (assuan_context_t ctx, char *line) } else if (!strcmp (line, "tor")) { - if (opt.use_tor) + int use_tor; + + use_tor = dirmngr_use_tor (); + if (use_tor) { if (!is_tor_running (ctrl)) err = assuan_write_status (ctx, "NO_TOR", "Tor not running"); else err = 0; if (!err) - assuan_set_okay_line (ctx, "- Tor mode is enabled"); + assuan_set_okay_line (ctx, use_tor == 1 ? "- Tor mode is enabled" + /**/ : "- Tor mode is enforced"); } else err = set_error (GPG_ERR_FALSE, "Tor mode is NOT enabled"); diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c index a87382a..35f5947 100644 --- a/dirmngr/t-http.c +++ b/dirmngr/t-http.c @@ -42,7 +42,7 @@ #include "logging.h" #include "http.h" - +#include <ksba.h> #if HTTP_USE_NTBTLS # include <ntbtls.h> #elif HTTP_USE_GNUTLS @@ -118,6 +118,57 @@ my_gnutls_log (int level, const char *text) } #endif +#if HTTP_USE_NTBTLS +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; + (void)http_flags; + + /* 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; +} +#endif /*HTTP_USE_NTBTLS*/ + + + /* Prepend FNAME with the srcdir environment variable's value and return an allocated filename. */ static char * @@ -142,14 +193,14 @@ 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; unsigned int my_http_flags = 0; int no_out = 0; int tls_dbg = 0; + int no_crl = 0; const char *cafile = NULL; http_session_t session = NULL; @@ -171,12 +222,13 @@ 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" " --force-tor use HTTP_FLAG_FORCE_TOR\n" - " --no-out do not print the content\n", + " --no-out do not print the content\n" + " --no-crl do not consuilt a CRL\n", stdout); exit (0); } @@ -191,7 +243,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) @@ -229,6 +281,11 @@ main (int argc, char **argv) no_out = 1; argc--; argv++; } + else if (!strcmp (*argv, "--no-crl")) + { + no_crl = 1; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) { fprintf (stderr, PGM ": unknown option '%s'\n", *argv); @@ -248,9 +305,13 @@ 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, + ((no_crl? HTTP_FLAG_NO_CRL : 0) + | 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 @@ -262,7 +323,10 @@ main (int argc, char **argv) http_register_tls_callback (verify_callback); http_register_tls_ca (cafile); - err = http_session_new (&session, NULL, NULL, HTTP_FLAG_TRUST_DEF); + err = http_session_new (&session, NULL, + ((no_crl? HTTP_FLAG_NO_CRL : 0) + | HTTP_FLAG_TRUST_DEF), + NULL, NULL); if (err) log_error ("http_session_new failed: %s\n", gpg_strerror (err)); diff --git a/dirmngr/validate.c b/dirmngr/validate.c index b3dc9d8..3671a8b 100644 --- a/dirmngr/validate.c +++ b/dirmngr/validate.c @@ -1,6 +1,6 @@ /* validate.c - Validate a certificate chain. * Copyright (C) 2001, 2003, 2004, 2008 Free Software Foundation, Inc. - * Copyright (C) 2004, 2006, 2008 g10 Code GmbH + * Copyright (C) 2004, 2006, 2008, 2017 g10 Code GmbH * * This file is part of DirMngr. * @@ -33,6 +33,20 @@ #include "validate.h" #include "misc.h" + +/* Mode parameters for cert_check_usage(). */ +enum cert_usage_modes + { + CERT_USAGE_MODE_SIGN, /* Usable for encryption. */ + CERT_USAGE_MODE_ENCR, /* Usable for signing. */ + CERT_USAGE_MODE_VRFY, /* Usable for verification. */ + CERT_USAGE_MODE_DECR, /* Usable for decryption. */ + CERT_USAGE_MODE_CERT, /* Usable for cert signing. */ + CERT_USAGE_MODE_OCSP, /* Usable for OCSP respone signing. */ + CERT_USAGE_MODE_CRL /* Usable for CRL signing. */ + }; + + /* While running the validation function we need to keep track of the certificates and the validation outcome of each. We use this type for it. */ @@ -60,6 +74,29 @@ static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; static gpg_error_t check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); +/* Make sure that the values defined in the headers are correct. We + * can't use the preprocessor due to the use of enums. */ +static void +check_header_constants (void) +{ + log_assert (CERTTRUST_CLASS_SYSTEM == VALIDATE_FLAG_TRUST_SYSTEM); + log_assert (CERTTRUST_CLASS_CONFIG == VALIDATE_FLAG_TRUST_CONFIG); + log_assert (CERTTRUST_CLASS_HKP == VALIDATE_FLAG_TRUST_HKP); + log_assert (CERTTRUST_CLASS_HKPSPOOL == VALIDATE_FLAG_TRUST_HKPSPOOL); + +#undef X +#define X (VALIDATE_FLAG_TRUST_SYSTEM | VALIDATE_FLAG_TRUST_CONFIG \ + | VALIDATE_FLAG_TRUST_HKP | VALIDATE_FLAG_TRUST_HKPSPOOL) + +#if ( X & VALIDATE_FLAG_MASK_TRUST ) != X +# error VALIDATE_FLAG_MASK_TRUST is bad +#endif +#if ( ~X & VALIDATE_FLAG_MASK_TRUST ) +# error VALIDATE_FLAG_MASK_TRUST is bad +#endif + +#undef X +} /* Check whether CERT contains critical extensions we don't know @@ -189,7 +226,7 @@ allowed_ca (ksba_cert_t cert, int *chainlen) return err; if (!flag) { - if (!is_trusted_cert (cert)) + if (!is_trusted_cert (cert, CERTTRUST_CLASS_CONFIG)) { /* The German SigG Root CA's certificate does not flag itself as a CA; thus we relax this requirement if we @@ -219,8 +256,8 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) int any_crl_too_old = 0; chain_item_t ci; - assert (ctrl->check_revocations_nest_level >= 0); - assert (chain); + log_assert (ctrl->check_revocations_nest_level >= 0); + log_assert (chain); if (ctrl->check_revocations_nest_level > 10) { @@ -365,19 +402,21 @@ is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) R_TRUST_ANCHOR; in all other cases NULL is stored there. */ gpg_error_t validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, - int mode, char **r_trust_anchor) + unsigned int flags, char **r_trust_anchor) { gpg_error_t err = 0; int depth, maxdepth; char *issuer = NULL; char *subject = NULL; - ksba_cert_t subject_cert = NULL, issuer_cert = NULL; + ksba_cert_t subject_cert = NULL; + ksba_cert_t issuer_cert = NULL; ksba_isotime_t current_time; ksba_isotime_t exptime; int any_expired = 0; int any_no_policy_match = 0; chain_item_t chain; + check_header_constants (); if (r_exptime) *r_exptime = 0; @@ -390,20 +429,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, dump_cert ("subject", cert); /* May the target certificate be used for this purpose? */ - switch (mode) - { - case VALIDATE_MODE_OCSP: - err = cert_use_ocsp_p (cert); - break; - case VALIDATE_MODE_CRL: - case VALIDATE_MODE_CRL_RECURSIVE: - err = cert_use_crl_p (cert); - break; - default: - err = 0; - break; - } - if (err) + if ((flags & VALIDATE_FLAG_OCSP) && (err = check_cert_use_ocsp (cert))) + return err; + if ((flags & VALIDATE_FLAG_CRL) && (err = check_cert_use_crl (cert))) return err; /* If we already validated the certificate not too long ago, we can @@ -438,7 +466,7 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, /* We walk up the chain until we find a trust anchor. */ subject_cert = cert; - maxdepth = 10; + maxdepth = 10; /* Sensible limit on the length of the chain. */ chain = NULL; depth = 0; for (;;) @@ -520,7 +548,7 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, goto leave; /* Is this a self-signed certificate? */ - if (is_root_cert ( subject_cert, issuer, subject)) + if (is_root_cert (subject_cert, issuer, subject)) { /* Yes, this is our trust anchor. */ if (check_cert_sig (subject_cert, subject_cert) ) @@ -536,7 +564,8 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, if (err) goto leave; /* No. */ - err = is_trusted_cert (subject_cert); + err = is_trusted_cert (subject_cert, + (flags & VALIDATE_FLAG_MASK_TRUST)); if (!err) ; /* Yes we trust this cert. */ else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED) @@ -630,9 +659,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, dump_cert ("issuer", issuer_cert); } - /* Now check the signature of the certificate. Well, we - should delay this until later so that faked certificates - can't be turned into a DoS easily. */ + /* Now check the signature of the certificate. FIXME: we should + * delay this until later so that faked certificates can't be + * turned into a DoS easily. */ err = check_cert_sig (issuer_cert, subject_cert); if (err) { @@ -669,14 +698,14 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } } #endif - /* We give a more descriptive error code than the one - returned from the signature checking. */ + /* Return a more descriptive error code than the one + * returned from the signature checking. */ err = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } /* Check that the length of the chain is not longer than allowed - by the CA. */ + * by the CA. */ { int chainlen; @@ -693,7 +722,7 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } /* May that certificate be used for certification? */ - err = cert_use_cert_p (issuer_cert); + err = check_cert_use_cert (issuer_cert); if (err) goto leave; /* No. */ @@ -722,9 +751,11 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, issuer_cert = NULL; } + /* Even if we have no error here we need to check whether we + * encountered an error somewhere during the checks. Set the error + * code to the most critical one. */ if (!err) - { /* If we encountered an error somewhere during the checks, set - the error code to the most critical one */ + { if (any_expired) err = gpg_error (GPG_ERR_CERT_EXPIRED); else if (any_no_policy_match) @@ -740,21 +771,26 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, cert_log_name (" certificate", citem->cert); } - if (!err && mode != VALIDATE_MODE_CRL) + /* Now check for revocations unless CRL checks are disabled or we + * are non-recursive CRL mode. */ + if (!err + && !(flags & VALIDATE_FLAG_NOCRLCHECK) + && !((flags & VALIDATE_FLAG_CRL) + && !(flags & VALIDATE_FLAG_RECURSIVE))) { /* Now that everything is fine, walk the chain and check each - certificate for revocations. - - 1. item in the chain - The root certificate. - 2. item - the CA below the root - last item - the target certificate. - - Now for each certificate in the chain check whether it has - been included in a CRL and thus be revoked. We don't do OCSP - here because this does not seem to make much sense. This - might become a recursive process and we should better cache - our validity results to avoid double work. Far worse a - catch-22 may happen for an improper setup hierarchy and we - need a way to break up such a deadlock. */ + * certificate for revocations. + * + * 1. item in the chain - The root certificate. + * 2. item - the CA below the root + * last item - the target certificate. + * + * Now for each certificate in the chain check whether it has + * been included in a CRL and thus be revoked. We don't do OCSP + * here because this does not seem to make much sense. This + * might become a recursive process and we should better cache + * our validity results to avoid double work. Far worse a + * catch-22 may happen for an improper setup hierarchy and we + * need a way to break up such a deadlock. */ err = check_revocations (ctrl, chain); } @@ -773,11 +809,11 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, if (!err && !(r_trust_anchor && *r_trust_anchor)) { /* With no error we can update the validation cache. We do this - for all certificates in the chain. Note that we can't use - the cache if the caller requested to check the trustiness of - the root certificate himself. Adding such a feature would - require us to also store the fingerprint of root - certificate. */ + * for all certificates in the chain. Note that we can't use + * the cache if the caller requested to check the trustiness of + * the root certificate himself. Adding such a feature would + * require us to also store the fingerprint of root + * certificate. */ chain_item_t citem; time_t validated_at = gnupg_get_time (); @@ -853,8 +889,8 @@ pk_algo_from_sexp (gcry_sexp_t pkey) /* Check the signature on CERT using the ISSUER_CERT. This function - does only test the cryptographic signature and nothing else. It is - assumed that the ISSUER_CERT is valid. */ + * does only test the cryptographic signature and nothing else. It is + * assumed that the ISSUER_CERT is valid. */ static gpg_error_t check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) { @@ -952,20 +988,23 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) /* Prepare the values for signature verification. At this point we - have these values: - - S_PKEY - S-expression with the issuer's public key. - S_SIG - Signature value as given in the certrificate. - MD - Finalized hash context with hash of the certificate. - ALGO_NAME - Lowercase hash algorithm name + * have these values: + * + * S_PKEY - S-expression with the issuer's public key. + * S_SIG - Signature value as given in the certificate. + * MD - Finalized hash context with hash of the certificate. + * ALGO_NAME - Lowercase hash algorithm name */ digestlen = gcry_md_get_algo_dlen (algo); digest = gcry_md_read (md, algo); if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA) { + /* NB.: We support only SHA-1 here because we had problems back + * then to get test data for DSA-2. Meanwhile DSA has been + * replaced by ECDSA which we do not yet support. */ if (digestlen != 20) { - log_error (_("DSA requires the use of a 160 bit hash algorithm\n")); + log_error ("DSA requires the use of a 160 bit hash algorithm\n"); gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); @@ -975,7 +1014,7 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) (int)digestlen, digest) ) BUG (); } - else /* Not DSA. */ + else /* Not DSA - we assume RSA */ { if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", algo_name, (int)digestlen, digest) ) @@ -995,13 +1034,9 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) -/* Return 0 if the cert is usable for encryption. A MODE of 0 checks - for signing, a MODE of 1 checks for encryption, a MODE of 2 checks - for verification and a MODE of 3 for decryption (just for - debugging). MODE 4 is for certificate signing, MODE 5 for OCSP - response signing, MODE 6 is for CRL signing. */ -static int -cert_usage_p (ksba_cert_t cert, int mode) +/* Return 0 if CERT is usable for MODE. */ +static gpg_error_t +check_cert_usage (ksba_cert_t cert, enum cert_usage_modes mode) { gpg_error_t err; unsigned int use; @@ -1071,7 +1106,8 @@ cert_usage_p (ksba_cert_t cert, int mode) if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; - if (opt.verbose && mode < 2) + if (opt.verbose && (mode == CERT_USAGE_MODE_SIGN + || mode == CERT_USAGE_MODE_ENCR)) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } @@ -1088,17 +1124,36 @@ cert_usage_p (ksba_cert_t cert, int mode) return err; } - if (mode == 4) + switch (mode) { + case CERT_USAGE_MODE_SIGN: + case CERT_USAGE_MODE_VRFY: + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_NON_REPUDIATION))) + return 0; + log_info (mode == CERT_USAGE_MODE_VRFY + ? _("certificate should not have been used for signing\n") + : _("certificate is not usable for signing\n")); + break; + + case CERT_USAGE_MODE_ENCR: + case CERT_USAGE_MODE_DECR: + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT + | KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + return 0; + log_info (mode == CERT_USAGE_MODE_DECR + ? _("certificate should not have been used for encryption\n") + : _("certificate is not usable for encryption\n")); + break; + + case CERT_USAGE_MODE_CERT: if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; log_info (_("certificate should not have " "been used for certification\n")); - return gpg_error (GPG_ERR_WRONG_KEY_USAGE); - } + break; - if (mode == 5) - { + case CERT_USAGE_MODE_OCSP: if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN @@ -1106,50 +1161,38 @@ cert_usage_p (ksba_cert_t cert, int mode) return 0; log_info (_("certificate should not have " "been used for OCSP response signing\n")); - return gpg_error (GPG_ERR_WRONG_KEY_USAGE); - } + break; - if (mode == 6) - { + case CERT_USAGE_MODE_CRL: if ((use & (KSBA_KEYUSAGE_CRL_SIGN))) return 0; log_info (_("certificate should not have " "been used for CRL signing\n")); - return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + break; } - if ((use & ((mode&1)? - (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): - (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) - ) - return 0; - - log_info (mode==3? _("certificate should not have been used " - "for encryption\n"): - mode==2? _("certificate should not have been used for signing\n"): - mode==1? _("certificate is not usable for encryption\n"): - _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } + /* Return 0 if the certificate CERT is usable for certification. */ gpg_error_t -cert_use_cert_p (ksba_cert_t cert) +check_cert_use_cert (ksba_cert_t cert) { - return cert_usage_p (cert, 4); + return check_cert_usage (cert, CERT_USAGE_MODE_CERT); } /* Return 0 if the certificate CERT is usable for signing OCSP responses. */ gpg_error_t -cert_use_ocsp_p (ksba_cert_t cert) +check_cert_use_ocsp (ksba_cert_t cert) { - return cert_usage_p (cert, 5); + return check_cert_usage (cert, CERT_USAGE_MODE_OCSP); } /* Return 0 if the certificate CERT is usable for signing CRLs. */ gpg_error_t -cert_use_crl_p (ksba_cert_t cert) +check_cert_use_crl (ksba_cert_t cert) { - return cert_usage_p (cert, 6); + return check_cert_usage (cert, CERT_USAGE_MODE_CRL); } diff --git a/dirmngr/validate.h b/dirmngr/validate.h index 0d9283c..c7082e3 100644 --- a/dirmngr/validate.h +++ b/dirmngr/validate.h @@ -22,34 +22,47 @@ #define VALIDATE_H -enum { - /* Simple certificate validation mode. */ - VALIDATE_MODE_CERT = 0, - /* Standard CRL issuer certificate validation; i.e. CRLs are not - considered for CRL issuer certificates. */ - VALIDATE_MODE_CRL = 1, - /* Full CRL validation. */ - VALIDATE_MODE_CRL_RECURSIVE = 2, - /* Validation as used for OCSP. */ - VALIDATE_MODE_OCSP = 3 -}; +/* Flag values matching the CERTTRUST_CLASS values and a MASK for + * them. check_header_constants() checks their consistency. */ +#define VALIDATE_FLAG_TRUST_SYSTEM 1 +#define VALIDATE_FLAG_TRUST_CONFIG 2 +#define VALIDATE_FLAG_TRUST_HKP 4 +#define VALIDATE_FLAG_TRUST_HKPSPOOL 8 +#define VALIDATE_FLAG_MASK_TRUST 0x0f + +/* Standard CRL issuer certificate validation; i.e. CRLs are not + * considered for CRL issuer certificates. */ +#define VALIDATE_FLAG_CRL 64 + +/* If this flag is set along with VALIDATE_FLAG_CRL a full CRL + * verification is done. */ +#define VALIDATE_FLAG_RECURSIVE 128 + +/* Validation mode as used for OCSP. */ +#define VALIDATE_FLAG_OCSP 256 + +/* Validation mode as used with TLS. */ +#define VALIDATE_FLAG_TLS 512 + +/* Don't do CRL checks. */ +#define VALIDATE_FLAG_NOCRLCHECK 1024 /* Validate the certificate CHAIN up to the trust anchor. Optionally return the closest expiration time in R_EXPTIME. */ gpg_error_t validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, - int mode, char **r_trust_anchor); + unsigned int flags, char **r_trust_anchor); /* Return 0 if the certificate CERT is usable for certification. */ -gpg_error_t cert_use_cert_p (ksba_cert_t cert); +gpg_error_t check_cert_use_cert (ksba_cert_t cert); /* Return 0 if the certificate CERT is usable for signing OCSP responses. */ -gpg_error_t cert_use_ocsp_p (ksba_cert_t cert); +gpg_error_t check_cert_use_ocsp (ksba_cert_t cert); /* Return 0 if the certificate CERT is usable for signing CRLs. */ -gpg_error_t cert_use_crl_p (ksba_cert_t cert); +gpg_error_t check_cert_use_crl (ksba_cert_t cert); #endif /*VALIDATE_H*/ |