diff --git a/Makefile.am b/Makefile.am index c01c0a843..680fe1be1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,10 +18,21 @@ ## Process this file with automake to produce Makefile.in +# Location of the released tarball archives. Note that this is an +# internal archive and before uploading this to the public server, +# manual tests should be run and the git release tat set and pushed. +# Adjust as needed. +RELEASE_ARCHIVE_DIR = wk@vigenere:tarballs/gnupg/v2.2 + +# The key used to sign the released sources. Adjust as needed. +RELEASE_SIGNING_KEY = D8692123C4065DEA5E0F3AB5249B39D24F25E3B6 + + +# Autoconf flags. ACLOCAL_AMFLAGS = -I m4 AM_DISTCHECK_CONFIGURE_FLAGS = --enable-gnupg-builddir-envvar \ --enable-all-tests --enable-symcryptrun --enable-g13 \ - --enable-gpg2-is-gpg --enable-gpgtar --enable-wks-tools --disable-ntbtls + --enable-gpgtar --enable-wks-tools --disable-ntbtls GITLOG_TO_CHANGELOG=gitlog-to-changelog @@ -151,8 +162,68 @@ TESTS_ENVIRONMENT = \ objdir=$(abs_top_builddir) \ GPGSCM_PATH=$(abs_top_srcdir)/tests/gpgscm -.PHONY: check-all +.PHONY: check-all release sign-release check-all: $(TESTS_ENVIRONMENT) \ $(abs_top_builddir)/tests/gpgscm/gpgscm \ $(abs_srcdir)/tests/run-tests.scm $(TESTFLAGS) $(TESTS) + +# Names of to help the release target. +RELEASE_NAME = $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) +RELEASE_W32_STEM_NAME = $(PACKAGE_TARNAME)-w32-$(PACKAGE_VERSION) + +release: + +(set -e;\ + if [ "$(abs_top_builddir)" = "$(abs_top_srcdir)" ]; then \ + echo "error: build directory must not be the source directory" >&2;\ + exit 2;\ + fi ;\ + echo "/* Build started at $$(date -uIseconds) */" ;\ + cd $(top_srcdir); \ + ./autogen.sh --force; \ + cd $(abs_top_builddir); \ + rm -rf dist; mkdir dist ; cd dist ; \ + $(abs_top_srcdir)/configure --enable-maintainer-mode; \ + $(MAKE) distcheck TESTFLAGS=--parallel; \ + $(TAR) xjf $(RELEASE_NAME).tar.bz2 ;\ + $(MAKE) -f $(RELEASE_NAME)/build-aux/speedo.mk w32-release ;\ + echo "/* Build finished at $$(date -uIseconds) */" ;\ + echo "/*" ;\ + echo " * Please run the final step interactivly:" ;\ + echo " * make sign-release" ;\ + echo " */" ;\ + ) 2>&1 | tee "$(RELEASE_NAME).buildlog" + +sign-release: + +(set -e; \ + cd dist; \ + release_w32_name="$(RELEASE_W32_STEM_NAME)_$$(date -u +%Y%m%d)" ;\ + files1="$(RELEASE_NAME).tar.bz2 \ + $${release_w32_name}.tar.xz \ + $${release_w32_name}.exe" ;\ + files2="$(RELEASE_NAME).tar.bz2.sig \ + $(RELEASE_NAME).swdb \ + $(RELEASE_NAME).buildlog \ + $${release_w32_name}.tar.xz.sig \ + $${release_w32_name}.exe.sig \ + $${release_w32_name}.exe.swdb" ;\ + $(MAKE) -f $(RELEASE_NAME)/build-aux/speedo.mk w32-sign-installer ;\ + echo "/* Signing the source tarball ..." ;\ + gpg -sbu $(RELEASE_SIGNING_KEY) $(RELEASE_NAME).tar.bz2 ;\ + echo "/* Signing the W32 source tarball ..." ;\ + gpg -sbu $(RELEASE_SIGNING_KEY) $${release_w32_name}.tar.xz ;\ + echo "/* Signing the W32 installer ..." ;\ + gpg -sbu $(RELEASE_SIGNING_KEY) $${release_w32_name}.exe ;\ + cat $(RELEASE_NAME).swdb >swdb.snippet;\ + echo '#+macro: gnupg22_branch STABLE-BRANCH-2-2' >>swdb.snippet;\ + cat $${release_w32_name}.exe.swdb >>swdb.snippet;\ + echo >>swdb.snippet ;\ + sha1sum $${files1} >>swdb.snippet ;\ + cat "../$(RELEASE_NAME).buildlog" swdb.snippet \ + | gzip >$(RELEASE_NAME).buildlog ;\ + echo "Release created - copying it to the local archive ..." ;\ + scp -p $${files1} $${files2} $(RELEASE_ARCHIVE_DIR)/ || true;\ + echo '/*' ;\ + echo ' * All done; for checksums see dist/swdb.snippet' ;\ + echo ' */' ;\ + ) diff --git a/agent/command.c b/agent/command.c index a5baf4d9a..1a08cfcc0 100644 --- a/agent/command.c +++ b/agent/command.c @@ -2825,6 +2825,7 @@ static const char hlp_getinfo[] = " std_env_names - List the names of the standard environment.\n" " std_session_env - List the standard session environment.\n" " std_startup_env - List the standard startup environment.\n" + " getenv NAME - Return value of envvar NAME.\n" " connections - Return number of active connections.\n" " jent_active - Returns OK if Libgcrypt's JENT is active.\n" " restricted - Returns OK if the connection is in restricted mode.\n" @@ -2961,6 +2962,23 @@ cmd_getinfo (assuan_context_t ctx, char *line) } } } + else if (!strncmp (line, "getenv", 6) + && (line[6] == ' ' || line[6] == '\t' || !line[6])) + { + line += 6; + while (*line == ' ' || *line == '\t') + line++; + if (!*line) + rc = gpg_error (GPG_ERR_MISSING_VALUE); + else + { + const char *s = getenv (line); + if (!s) + rc = set_error (GPG_ERR_NOT_FOUND, "No such envvar"); + else + rc = assuan_send_data (ctx, s, strlen (s)); + } + } else if (!strcmp (line, "connections")) { char numbuf[20]; diff --git a/build-aux/getswdb.sh b/build-aux/getswdb.sh index 83ecdb2f3..cd419f2bd 100755 --- a/build-aux/getswdb.sh +++ b/build-aux/getswdb.sh @@ -175,9 +175,9 @@ fi # to help detect rollback attacks. # if [ $skip_selfcheck = no ]; then - gnupg_ver=$(awk '$1=="gnupg21_ver" {print $2;exit}' swdb.lst) + gnupg_ver=$(awk '$1=="gnupg22_ver" {print $2;exit}' swdb.lst) if [ -z "$gnupg_ver" ]; then - echo "GnuPG 2.1 version missing in swdb.lst!" >&2 + echo "GnuPG 2.2 version missing in swdb.lst!" >&2 exit 1 fi gnupg_ver_num=$(echo "$gnupg_ver" | cvtver) diff --git a/build-aux/speedo/w32/inst.nsi b/build-aux/speedo/w32/inst.nsi index b89876e44..fb452d513 100644 --- a/build-aux/speedo/w32/inst.nsi +++ b/build-aux/speedo/w32/inst.nsi @@ -625,6 +625,7 @@ Section "GnuPG" SEC_gnupg File "bin/gpgconf.exe" File "bin/gpg-connect-agent.exe" File "bin/gpgtar.exe" + File "libexec/dirmngr_ldap.exe" File "libexec/gpg-preset-passphrase.exe" File "libexec/gpg-wks-client.exe" @@ -743,6 +744,8 @@ Section "-libgpg-error" SEC_libgpg_error File share/locale/de/LC_MESSAGES/libgpg-error.mo SetOutPath "$INSTDIR\share\locale\eo\LC_MESSAGES" File share/locale/eo/LC_MESSAGES/libgpg-error.mo + SetOutPath "$INSTDIR\share\locale\es\LC_MESSAGES" + File share/locale/es/LC_MESSAGES/libgpg-error.mo SetOutPath "$INSTDIR\share\locale\fr\LC_MESSAGES" File share/locale/fr/LC_MESSAGES/libgpg-error.mo SetOutPath "$INSTDIR\share\locale\hu\LC_MESSAGES" @@ -1249,6 +1252,9 @@ Section "-un.libgpg-error" Delete "$INSTDIR\share\locale\eo\LC_MESSAGES\libgpg-error.mo" RMDir "$INSTDIR\share\locale\eo\LC_MESSAGES" RMDir "$INSTDIR\share\locale\eo" + Delete "$INSTDIR\share\locale\es\LC_MESSAGES\libgpg-error.mo" + RMDir "$INSTDIR\share\locale\es\LC_MESSAGES" + RMDir "$INSTDIR\share\locale\es" Delete "$INSTDIR\share\locale\fr\LC_MESSAGES\libgpg-error.mo" RMDir "$INSTDIR\share\locale\fr\LC_MESSAGES" RMDir "$INSTDIR\share\locale\fr" @@ -1307,6 +1313,7 @@ Section "-un.gnupg" Delete "$INSTDIR\bin\gpgconf.exe" Delete "$INSTDIR\bin\gpg-connect-agent.exe" Delete "$INSTDIR\bin\gpgtar.exe" + Delete "$INSTDIR\bin\dirmngr_ldap.exe" Delete "$INSTDIR\bin\gpg-preset-passphrase.exe" Delete "$INSTDIR\bin\gpg-wks-client.exe" diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c index fddcbb65c..86b1d6869 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -556,7 +556,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE; si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0]; si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; diff --git a/common/miscellaneous.c b/common/miscellaneous.c index 7997a1a83..0b374e6c8 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -401,6 +401,7 @@ is_file_compressed (const char *s, int *ret_rc) *ret_rc = gpg_error_from_syserror (); return 0; } + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) { *ret_rc = 0; diff --git a/configure.ac b/configure.ac index 540dffcd0..0665115dc 100644 --- a/configure.ac +++ b/configure.ac @@ -665,7 +665,6 @@ case "${host}" in have_dosish_system=yes have_w32_system=yes require_iconv=no - use_ldapwrapper=no # Fixme: Do this only for CE. require_pipe_to_unblock_pselect=no case "${host}" in *-mingw32ce*) @@ -976,6 +975,17 @@ else *** we need the support of the New Portable Threads Library. ***]]) fi +# +# Enable debugging of nPth +# +AC_ARG_ENABLE(npth-debug, + AC_HELP_STRING([--enable-npth-debug], + [build with debug version of npth]), + [if test $enableval = yes ; then + AC_DEFINE(NPTH_ENABLE_DEBUG,1, + [Build with debug version of nPth]) + fi]) + # diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index 56629fdda..adb005ec8 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -423,6 +423,9 @@ load_certs_from_dir (const char *dirname, unsigned int trustclass) log_info (_("certificate '%s' already cached\n"), fname); else if (!err) { + if ((trustclass & CERTTRUST_CLASS_CONFIG)) + http_register_cfg_ca (fname); + if (trustclass) log_info (_("trusted certificate '%s' loaded\n"), fname); else @@ -763,6 +766,8 @@ cert_cache_deinit (int full) } } + http_register_cfg_ca (NULL); + total_nonperm_certificates = 0; any_cert_of_class = 0; initialization_done = 0; diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 8687c7bf4..fbe3beea1 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -125,6 +125,9 @@ idea anyway to limit the number of opened cache files. */ #define MAX_OPEN_DB_FILES 5 +#ifndef O_BINARY +# define O_BINARY 0 +#endif static const char oidstr_crlNumber[] = "2.5.29.20"; /* static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; */ @@ -1139,7 +1142,7 @@ lock_db_file (crl_cache_t cache, crl_cache_entry_t entry) xfree (fname); return NULL; } - fd = open (fname, O_RDONLY); + fd = open (fname, O_RDONLY | O_BINARY); if (fd == -1) { log_error (_("error opening cache file '%s': %s\n"), @@ -2051,7 +2054,7 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) } } - fd_cdb = open (fname, O_WRONLY | O_CREAT | O_TRUNC, 0644); + fd_cdb = open (fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd_cdb == -1) { err = gpg_error_from_errno (errno); diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c index 0892421e9..57ac51b93 100644 --- a/dirmngr/crlfetch.c +++ b/dirmngr/crlfetch.c @@ -28,6 +28,7 @@ #include "dirmngr.h" #include "misc.h" #include "http.h" +#include "ks-engine.h" /* For ks_http_fetch. */ #if USE_LDAP # include "ldap-wrapper.h" @@ -154,41 +155,17 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) { gpg_error_t err; parsed_uri_t uri; - char *free_this = NULL; - int redirects_left = 2; /* We allow for 2 redirect levels. */ + estream_t httpfp = NULL; *reader = NULL; if (!url) return gpg_error (GPG_ERR_INV_ARG); - once_more: err = http_parse_uri (&uri, url, 0); http_release_parsed_uri (uri); - if (err && !strncmp (url, "https:", 6)) - { - /* 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) - { - strcpy (stpcpy (free_this,"http:"), url+6); - err = http_parse_uri (&uri, free_this, 0); - http_release_parsed_uri (uri); - if (!err) - { - log_info (_("using \"http\" instead of \"https\"\n")); - url = free_this; - } - } - } if (!err) /* Yes, our HTTP code groks that. */ { - http_t hd; - if (opt.disable_http) { log_error (_("CRL access not possible due to disabled %s\n"), @@ -196,97 +173,57 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) err = gpg_error (GPG_ERR_NOT_SUPPORTED); } else - err = http_open_document (&hd, url, NULL, - ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0) - |(DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0) - |(dirmngr_use_tor()? HTTP_FLAG_FORCE_TOR:0) - |(opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4:0) - |(opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6:0) - ), - ctrl->http_proxy, NULL, NULL, NULL); - - switch ( err? 99999 : http_get_status_code (hd) ) { - case 200: - { - estream_t fp = http_get_read_ptr (hd); - struct reader_cb_context_s *cb_ctx; + /* Note that we also allow root certificates loaded from + * "/etc/gnupg/trusted-certs/". We also do not consult the + * CRL for the TLS connection - that may lead to a loop. + * Due to cacert.org redirecting their https URL to http we + * also allow such a downgrade. */ + err = ks_http_fetch (ctrl, url, + (KS_HTTP_FETCH_TRUST_CFG + | KS_HTTP_FETCH_NO_CRL + | KS_HTTP_FETCH_ALLOW_DOWNGRADE ), + &httpfp); + } - cb_ctx = xtrycalloc (1, sizeof *cb_ctx); - if (!cb_ctx) - err = gpg_error_from_syserror (); - if (!err) - err = ksba_reader_new (reader); - if (!err) - { - cb_ctx->fp = fp; - err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx); - } - if (err) - { - log_error (_("error initializing reader object: %s\n"), - gpg_strerror (err)); - ksba_reader_release (*reader); - *reader = NULL; - http_close (hd, 0); - } - else - { - /* The ksba reader misses a user pointer thus we need - to come up with our own way of associating a file - pointer (or well the callback context) with the - reader. It is only required when closing the - reader thus there is no performance issue doing it - this way. FIXME: We now have a close notification - which might be used here. */ - register_file_reader (*reader, cb_ctx); - http_close (hd, 1); - } - } - break; + if (err) + log_error (_("error retrieving '%s': %s\n"), url, gpg_strerror (err)); + else + { + struct reader_cb_context_s *cb_ctx; - case 301: /* Redirection (perm.). */ - case 302: /* Redirection (temp.). */ - { - const char *s = http_get_header (hd, "Location"); + cb_ctx = xtrycalloc (1, sizeof *cb_ctx); + if (!cb_ctx) + err = gpg_error_from_syserror (); + else if (!(err = ksba_reader_new (reader))) + { + cb_ctx->fp = httpfp; + err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx); + if (!err) + { + /* The ksba reader misses a user pointer thus we + * need to come up with our own way of associating a + * file pointer (or well the callback context) with + * the reader. It is only required when closing the + * reader thus there is no performance issue doing + * it this way. FIXME: We now have a close + * notification which might be used here. */ + register_file_reader (*reader, cb_ctx); + httpfp = NULL; + } + } - log_info (_("URL '%s' redirected to '%s' (%u)\n"), - url, s?s:"[none]", http_get_status_code (hd)); - if (s && *s && redirects_left-- ) - { - xfree (free_this); url = NULL; - free_this = xtrystrdup (s); - if (!free_this) - err = gpg_error_from_errno (errno); - else - { - url = free_this; - http_close (hd, 0); - /* Note, that our implementation of redirection - actually handles a redirect to LDAP. */ - goto once_more; - } - } - else - err = gpg_error (GPG_ERR_NO_DATA); - log_error (_("too many redirections\n")); /* Or no "Location". */ - http_close (hd, 0); - } - break; - - case 99999: /* Made up status code for error reporting. */ - log_error (_("error retrieving '%s': %s\n"), - url, gpg_strerror (err)); - break; - - default: - log_error (_("error retrieving '%s': http status %u\n"), - url, http_get_status_code (hd)); - err = gpg_error (GPG_ERR_NO_DATA); - http_close (hd, 0); + if (err) + { + log_error (_("error initializing reader object: %s\n"), + gpg_strerror (err)); + ksba_reader_release (*reader); + *reader = NULL; + xfree (cb_ctx); + } } } - else /* Let the LDAP code try other schemes. */ + else /* Let the LDAP code parse other schemes. */ { if (opt.disable_ldap) { @@ -310,7 +247,7 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) } } - xfree (free_this); + es_fclose (httpfp); return err; } diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index 00caf0686..6fdfe36c2 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -2243,7 +2243,8 @@ handle_connections (assuan_fd_t listen_fd) npth_timersub (&abstime, &curtime, &timeout); #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask()); + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + npth_sigev_sigmask()); saved_errno = errno; while (npth_sigev_get_pending(&signo)) diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index 5be4e5814..8452c3ba0 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -29,7 +29,6 @@ # include #endif #include -#include #include #include #ifndef USE_LDAPWRAPPER @@ -343,7 +342,7 @@ ldap_wrapper_main (char **argv, estream_t outstream) usage (1); #else /* All passed arguments should be fine in this case. */ - assert (argc); + log_assert (argc); #endif #ifdef USE_LDAPWRAPPER @@ -382,16 +381,56 @@ catch_alarm (int dummy) } #endif + +#ifdef HAVE_W32_SYSTEM +static DWORD CALLBACK +alarm_thread (void *arg) +{ + HANDLE timer = arg; + + WaitForSingleObject (timer, INFINITE); + _exit (10); + + return 0; +} +#endif + + static void set_timeout (my_opt_t myopt) { -#ifdef HAVE_W32_SYSTEM - /* FIXME for W32. */ - (void)myopt; -#else if (myopt->alarm_timeout) - alarm (myopt->alarm_timeout); + { +#ifdef HAVE_W32_SYSTEM + static HANDLE timer; + LARGE_INTEGER due_time; + + /* A negative value is a relative time. */ + due_time.QuadPart = (unsigned long long)-10000000 * myopt->alarm_timeout; + + if (!timer) + { + SECURITY_ATTRIBUTES sec_attr; + DWORD tid; + + memset (&sec_attr, 0, sizeof sec_attr); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Create a manual resetable timer. */ + timer = CreateWaitableTimer (NULL, TRUE, NULL); + /* Intially set the timer. */ + SetWaitableTimer (timer, &due_time, 0, NULL, NULL, 0); + + if (CreateThread (&sec_attr, 0, alarm_thread, timer, 0, &tid)) + log_error ("failed to create alarm thread\n"); + } + else /* Retrigger the timer. */ + SetWaitableTimer (timer, &due_time, 0, NULL, NULL, 0); +#else + alarm (myopt->alarm_timeout); #endif + } } diff --git a/dirmngr/http-ntbtls.c b/dirmngr/http-ntbtls.c index ea66a4d73..ed4cdd496 100644 --- a/dirmngr/http-ntbtls.c +++ b/dirmngr/http-ntbtls.c @@ -87,13 +87,15 @@ gnupg_http_tls_verify_cb (void *opaque, } else /* Use the certificates as requested from the HTTP module. */ { + if ((http_flags & HTTP_FLAG_TRUST_CFG)) + validate_flags |= VALIDATE_FLAG_TRUST_CONFIG; 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 HKP trust is requested and there are no HKP certificates - * configured, also try thye standard system certificates. */ + * configured, also try the standard system certificates. */ if ((validate_flags & VALIDATE_FLAG_TRUST_HKP) && !cert_cache_any_in_class (CERTTRUST_CLASS_HKP)) validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM; diff --git a/dirmngr/http.c b/dirmngr/http.c index cc7f5a553..049aefc29 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -318,6 +318,9 @@ static gpg_error_t (*tls_callback) (http_t, http_session_t, int); /* The list of files with trusted CA certificates. */ static strlist_t tls_ca_certlist; +/* The list of files with extra trusted CA certificates. */ +static strlist_t cfg_ca_certlist; + /* The global callback for net activity. */ static void (*netactivity_cb)(void); @@ -596,6 +599,35 @@ http_register_tls_ca (const char *fname) } +/* Register a CA certificate for future use. The certificate is + * expected to be in FNAME. PEM format is assume if FNAME has a + * suffix of ".pem". If FNAME is NULL the list of CA files is + * removed. This is a variant of http_register_tls_ca which puts the + * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG. */ +void +http_register_cfg_ca (const char *fname) +{ + strlist_t sl; + + if (!fname) + { + free_strlist (cfg_ca_certlist); + cfg_ca_certlist = NULL; + } + else + { + /* Warn if we can't access right now, but register it anyway in + case it becomes accessible later */ + if (access (fname, F_OK)) + log_info (_("can't access '%s': %s\n"), fname, + gpg_strerror (gpg_error_from_syserror())); + sl = add_to_strlist (&cfg_ca_certlist, fname); + if (*sl->d && !strcmp (sl->d + strlen (sl->d) - 4, ".pem")) + sl->flags = 1; + } +} + + /* Register a callback which is called every time the HTTP mode has * made a successful connection to some server. */ void @@ -680,6 +712,7 @@ 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_TRUST_CFG - Also use CAs set with http_register_cfg_ca * HTTP_FLAG_NO_CRL - Do not consult CRLs for https. */ gpg_error_t @@ -793,6 +826,21 @@ http_session_new (http_session_t *r_session, #endif /* gnutls >= 3.0.20 */ } + /* Add other configured certificates to the session. */ + if ((flags & HTTP_FLAG_TRUST_CFG)) + { + for (sl = cfg_ca_certlist; sl; sl = sl->next) + { + rc = gnutls_certificate_set_x509_trust_file + (sess->certcred, sl->d, + (sl->flags & 1)? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER); + if (rc < 0) + log_info ("setting extra CA from file '%s' failed: %s\n", + sl->d, gnutls_strerror (rc)); + } + } + + rc = gnutls_init (&sess->tls_session, GNUTLS_CLIENT); if (rc < 0) { @@ -1688,9 +1736,19 @@ send_request (http_t hd, const char *httphost, const char *auth, #ifdef USE_TLS if (hd->uri->use_tls && !hd->session->tls_session) { - log_error ("TLS requested but no GNUTLS context available\n"); + log_error ("TLS requested but no TLS context available\n"); return gpg_err_make (default_errsource, GPG_ERR_INTERNAL); } + if (opt_debug) + log_debug ("Using TLS library: %s %s\n", +# if HTTP_USE_NTBTLS + "NTBTLS", ntbtls_check_version (NULL) +# elif HTTP_USE_GNUTLS + "GNUTLS", gnutls_check_version (NULL) +# else + "?", "?" +# endif /*HTTP_USE_*TLS*/ + ); #endif /*USE_TLS*/ if ((hd->flags & HTTP_FLAG_FORCE_TOR)) diff --git a/dirmngr/http.h b/dirmngr/http.h index 9fa462c05..4cfb4c890 100644 --- a/dirmngr/http.h +++ b/dirmngr/http.h @@ -88,8 +88,9 @@ enum 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 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. */ + HTTP_FLAG_TRUST_SYS = 512, /* Also use the system defined CAs. */ + HTTP_FLAG_TRUST_CFG = 1024, /* Also use configured CAs. */ + HTTP_FLAG_NO_CRL = 2048 /* Do not consult CRLs for https. */ }; @@ -110,6 +111,7 @@ void http_set_verbose (int verbose, int debug); void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int)); void http_register_tls_ca (const char *fname); +void http_register_cfg_ca (const char *fname); void http_register_netactivity_cb (void (*cb)(void)); diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index 38cd02feb..c1ecafb58 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -257,7 +257,9 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, if (is_hkp_s) err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp); else if (is_http_s) - err = ks_http_fetch (ctrl, uri->parsed_uri->original, &infp); + err = ks_http_fetch (ctrl, uri->parsed_uri->original, + KS_HTTP_FETCH_NOCACHE, + &infp); else BUG (); @@ -314,7 +316,7 @@ ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp) if (parsed_uri->is_http) { - err = ks_http_fetch (ctrl, url, &infp); + err = ks_http_fetch (ctrl, url, KS_HTTP_FETCH_NOCACHE, &infp); if (!err) { err = copy_stream (infp, outfp); diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index a9bb93666..32840e68e 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -55,7 +55,7 @@ /* Number of seconds after a host is marked as resurrected. */ -#define RESURRECT_INTERVAL (3600*3) /* 3 hours */ +#define RESURRECT_INTERVAL (3600+1800) /* 1.5 hours */ /* To match the behaviour of our old gpgkeys helper code we escape more characters than actually needed. */ @@ -110,7 +110,7 @@ static hostinfo_t *hosttable; static int hosttable_size; /* The number of host slots we initially allocate for HOSTTABLE. */ -#define INITIAL_HOSTTABLE_SIZE 10 +#define INITIAL_HOSTTABLE_SIZE 50 /* Create a new hostinfo object, fill in NAME and put it into @@ -583,7 +583,7 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect, /* Deal with the pool name before selecting a host. */ if (r_httphost) { - *r_httphost = xtrystrdup (hi->cname? hi->cname : hi->name); + *r_httphost = xtrystrdup (hi->name); if (!*r_httphost) return gpg_error_from_syserror (); } diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index 6492dda8a..946c92769 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -62,12 +62,17 @@ ks_http_help (ctrl_t ctrl, parsed_uri_t uri) /* Get the key from URL which is expected to specify a http style - scheme. On success R_FP has an open stream to read the data. */ + * scheme. On success R_FP has an open stream to read the data. + * Despite its name this function is also used to retrieve arbitrary + * data via https or http. + */ gpg_error_t -ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) +ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, + estream_t *r_fp) { gpg_error_t err; http_session_t session = NULL; + unsigned int session_flags; http_t http = NULL; int redirects_left = MAX_REDIRECTS; estream_t fp = NULL; @@ -81,12 +86,16 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) is_onion = uri->onion; is_https = uri->use_tls; - once_more: - /* Note that we only use the system provided certificates with the + /* By default we only use the system provided certificates with this * fetch command. */ - err = http_session_new (&session, NULL, - ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) - | HTTP_FLAG_TRUST_SYS), + session_flags = HTTP_FLAG_TRUST_SYS; + if ((flags & KS_HTTP_FETCH_NO_CRL) || ctrl->http_no_crl) + session_flags |= HTTP_FLAG_NO_CRL; + if ((flags & KS_HTTP_FETCH_TRUST_CFG)) + session_flags |= HTTP_FLAG_TRUST_CFG; + + once_more: + err = http_session_new (&session, NULL, session_flags, gnupg_http_tls_verify_cb, ctrl); if (err) goto leave; @@ -100,6 +109,7 @@ 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) + | (DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0) | (dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0) | (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0) | (opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6 : 0)), @@ -111,10 +121,11 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) { fp = http_get_write_ptr (http); /* Avoid caches to get the most recent copy of the key. We set - both the Pragma and Cache-Control versions of the header, so - we're good with both HTTP 1.0 and 1.1. */ - es_fputs ("Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n", fp); + * both the Pragma and Cache-Control versions of the header, so + * we're good with both HTTP 1.0 and 1.1. */ + if ((flags & KS_HTTP_FETCH_NOCACHE)) + es_fputs ("Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n", fp); http_start_data (http); if (es_ferror (fp)) err = gpg_error_from_syserror (); @@ -164,7 +175,13 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp) if (err) goto leave; - if ((is_onion && ! uri->onion) || (is_https && ! uri->use_tls)) + if (is_onion && !uri->onion) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + if (!(flags & KS_HTTP_FETCH_ALLOW_DOWNGRADE) + && is_https && !uri->use_tls) { err = gpg_error (GPG_ERR_FORBIDDEN); goto leave; diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h index b5b4dd08b..d28c6ab71 100644 --- a/dirmngr/ks-engine.h +++ b/dirmngr/ks-engine.h @@ -41,8 +41,16 @@ gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen); /*-- ks-engine-http.c --*/ + +/* Flags for the ks_http_fetch. */ +#define KS_HTTP_FETCH_NOCACHE 1 /* Request no caching. */ +#define KS_HTTP_FETCH_TRUST_CFG 2 /* Requests HTTP_FLAG_TRUST_CFG. */ +#define KS_HTTP_FETCH_NO_CRL 4 /* Requests HTTP_FLAG_NO_CRL. */ +#define KS_HTTP_FETCH_ALLOW_DOWNGRADE 8 /* Allow redirect https -> http. */ + gpg_error_t ks_http_help (ctrl_t ctrl, parsed_uri_t uri); -gpg_error_t ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp); +gpg_error_t ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, + estream_t *r_fp); /*-- ks-engine-finger.c --*/ diff --git a/dirmngr/ldap-wrapper-ce.c b/dirmngr/ldap-wrapper-ce.c index 01f8f647e..884bb325d 100644 --- a/dirmngr/ldap-wrapper-ce.c +++ b/dirmngr/ldap-wrapper-ce.c @@ -45,6 +45,7 @@ #ifdef USE_LDAPWRAPPER # error This module is not expected to be build. #endif +#error This module might not anymore work. diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c index 8b53bd60f..d01c4808e 100644 --- a/dirmngr/ldap-wrapper.c +++ b/dirmngr/ldap-wrapper.c @@ -1,5 +1,5 @@ /* ldap-wrapper.c - LDAP access via a wrapper process - * Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH + * Copyright (C) 2004, 2005, 2007, 2008, 2018 g10 Code GmbH * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -19,31 +19,34 @@ */ /* - We can't use LDAP directly for these reasons: - - 1. On some systems the LDAP library uses (indirectly) pthreads and - that is not compatible with PTh. - - 2. It is huge library in particular if TLS comes into play. So - problems with unfreed memory might turn up and we don't want - this in a long running daemon. - - 3. There is no easy way for timeouts. In particular the timeout - value does not work for DNS lookups (well, this is usual) and it - seems not to work while loading a large attribute like a - CRL. Having a separate process allows us to either tell the - process to commit suicide or have our own housekepping function - kill it after some time. The latter also allows proper - cancellation of a query at any point of time. - - 4. Given that we are going out to the network and usually get back - a long response, the fork/exec overhead is acceptable. - - Note that under WindowsCE the number of processes is strongly - limited (32 processes including the kernel processes) and thus we - don't use the process approach but implement a different wrapper in - ldap-wrapper-ce.c. -*/ + * We can't use LDAP directly for these reasons: + * + * 1. On some systems the LDAP library uses (indirectly) pthreads and + * that is not compatible with GNU Pth. Since 2.1 we use nPth + * instead of GNU Pth which does not have this problem anymore + * because it will use pthreads if the platform supports it. Thus + * this was a historical reasons. + * + * 2. It is huge library in particular if TLS comes into play. So + * problems with unfreed memory might turn up and we don't want + * this in a long running daemon. + * + * 3. There is no easy way for timeouts. In particular the timeout + * value does not work for DNS lookups (well, this is usual) and it + * seems not to work while loading a large attribute like a + * CRL. Having a separate process allows us to either tell the + * process to commit suicide or have our own housekepping function + * kill it after some time. The latter also allows proper + * cancellation of a query at any point of time. + * + * 4. Given that we are going out to the network and usually get back + * a long response, the fork/exec overhead is acceptable. + * + * Note that under WindowsCE the number of processes is strongly + * limited (32 processes including the kernel processes) and thus we + * don't use the process approach but implement a different wrapper in + * ldap-wrapper-ce.c. + */ #include @@ -89,39 +92,66 @@ struct wrapper_context_s { struct wrapper_context_s *next; - pid_t pid; /* The pid of the wrapper process. */ - int printable_pid; /* Helper to print diagnostics after the process has - been cleaned up. */ - int fd; /* Connected with stdout of the ldap wrapper. */ - gpg_error_t fd_error; /* Set to the gpg_error of the last read error - if any. */ - int log_fd; /* Connected with stderr of the ldap wrapper. */ - ctrl_t ctrl; /* Connection data. */ - int ready; /* Internally used to mark to be removed contexts. */ - ksba_reader_t reader; /* The ksba reader object or NULL. */ - char *line; /* Used to print the log lines (malloced). */ - size_t linesize;/* Allocated size of LINE. */ - size_t linelen; /* Use size of LINE. */ - time_t stamp; /* The last time we noticed ativity. */ + pid_t pid; /* The pid of the wrapper process. */ + int printable_pid; /* Helper to print diagnostics after the process has + * been cleaned up. */ + estream_t fp; /* Connected with stdout of the ldap wrapper. */ + gpg_error_t fp_err; /* Set to the gpg_error of the last read error + * if any. */ + estream_t log_fp; /* Connected with stderr of the ldap wrapper. */ + ctrl_t ctrl; /* Connection data. */ + int ready; /* Internally used to mark to be removed contexts. */ + ksba_reader_t reader;/* The ksba reader object or NULL. */ + char *line; /* Used to print the log lines (malloced). */ + size_t linesize; /* Allocated size of LINE. */ + size_t linelen; /* Use size of LINE. */ + time_t stamp; /* The last time we noticed ativity. */ + int reaper_idx; /* Private to ldap_wrapper_thread. */ }; -/* We keep a global list of spawned wrapper process. A separate thread - makes use of this list to log error messages and to watch out for - finished processes. */ -static struct wrapper_context_s *wrapper_list; +/* We keep a global list of spawned wrapper process. A separate + * thread makes use of this list to log error messages and to watch + * out for finished processes. Access to list is protected by a + * mutex. The condition variable is used to wakeup the reaper + * thread. */ +static struct wrapper_context_s *reaper_list; +static npth_mutex_t reaper_list_mutex = NPTH_MUTEX_INITIALIZER; +static npth_cond_t reaper_run_cond = NPTH_COND_INITIALIZER; /* We need to know whether we are shutting down the process. */ static int shutting_down; -/* Close the pth file descriptor FD and set it to -1. */ -#define SAFE_CLOSE(fd) \ - do { int _fd = fd; if (_fd != -1) { close (_fd); fd = -1;} } while (0) + + +/* Close the estream fp and set it to NULL. */ +#define SAFE_CLOSE(fp) \ + do { estream_t _fp = fp; es_fclose (_fp); fp = NULL; } while (0) + +static void +lock_reaper_list (void) +{ + if (npth_mutex_lock (&reaper_list_mutex)) + log_fatal ("%s: failed to acquire mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); +} + + +static void +unlock_reaper_list (void) +{ + if (npth_mutex_unlock (&reaper_list_mutex)) + log_fatal ("%s: failed to release mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); +} + + + /* Read a fixed amount of data from READER into BUFFER. */ static gpg_error_t read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) @@ -151,8 +181,8 @@ destroy_wrapper (struct wrapper_context_s *ctx) gnupg_release_process (ctx->pid); } ksba_reader_release (ctx->reader); - SAFE_CLOSE (ctx->fd); - SAFE_CLOSE (ctx->log_fd); + SAFE_CLOSE (ctx->fp); + SAFE_CLOSE (ctx->log_fp); xfree (ctx->line); xfree (ctx); } @@ -218,25 +248,27 @@ print_log_line (struct wrapper_context_s *ctx, char *line) /* Read data from the log stream. Returns true if the log stream - indicated EOF or error. */ + * indicated EOF or error. */ static int read_log_data (struct wrapper_context_s *ctx) { - int n; + int rc; + size_t n; char line[256]; - /* We must use the npth_read function for pipes, always. */ - do - n = npth_read (ctx->log_fd, line, sizeof line - 1); - while (n < 0 && errno == EINTR); - - if (n <= 0) /* EOF or error. */ + rc = es_read (ctx->log_fp, line, sizeof line - 1, &n); + if (rc || !n) /* Error or EOF. */ { - if (n < 0) - log_error (_("error reading log from ldap wrapper %d: %s\n"), - (int)ctx->pid, strerror (errno)); - print_log_line (ctx, NULL); - SAFE_CLOSE (ctx->log_fd); + if (rc) + { + gpg_error_t err = gpg_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + return 0; + log_error (_("error reading log from ldap wrapper %d: %s\n"), + (int)ctx->pid, gpg_strerror (err)); + } + print_log_line (ctx, NULL); /* Flush. */ + SAFE_CLOSE (ctx->log_fp); return 1; } @@ -251,15 +283,18 @@ read_log_data (struct wrapper_context_s *ctx) /* This function is run by a separate thread to maintain the list of wrappers and to log error messages from these wrappers. */ void * -ldap_wrapper_thread (void *dummy) +ldap_reaper_thread (void *dummy) { - int nfds; + gpg_error_t err; struct wrapper_context_s *ctx; struct wrapper_context_s *ctx_prev; struct timespec abstime; struct timespec curtime; struct timespec timeout; - fd_set fdset; + int millisecs; + gpgrt_poll_t *fparray = NULL; + int fparraysize = 0; + int count, i; int ret; time_t exptime; @@ -272,6 +307,61 @@ ldap_wrapper_thread (void *dummy) { int any_action = 0; + /* Wait until we are needed and then setup the FPARRAY. */ + /* Note: There is one unlock inside the block! */ + lock_reaper_list (); + { + while (!reaper_list && !shutting_down) + { + if (npth_cond_wait (&reaper_run_cond, &reaper_list_mutex)) + log_error ("ldap-reaper: waiting on condition failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + } + + for (count = 0, ctx = reaper_list; ctx; ctx = ctx->next) + if (ctx->log_fp) + count++; + if (count > fparraysize || !fparray) + { + /* Need to realloc the array. We simply discard it and + * replace it by a new one. */ + xfree (fparray); + fparray = xtrycalloc (count? count : 1, sizeof *fparray); + if (!fparray) + { + err = gpg_error_from_syserror (); + log_error ("ldap-reaper can't allocate poll array: %s" + " - waiting 1s\n", gpg_strerror (err)); + /* Note: Here we unlock and continue! */ + unlock_reaper_list (); + npth_sleep (1); + continue; + } + fparraysize = count; + } + for (count = 0, ctx = reaper_list; ctx; ctx = ctx->next) + { + if (ctx->log_fp) + { + log_assert (count < fparraysize); + fparray[count].stream = ctx->log_fp; + fparray[count].want_read = 1; + fparray[count].ignore = 0; + ctx->reaper_idx = count; + count++; + } + else + { + ctx->reaper_idx = -1; + fparray[count].ignore = 1; + } + } + for (i=count; i < fparraysize; i++) + fparray[i].ignore = 1; + } + unlock_reaper_list (); /* Note the one unlock inside the block. */ + + /* Compute the next timeout. */ npth_clock_gettime (&curtime); if (!(npth_timercmp (&curtime, &abstime, <))) { @@ -280,142 +370,166 @@ ldap_wrapper_thread (void *dummy) abstime.tv_sec += TIMERTICK_INTERVAL; } npth_timersub (&abstime, &curtime, &timeout); + millisecs = timeout.tv_sec * 1000; + millisecs += timeout.tv_nsec / 1000000; + if (millisecs < 0) + millisecs = 1; - FD_ZERO (&fdset); - nfds = -1; - for (ctx = wrapper_list; ctx; ctx = ctx->next) + if (DBG_EXTPROG) { - if (ctx->log_fd != -1) - { - FD_SET (ctx->log_fd, &fdset); - if (ctx->log_fd > nfds) - nfds = ctx->log_fd; - } + log_debug ("ldap-reaper: next run (count=%d size=%d, timeout=%d)\n", + count, fparraysize, millisecs); + for (count=0; count < fparraysize; count++) + if (!fparray[count].ignore) + log_debug ("ldap-reaper: fp[%d] stream=%p want=%d\n", + count, fparray[count].stream,fparray[count].want_read); } - nfds++; - /* FIXME: For Windows, we have to use a reader thread on the - pipe that signals an event (and a npth_select_ev variant). */ - ret = npth_pselect (nfds + 1, &fdset, NULL, NULL, &timeout, NULL); - if (ret == -1) + ret = es_poll (fparray, fparraysize, millisecs); + if (ret < 0) { - if (errno != EINTR) - { - log_error (_("npth_select failed: %s - waiting 1s\n"), - strerror (errno)); - npth_sleep (1); - } + err = gpg_error_from_syserror (); + log_error ("ldap-reaper failed to poll: %s" + " - waiting 1s\n", gpg_strerror (err)); + /* In case the reason for the error is a too large array, we + * release it so that it will be allocated smaller in the + * next round. */ + xfree (fparray); + fparray = NULL; + fparraysize = 0; + npth_sleep (1); continue; } + if (DBG_EXTPROG) + { + for (count=0; count < fparraysize; count++) + if (!fparray[count].ignore) + log_debug ("ldap-reaper: fp[%d] stream=%p r=%d %c%c%c%c%c%c%c\n", + count, fparray[count].stream, ret, + fparray[count].got_read? 'r':'-', + fparray[count].got_write?'w':'-', + fparray[count].got_oob? 'o':'-', + fparray[count].got_rdhup?'H':'-', + fparray[count].got_err? 'e':'-', + fparray[count].got_hup? 'h':'-', + fparray[count].got_nval? 'n':'-'); + } + /* All timestamps before exptime should be considered expired. */ exptime = time (NULL); if (exptime > INACTIVITY_TIMEOUT) exptime -= INACTIVITY_TIMEOUT; - /* Note that there is no need to lock the list because we always - add entries at the head (with a pending event status) and - thus traversing the list will even work if we have a context - switch in waitpid (which should anyway only happen with Pth's - hard system call mapping). */ - for (ctx = wrapper_list; ctx; ctx = ctx->next) - { - /* Check whether there is any logging to be done. */ - if (nfds && ctx->log_fd != -1 && FD_ISSET (ctx->log_fd, &fdset)) - { - if (read_log_data (ctx)) - { - SAFE_CLOSE (ctx->log_fd); - any_action = 1; - } - } - - /* Check whether the process is still running. */ - if (ctx->pid != (pid_t)(-1)) - { - gpg_error_t err; - int status; - - err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, - &status); - if (!err) - { - log_info (_("ldap wrapper %d ready"), (int)ctx->pid); - ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); - any_action = 1; - } - else if (gpg_err_code (err) == GPG_ERR_GENERAL) - { - if (status == 10) - log_info (_("ldap wrapper %d ready: timeout\n"), - (int)ctx->pid); - else - log_info (_("ldap wrapper %d ready: exitcode=%d\n"), - (int)ctx->pid, status); - ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); - any_action = 1; - } - else if (gpg_err_code (err) != GPG_ERR_TIMEOUT) - { - log_error (_("waiting for ldap wrapper %d failed: %s\n"), - (int)ctx->pid, gpg_strerror (err)); - any_action = 1; - } - } - - /* Check whether we should terminate the process. */ - if (ctx->pid != (pid_t)(-1) - && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) - { - gnupg_kill_process (ctx->pid); - ctx->stamp = (time_t)(-1); - log_info (_("ldap wrapper %d stalled - killing\n"), - (int)ctx->pid); - /* We need to close the log fd because the cleanup loop - waits for it. */ - SAFE_CLOSE (ctx->log_fd); - any_action = 1; - } - } - - /* If something has been printed to the log file or we got an - EOF from a wrapper, we now print the list of active - wrappers. */ - if (any_action && DBG_LOOKUP) - { - log_info ("ldap worker stati:\n"); - for (ctx = wrapper_list; ctx; ctx = ctx->next) - log_info (" c=%p pid=%d/%d rdr=%p ctrl=%p/%d la=%lu rdy=%d\n", - ctx, - (int)ctx->pid, (int)ctx->printable_pid, - ctx->reader, - ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, - (unsigned long)ctx->stamp, ctx->ready); - } - - - /* Use a separate loop to check whether ready marked wrappers - may be removed. We may only do so if the ksba reader object - is not anymore in use or we are in shutdown state. */ - again: - for (ctx_prev=NULL, ctx=wrapper_list; ctx; ctx_prev=ctx, ctx=ctx->next) - if (ctx->ready - && ((ctx->log_fd == -1 && !ctx->reader) || shutting_down)) + lock_reaper_list (); + { + for (ctx = reaper_list; ctx; ctx = ctx->next) { - if (ctx_prev) - ctx_prev->next = ctx->next; - else - wrapper_list = ctx->next; - destroy_wrapper (ctx); - /* We need to restart because destroy_wrapper might have - done a context switch. */ - goto again; + /* Check whether there is any logging to be done. We need + * to check FPARRAYSIZE because it can be 0 in case + * es_poll returned a timeout. */ + if (fparraysize && ctx->log_fp && ctx->reaper_idx >= 0) + { + log_assert (ctx->reaper_idx < fparraysize); + if (fparray[ctx->reaper_idx].got_read) + { + if (read_log_data (ctx)) + { + SAFE_CLOSE (ctx->log_fp); + any_action = 1; + } + } + } + + /* Check whether the process is still running. */ + if (ctx->pid != (pid_t)(-1)) + { + int status; + + err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, + &status); + if (!err) + { + if (DBG_EXTPROG) + log_info (_("ldap wrapper %d ready"), (int)ctx->pid); + ctx->ready = 1; + gnupg_release_process (ctx->pid); + ctx->pid = (pid_t)(-1); + any_action = 1; + } + else if (gpg_err_code (err) == GPG_ERR_GENERAL) + { + if (status == 10) + log_info (_("ldap wrapper %d ready: timeout\n"), + (int)ctx->pid); + else + log_info (_("ldap wrapper %d ready: exitcode=%d\n"), + (int)ctx->pid, status); + ctx->ready = 1; + gnupg_release_process (ctx->pid); + ctx->pid = (pid_t)(-1); + any_action = 1; + } + else if (gpg_err_code (err) != GPG_ERR_TIMEOUT) + { + log_error (_("waiting for ldap wrapper %d failed: %s\n"), + (int)ctx->pid, gpg_strerror (err)); + any_action = 1; + } + } + + /* Check whether we should terminate the process. */ + if (ctx->pid != (pid_t)(-1) + && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) + { + gnupg_kill_process (ctx->pid); + ctx->stamp = (time_t)(-1); + log_info (_("ldap wrapper %d stalled - killing\n"), + (int)ctx->pid); + /* We need to close the log stream because the cleanup + * loop waits for it. */ + SAFE_CLOSE (ctx->log_fp); + any_action = 1; + } } + + /* If something has been printed to the log file or we got an + * EOF from a wrapper, we now print the list of active + * wrappers. */ + if (any_action && DBG_EXTPROG) + { + log_debug ("ldap worker stati:\n"); + for (ctx = reaper_list; ctx; ctx = ctx->next) + log_debug (" c=%p pid=%d/%d rdr=%p logfp=%p" + " ctrl=%p/%d la=%lu rdy=%d\n", + ctx, + (int)ctx->pid, (int)ctx->printable_pid, + ctx->reader, ctx->log_fp, + ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, + (unsigned long)ctx->stamp, ctx->ready); + } + + /* An extra loop to check whether ready marked wrappers may be + * removed. We may only do so if the ksba reader object is + * not anymore in use or we are in shutdown state. */ + again: + for (ctx_prev=NULL, ctx=reaper_list; ctx; ctx_prev=ctx, ctx=ctx->next) + { + if (ctx->ready + && ((!ctx->log_fp && !ctx->reader) || shutting_down)) + { + if (ctx_prev) + ctx_prev->next = ctx->next; + else + reaper_list = ctx->next; + destroy_wrapper (ctx); + goto again; + } + } + } + unlock_reaper_list (); } + /*NOTREACHED*/ return NULL; /* Make the compiler happy. */ } @@ -424,7 +538,7 @@ ldap_wrapper_thread (void *dummy) /* Start the reaper thread for the ldap wrapper. */ void -ldap_wrapper_launch_thread (void) +ldap_reaper_launch_thread (void) { static int done; npth_attr_t tattr; @@ -435,14 +549,21 @@ ldap_wrapper_launch_thread (void) return; done = 1; +#ifdef HAVE_W32_SYSTEM + /* Static init does not yet work in W32 nPth. */ + if (npth_cond_init (&reaper_run_cond, NULL)) + log_fatal ("%s: failed to init condition variabale: %s\n", + __func__, gpg_strerror (gpg_error_from_syserror ())); +#endif + npth_attr_init (&tattr); npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); - err = npth_create (&thread, &tattr, ldap_wrapper_thread, NULL); - if (err) + if (npth_create (&thread, &tattr, ldap_reaper_thread, NULL)) { - log_error (_("error spawning ldap wrapper reaper thread: %s\n"), - strerror (err) ); + err = gpg_error_from_syserror (); + log_error ("error spawning ldap reaper reaper thread: %s\n", + gpg_strerror (err) ); dirmngr_exit (1); } npth_setname_np (thread, "ldap-reaper"); @@ -451,16 +572,20 @@ ldap_wrapper_launch_thread (void) - - /* Wait until all ldap wrappers have terminated. We assume that the kill has already been sent to all of them. */ void ldap_wrapper_wait_connections () { - shutting_down = 1; - /* FIXME: This is a busy wait. */ - while (wrapper_list) + lock_reaper_list (); + { + shutting_down = 1; + if (npth_cond_signal (&reaper_run_cond)) + log_error ("%s: Ooops: signaling condition failed: %s\n", + __func__, gpg_strerror (gpg_error_from_syserror ())); + } + unlock_reaper_list (); + while (reaper_list) npth_usleep (200); } @@ -475,30 +600,35 @@ ldap_wrapper_release_context (ksba_reader_t reader) if (!reader ) return; - for (ctx=wrapper_list; ctx; ctx=ctx->next) - if (ctx->reader == reader) - { - if (DBG_LOOKUP) - log_info ("releasing ldap worker c=%p pid=%d/%d rdr=%p ctrl=%p/%d\n", - ctx, - (int)ctx->pid, (int)ctx->printable_pid, - ctx->reader, - ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); + lock_reaper_list (); + { + for (ctx=reaper_list; ctx; ctx=ctx->next) + if (ctx->reader == reader) + { + if (DBG_EXTPROG) + log_debug ("releasing ldap worker c=%p pid=%d/%d rdr=%p" + " ctrl=%p/%d\n", ctx, + (int)ctx->pid, (int)ctx->printable_pid, + ctx->reader, + ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); - ctx->reader = NULL; - SAFE_CLOSE (ctx->fd); - if (ctx->ctrl) - { - ctx->ctrl->refcount--; - ctx->ctrl = NULL; - } - if (ctx->fd_error) - log_info (_("reading from ldap wrapper %d failed: %s\n"), - ctx->printable_pid, gpg_strerror (ctx->fd_error)); - break; - } + ctx->reader = NULL; + SAFE_CLOSE (ctx->fp); + if (ctx->ctrl) + { + ctx->ctrl->refcount--; + ctx->ctrl = NULL; + } + if (ctx->fp_err) + log_info ("%s: reading from ldap wrapper %d failed: %s\n", + __func__, ctx->printable_pid, gpg_strerror (ctx->fp_err)); + break; + } + } + unlock_reaper_list (); } + /* Cleanup all resources held by the connection associated with CTRL. This is used after a cancel to kill running wrappers. */ void @@ -506,41 +636,45 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl) { struct wrapper_context_s *ctx; - for (ctx=wrapper_list; ctx; ctx=ctx->next) - if (ctx->ctrl && ctx->ctrl == ctrl) - { - ctx->ctrl->refcount--; - ctx->ctrl = NULL; - if (ctx->pid != (pid_t)(-1)) - gnupg_kill_process (ctx->pid); - if (ctx->fd_error) - log_info (_("reading from ldap wrapper %d failed: %s\n"), - ctx->printable_pid, gpg_strerror (ctx->fd_error)); - } + lock_reaper_list (); + { + for (ctx=reaper_list; ctx; ctx=ctx->next) + if (ctx->ctrl && ctx->ctrl == ctrl) + { + ctx->ctrl->refcount--; + ctx->ctrl = NULL; + if (ctx->pid != (pid_t)(-1)) + gnupg_kill_process (ctx->pid); + if (ctx->fp_err) + log_info ("%s: reading from ldap wrapper %d failed: %s\n", + __func__, ctx->printable_pid, gpg_strerror (ctx->fp_err)); + } + } + unlock_reaper_list (); } /* This is the callback used by the ldap wrapper to feed the ksba - reader with the wrappers stdout. See the description of - ksba_reader_set_cb for details. */ + * reader with the wrapper's stdout. See the description of + * ksba_reader_set_cb for details. */ static int reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread) { struct wrapper_context_s *ctx = cb_value; size_t nleft = count; - int nfds; struct timespec abstime; struct timespec curtime; struct timespec timeout; - int saved_errno; - fd_set fdset, read_fdset; + int millisecs; + gpgrt_poll_t fparray[1]; int ret; + gpg_error_t err; + /* FIXME: We might want to add some internal buffering because the ksba code does not do any buffering for itself (because a ksba reader may be detached from another stream to read other data and - the it would be cumbersome to get back already buffered - stuff). */ + then it would be cumbersome to get back already buffered stuff). */ if (!buffer && !count && !nread) return -1; /* Rewind is not supported. */ @@ -548,81 +682,108 @@ reader_callback (void *cb_value, char *buffer, size_t count, size_t *nread) /* If we ever encountered a read error, don't continue (we don't want to possibly overwrite the last error cause). Bail out also if the file descriptor has been closed. */ - if (ctx->fd_error || ctx->fd == -1) + if (ctx->fp_err || !ctx->fp) { *nread = 0; return -1; } - FD_ZERO (&fdset); - FD_SET (ctx->fd, &fdset); - nfds = ctx->fd + 1; + memset (fparray, 0, sizeof fparray); + fparray[0].stream = ctx->fp; + fparray[0].want_read = 1; npth_clock_gettime (&abstime); abstime.tv_sec += TIMERTICK_INTERVAL; while (nleft > 0) { - int n; - gpg_error_t err; - npth_clock_gettime (&curtime); if (!(npth_timercmp (&curtime, &abstime, <))) { err = dirmngr_tick (ctx->ctrl); if (err) { - ctx->fd_error = err; - SAFE_CLOSE (ctx->fd); + ctx->fp_err = err; + SAFE_CLOSE (ctx->fp); return -1; } npth_clock_gettime (&abstime); abstime.tv_sec += TIMERTICK_INTERVAL; } npth_timersub (&abstime, &curtime, &timeout); + millisecs = timeout.tv_sec * 1000; + millisecs += timeout.tv_nsec / 1000000; + if (millisecs < 0) + millisecs = 1; - read_fdset = fdset; - ret = npth_pselect (nfds, &read_fdset, NULL, NULL, &timeout, NULL); - saved_errno = errno; + if (DBG_EXTPROG) + { + log_debug ("%s: fp[0] stream=%p want=%d\n", + __func__, fparray[0].stream,fparray[0].want_read); + } - if (ret == -1 && saved_errno != EINTR) + ret = es_poll (fparray, DIM (fparray), millisecs); + if (ret < 0) { - ctx->fd_error = gpg_error_from_errno (errno); - SAFE_CLOSE (ctx->fd); + ctx->fp_err = gpg_error_from_syserror (); + log_error ("error polling stdout of ldap wrapper %d: %s\n", + ctx->printable_pid, gpg_strerror (ctx->fp_err)); + SAFE_CLOSE (ctx->fp); return -1; } - if (ret <= 0) - /* Timeout. Will be handled when calculating the next timeout. */ - continue; + if (DBG_EXTPROG) + { + log_debug ("%s: fp[0] stream=%p r=%d %c%c%c%c%c%c%c\n", + __func__, fparray[0].stream, ret, + fparray[0].got_read? 'r':'-', + fparray[0].got_write?'w':'-', + fparray[0].got_oob? 'o':'-', + fparray[0].got_rdhup?'H':'-', + fparray[0].got_err? 'e':'-', + fparray[0].got_hup? 'h':'-', + fparray[0].got_nval? 'n':'-'); + } + if (!ret) + { + /* Timeout. Will be handled when calculating the next timeout. */ + continue; + } - /* This should not block now that select returned with a file - descriptor. So it shouldn't be necessary to use npth_read - (and it is slightly dangerous in the sense that a concurrent - thread might (accidentially?) change the status of ctx->fd - before we read. FIXME: Set ctx->fd to nonblocking? */ - n = read (ctx->fd, buffer, nleft); - if (n < 0) + if (fparray[0].got_read) { - ctx->fd_error = gpg_error_from_errno (errno); - SAFE_CLOSE (ctx->fd); - return -1; + size_t n; + + if (es_read (ctx->fp, buffer, nleft, &n)) + { + ctx->fp_err = gpg_error_from_syserror (); + if (gpg_err_code (ctx->fp_err) == GPG_ERR_EAGAIN) + ctx->fp_err = 0; + else + { + log_error ("%s: error reading: %s (%d)\n", + __func__, gpg_strerror (ctx->fp_err), ctx->fp_err); + SAFE_CLOSE (ctx->fp); + return -1; + } + } + else if (!n) /* EOF */ + { + if (nleft == count) + return -1; /* EOF. */ + break; + } + nleft -= n; + buffer += n; + if (n > 0 && ctx->stamp != (time_t)(-1)) + ctx->stamp = time (NULL); } - else if (!n) - { - if (nleft == count) - return -1; /* EOF. */ - break; - } - nleft -= n; - buffer += n; - if (n > 0 && ctx->stamp != (time_t)(-1)) - ctx->stamp = time (NULL); } *nread = count - nleft; return 0; } + /* Fork and exec the LDAP wrapper and return a new libksba reader object at READER. ARGV is a NULL terminated list of arguments for the wrapper. The function returns 0 on success or an error code. @@ -646,7 +807,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) int j; const char **arg_list; const char *pgmname; - int outpipe[2], errpipe[2]; + estream_t outfp, errfp; /* It would be too simple to connect stderr just to our logging stream. The problem is that if we are running multi-threaded @@ -656,7 +817,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) wrapper module to do the logging on its own. Given that we anyway need a way to reap the child process and this is best done using a general reaping thread, that thread can do the logging too. */ - ldap_wrapper_launch_thread (); + ldap_reaper_launch_thread (); *reader = NULL; @@ -696,41 +857,21 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) return err; } - err = gnupg_create_inbound_pipe (outpipe, NULL, 0); - if (!err) - { - err = gnupg_create_inbound_pipe (errpipe, NULL, 0); - if (err) - { - close (outpipe[0]); - close (outpipe[1]); - } - } - if (err) - { - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - xfree (arg_list); - xfree (ctx); - return err; - } - - err = gnupg_spawn_process_fd (pgmname, arg_list, - -1, outpipe[1], errpipe[1], &pid); + err = gnupg_spawn_process (pgmname, arg_list, + NULL, NULL, GNUPG_SPAWN_NONBLOCK, + NULL, &outfp, &errfp, &pid); xfree (arg_list); - close (outpipe[1]); - close (errpipe[1]); if (err) { - close (outpipe[0]); - close (errpipe[0]); xfree (ctx); + log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); return err; } ctx->pid = pid; ctx->printable_pid = (int) pid; - ctx->fd = outpipe[0]; - ctx->log_fd = errpipe[0]; + ctx->fp = outfp; + ctx->log_fp = errfp; ctx->ctrl = ctrl; ctrl->refcount++; ctx->stamp = time (NULL); @@ -749,12 +890,20 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) } /* Hook the context into our list of running wrappers. */ - ctx->reader = *reader; - ctx->next = wrapper_list; - wrapper_list = ctx; - if (opt.verbose) - log_info ("ldap wrapper %d started (reader %p)\n", - (int)ctx->pid, ctx->reader); + lock_reaper_list (); + { + ctx->reader = *reader; + ctx->next = reaper_list; + reaper_list = ctx; + if (npth_cond_signal (&reaper_run_cond)) + log_error ("ldap-wrapper: Ooops: signaling condition failed: %s (%d)\n", + gpg_strerror (gpg_error_from_syserror ()), errno); + } + unlock_reaper_list (); + + if (DBG_EXTPROG) + log_debug ("ldap wrapper %d started (%p, %s)\n", + (int)ctx->pid, ctx->reader, pgmname); /* Need to wait for the first byte so we are able to detect an empty output and not let the consumer see an EOF without further error diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index adf83071e..cb3c0b763 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -136,8 +136,12 @@ run_ldap_wrapper (ctrl_t ctrl, argv[argc++] = "--pass"; argv[argc++] = pass; } - if (opt.verbose) + + if (DBG_LOOKUP) argv[argc++] = "-vv"; + else if (DBG_EXTPROG) + argv[argc++] = "-v"; + argv[argc++] = "--log-with-pid"; if (multi_mode) argv[argc++] = "--multi"; @@ -564,8 +568,12 @@ start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, argv[argc++] = "--pass"; argv[argc++] = pass; } - if (opt.verbose) + + if (DBG_LOOKUP) argv[argc++] = "-vv"; + else if (DBG_EXTPROG) + argv[argc++] = "-v"; + argv[argc++] = "--log-with-pid"; argv[argc++] = "--multi"; if (opt.ldaptimeout) diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c index bc004665d..fb883722a 100644 --- a/dirmngr/loadswdb.c +++ b/dirmngr/loadswdb.c @@ -126,7 +126,7 @@ fetch_file (ctrl_t ctrl, const char *url, estream_t *r_fp) size_t nread, nwritten; char buffer[1024]; - if ((err = ks_http_fetch (ctrl, url, &httpfp))) + if ((err = ks_http_fetch (ctrl, url, KS_HTTP_FETCH_NOCACHE, &httpfp))) goto leave; /* We now read the data from the web server into a memory buffer. diff --git a/dirmngr/server.c b/dirmngr/server.c index 60d980211..b7cdb24c9 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -1105,7 +1105,7 @@ cmd_ldapserver (assuan_context_t ctx, char *line) static const char hlp_isvalid[] = "ISVALID [--only-ocsp] [--force-default-responder]" - " |\n" + " []\n" "\n" "This command checks whether the certificate identified by the\n" "certificate_id is valid. This is done by consulting CRLs or\n" @@ -1117,8 +1117,9 @@ static const char hlp_isvalid[] = "delimited by a single dot. The first part is the SHA-1 hash of the\n" "issuer name and the second part the serial number.\n" "\n" - "Alternatively the certificate's fingerprint may be given in which\n" - "case an OCSP request is done before consulting the CRL.\n" + "If an OCSP check is desired CERTIFICATE_FPR with the hex encoded\n" + "fingerprint of the certificate is required. In this case an OCSP\n" + "request is done before consulting the CRL.\n" "\n" "If the option --only-ocsp is given, no fallback to a CRL check will\n" "be used.\n" @@ -1130,7 +1131,7 @@ static gpg_error_t cmd_isvalid (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - char *issuerhash, *serialno; + char *issuerhash, *serialno, *fpr; gpg_error_t err; int did_inquire = 0; int ocsp_mode = 0; @@ -1141,25 +1142,36 @@ cmd_isvalid (assuan_context_t ctx, char *line) force_default_responder = has_option (line, "--force-default-responder"); line = skip_options (line); - issuerhash = xstrdup (line); /* We need to work on a copy of the - line because that same Assuan - context may be used for an inquiry. - That is because Assuan reuses its - line buffer. - */ + /* We need to work on a copy of the line because that same Assuan + * context may be used for an inquiry. That is because Assuan + * reuses its line buffer. */ + issuerhash = xstrdup (line); serialno = strchr (issuerhash, '.'); - if (serialno) - *serialno++ = 0; - else + if (!serialno) { - char *endp = strchr (issuerhash, ' '); + xfree (issuerhash); + return leave_cmd (ctx, PARM_ERROR (_("serialno missing in cert ID"))); + } + *serialno++ = 0; + if (strlen (issuerhash) != 40) + { + xfree (issuerhash); + return leave_cmd (ctx, PARM_ERROR ("cert ID is too short")); + } + + fpr = strchr (serialno, ' '); + while (fpr && spacep (fpr)) + fpr++; + if (fpr && *fpr) + { + char *endp = strchr (fpr, ' '); if (endp) *endp = 0; - if (strlen (issuerhash) != 40) + if (strlen (fpr) != 40) { xfree (issuerhash); - return leave_cmd (ctx, PARM_ERROR (_("serialno missing in cert ID"))); + return leave_cmd (ctx, PARM_ERROR ("fingerprint too short")); } ocsp_mode = 1; } @@ -1168,17 +1180,24 @@ cmd_isvalid (assuan_context_t ctx, char *line) again: if (ocsp_mode) { - /* Note, that we ignore the given issuer hash and instead rely - on the current certificate semantics used with this - command. */ + /* Note, that we currently ignore the supplied fingerprint FPR; + * instead ocsp_isvalid does an inquire to ask for the cert. + * The fingerprint may eventually be used to lookup the + * certificate in a local cache. */ if (!opt.allow_ocsp) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else err = ocsp_isvalid (ctrl, NULL, NULL, force_default_responder); - /* Fixme: If we got no ocsp response and --only-ocsp is not used - we should fall back to CRL mode. Thus we need to clear - OCSP_MODE, get the issuerhash and the serialno from the - current certificate and jump to again. */ + + if (gpg_err_code (err) == GPG_ERR_CONFIGURATION + && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR) + { + /* No default responder configured - fallback to CRL. */ + if (!only_ocsp) + log_info ("falling back to CRL check\n"); + ocsp_mode = 0; + goto again; + } } else if (only_ocsp) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); @@ -1858,7 +1877,7 @@ static const char hlp_validate[] = " INQUIRE CERTLIST\n" "\n" "Here the first certificate is the target certificate, the remaining\n" - "certificates are suggested intermediary certificates. All certifciates\n" + "certificates are suggested intermediary certificates. All certificates\n" "need to be PEM encoded.\n" "\n" "The option --systrust changes the behaviour to include the system\n" @@ -1909,7 +1928,7 @@ cmd_validate (assuan_context_t ctx, char *line) err = gpg_error (GPG_ERR_MISSING_CERT); if (!err) { - /* Extraxt the first certificate from the list. */ + /* Extract the first certificate from the list. */ cert = certlist->cert; ksba_cert_ref (cert); } @@ -1978,6 +1997,38 @@ make_keyserver_item (const char *uri, uri_item_t *r_item) uri_item_t item; *r_item = NULL; + + /* We used to have DNS CNAME redirection from the URLs below to + * sks-keyserver. pools. The idea was to allow for a quick way to + * switch to a different set of pools. The problem with that + * approach is that TLS needs to verify the hostname and - because + * DNS is not secured - it can only check the user supplied hostname + * and not a hostname from a CNAME RR. Thus the final server all + * need to have certificates with the actual pool name as well as + * for keys.gnupg.net - that would render the advantage of + * keys.gnupg.net useless and so we better give up on this. Because + * the keys.gnupg.net URL are still in widespread use we do a static + * mapping here. + */ + if (!strcmp (uri, "hkps://keys.gnupg.net") + || !strcmp (uri, "keys.gnupg.net")) + uri = "hkps://hkps.pool.sks-keyservers.net"; + else if (!strcmp (uri, "https://keys.gnupg.net")) + uri = "https://hkps.pool.sks-keyservers.net"; + else if (!strcmp (uri, "hkp://keys.gnupg.net")) + uri = "hkp://hkps.pool.sks-keyservers.net"; + else if (!strcmp (uri, "http://keys.gnupg.net")) + uri = "http://hkps.pool.sks-keyservers.net"; + else if (!strcmp (uri, "hkps://http-keys.gnupg.net") + || !strcmp (uri, "http-keys.gnupg.net")) + uri = "hkps://ha.pool.sks-keyservers.net"; + else if (!strcmp (uri, "https://http-keys.gnupg.net")) + uri = "https://ha.pool.sks-keyservers.net"; + else if (!strcmp (uri, "hkp://http-keys.gnupg.net")) + uri = "hkp://ha.pool.sks-keyservers.net"; + else if (!strcmp (uri, "http://http-keys.gnupg.net")) + uri = "http://ha.pool.sks-keyservers.net"; + item = xtrymalloc (sizeof *item + strlen (uri)); if (!item) return gpg_error_from_syserror (); @@ -2489,7 +2540,8 @@ static const char hlp_getinfo[] = "dnsinfo - Return info about the DNS resolver\n" "socket_name - Return the name of the socket.\n" "session_id - Return the current session_id.\n" - "workqueue - Inspect the work queue\n"; + "workqueue - Inspect the work queue\n" + "getenv NAME - Return value of envvar NAME\n"; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { @@ -2557,6 +2609,23 @@ cmd_getinfo (assuan_context_t ctx, char *line) workqueue_dump_queue (ctrl); err = 0; } + else if (!strncmp (line, "getenv", 6) + && (line[6] == ' ' || line[6] == '\t' || !line[6])) + { + line += 6; + while (*line == ' ' || *line == '\t') + line++; + if (!*line) + err = gpg_error (GPG_ERR_MISSING_VALUE); + else + { + const char *s = getenv (line); + if (!s) + err = set_error (GPG_ERR_NOT_FOUND, "No such envvar"); + else + err = assuan_send_data (ctx, s, strlen (s)); + } + } else err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); diff --git a/doc/DETAILS b/doc/DETAILS index a4063b4a6..52051ed2c 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -105,6 +105,19 @@ described here. certificate (i.e. for the trust anchor) and an 'f' for all other valid certificates. + In "sig" records, this field may have one of these values as first + character: + + - ! :: Signature is good. + - - :: Signature is bad. + - ? :: No public key to verify signature or public key is not usable. + - % :: Other error verifying a signature + + More values may be added later. The field may also be empty if + gpg has been invoked in a non-checking mode (--list-sigs) or in a + fast checking mode. Since 2.2.7 '?' will also be printed by the + command --list-sigs if the key is not in the local keyring. + *** Field 3 - Key length The length of key in bits. @@ -195,9 +208,11 @@ described here. gpg's --edit-key menu does. For "sig" records, this is the fingerprint of the key that issued - the signature. Note that this is only filled in if the signature + the signature. Note that this may only be filled if the signature verified correctly. Note also that for various technical reasons, this fingerprint is only available if --no-sig-cache is used. + Since 2.2.7 this field will also be set if the key is missing but + the signature carries an issuer fingerprint as meta data. *** Field 14 - Flag field @@ -437,14 +452,17 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: available. This is the case with CMS and might eventually also be available for OpenPGP. -*** ERRSIG