1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

Merge branch 'STABLE-BRANCH-2-2' into master

--

Resolved Conflicts:
	NEWS  - removed
	configure.ac - removed

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-05-13 13:29:40 +02:00
commit 7b7576637d
No known key found for this signature in database
GPG key ID: E3FDFF218E45B72B
73 changed files with 3270 additions and 3583 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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))

View file

@ -29,7 +29,6 @@
# include <signal.h>
#endif
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <unistd.h>
#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
}
}

View file

@ -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;

View file

@ -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))

View file

@ -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));

View file

@ -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);

View file

@ -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 ();
}

View file

@ -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;

View file

@ -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 --*/

View file

@ -45,6 +45,7 @@
#ifdef USE_LDAPWRAPPER
# error This module is not expected to be build.
#endif
#error This module might not anymore work.

View file

@ -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 <config.h>
@ -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

View file

@ -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)

View file

@ -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.

View file

@ -1105,7 +1105,7 @@ cmd_ldapserver (assuan_context_t ctx, char *line)
static const char hlp_isvalid[] =
"ISVALID [--only-ocsp] [--force-default-responder]"
" <certificate_id>|<certificate_fpr>\n"
" <certificate_id> [<certificate_fpr>]\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");