summaryrefslogtreecommitdiff
path: root/dirmngr
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr')
-rw-r--r--dirmngr/ChangeLog-20114
-rw-r--r--dirmngr/Makefile.am7
-rw-r--r--dirmngr/certcache.c597
-rw-r--r--dirmngr/certcache.h32
-rw-r--r--dirmngr/crlcache.c6
-rw-r--r--dirmngr/crlfetch.c31
-rw-r--r--dirmngr/dirmngr.c101
-rw-r--r--dirmngr/dirmngr.h34
-rw-r--r--dirmngr/dns-stuff.c81
-rw-r--r--dirmngr/dns-stuff.h5
-rw-r--r--dirmngr/http-ntbtls.c124
-rw-r--r--dirmngr/http.c433
-rw-r--r--dirmngr/http.h17
-rw-r--r--dirmngr/ks-engine-finger.c4
-rw-r--r--dirmngr/ks-engine-hkp.c207
-rw-r--r--dirmngr/ks-engine-http.c8
-rw-r--r--dirmngr/ks-engine-ldap.c8
-rw-r--r--dirmngr/ldap.c22
-rw-r--r--dirmngr/loadswdb.c7
-rw-r--r--dirmngr/misc.c2
-rw-r--r--dirmngr/ocsp.c5
-rw-r--r--dirmngr/server.c166
-rw-r--r--dirmngr/t-http.c84
-rw-r--r--dirmngr/validate.c235
-rw-r--r--dirmngr/validate.h43
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*/