diff options
Diffstat (limited to 'dirmngr')
-rw-r--r-- | dirmngr/Makefile.am | 1 | ||||
-rw-r--r-- | dirmngr/certcache.c | 27 | ||||
-rw-r--r-- | dirmngr/certcache.h | 2 | ||||
-rw-r--r-- | dirmngr/crlcache.c | 48 | ||||
-rw-r--r-- | dirmngr/crlcache.h | 9 | ||||
-rw-r--r-- | dirmngr/crlfetch.c | 3 | ||||
-rw-r--r-- | dirmngr/dirmngr.c | 20 | ||||
-rw-r--r-- | dirmngr/dirmngr.h | 11 | ||||
-rw-r--r-- | dirmngr/dirmngr_ldap.c | 2 | ||||
-rw-r--r-- | dirmngr/domaininfo.c | 13 | ||||
-rw-r--r-- | dirmngr/fakecrl.c | 63 | ||||
-rw-r--r-- | dirmngr/ks-action.c | 151 | ||||
-rw-r--r-- | dirmngr/ks-action.h | 7 | ||||
-rw-r--r-- | dirmngr/ks-engine-ldap.c | 643 | ||||
-rw-r--r-- | dirmngr/ks-engine.h | 8 | ||||
-rw-r--r-- | dirmngr/ldap-misc.c | 87 | ||||
-rw-r--r-- | dirmngr/ldap-misc.h | 2 | ||||
-rw-r--r-- | dirmngr/ldap.c | 2 | ||||
-rw-r--r-- | dirmngr/server.c | 245 | ||||
-rw-r--r-- | dirmngr/validate.c | 16 | ||||
-rw-r--r-- | dirmngr/workqueue.c | 6 |
21 files changed, 1148 insertions, 218 deletions
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am index 1c8065dbb..feee2f5c8 100644 --- a/dirmngr/Makefile.am +++ b/dirmngr/Makefile.am @@ -58,6 +58,7 @@ endif noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \ + fakecrl.c \ certcache.c certcache.h \ domaininfo.c \ workqueue.c \ diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index 1e73d6f85..6b194f31c 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -768,7 +768,7 @@ cert_cache_init (strlist_t hkp_cacerts) initialization_done = 1; release_cache_lock (); - cert_cache_print_stats (); + cert_cache_print_stats (NULL); } /* Deinitialize the certificate cache. With FULL set to true even the @@ -811,7 +811,7 @@ cert_cache_deinit (int full) /* Print some statistics to the log file. */ void -cert_cache_print_stats (void) +cert_cache_print_stats (ctrl_t ctrl) { cert_item_t ci; int idx; @@ -848,16 +848,19 @@ cert_cache_print_stats (void) release_cache_lock (); - log_info (_("permanently loaded certificates: %u\n"), - n_permanent); - log_info (_(" runtime cached certificates: %u\n"), - n_nonperm); - log_info (_(" trusted certificates: %u (%u,%u,%u,%u)\n"), - n_trusted, - n_trustclass_system, - n_trustclass_config, - n_trustclass_hkp, - n_trustclass_hkpspool); + dirmngr_status_helpf (ctrl, + _("permanently loaded certificates: %u\n"), + n_permanent); + dirmngr_status_helpf (ctrl, + _(" runtime cached certificates: %u\n"), + n_nonperm); + dirmngr_status_helpf (ctrl, + _(" trusted certificates: %u (%u,%u,%u,%u)\n"), + n_trusted, + n_trustclass_system, + n_trustclass_config, + n_trustclass_hkp, + n_trustclass_hkpspool); } diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h index 8d645836d..3a773636f 100644 --- a/dirmngr/certcache.h +++ b/dirmngr/certcache.h @@ -37,7 +37,7 @@ void cert_cache_init (strlist_t hkp_cacerts); void cert_cache_deinit (int full); /* Print some statistics to the log file. */ -void cert_cache_print_stats (void); +void cert_cache_print_stats (ctrl_t ctrl); /* Return true if any cert of a class in MASK is permanently loaded. */ int cert_cache_any_in_class (unsigned int mask); diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index befc6b94b..9f0b910f3 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -125,6 +125,13 @@ # define O_BINARY 0 #endif + +/* Reason flags for an invalid CRL. */ +#define INVCRL_TOO_OLD 1 +#define INVCRL_UNKNOWN_EXTN 2 +#define INVCRL_GENERAL 127 + + static const char oidstr_crlNumber[] = "2.5.29.20"; /* static const char oidstr_issuingDistributionPoint[] = "2.5.29.28"; */ static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; @@ -157,7 +164,7 @@ struct crl_cache_entry_s unsigned int cdb_use_count; /* Current use count. */ unsigned int cdb_lru_count; /* Used for LRU purposes. */ int dbfile_checked; /* Set to true if the dbfile_hash value has - been checked one. */ + been checked once. */ }; @@ -569,8 +576,8 @@ open_dir (crl_cache_t *r_cache) if (*line == 'i') { entry->invalid = atoi (line+1); - if (entry->invalid < 1) - entry->invalid = 1; + if (!entry->invalid) + entry->invalid = INVCRL_GENERAL; } else if (*line == 'u') entry->user_trust_req = 1; @@ -1395,7 +1402,7 @@ cache_isvalid (ctrl_t ctrl, const char *issuer_hash, { if (opt.verbose) log_info ("no system trust and client does not trust either\n"); - retval = CRL_CACHE_CANTUSE; + retval = CRL_CACHE_NOTTRUSTED; } else { @@ -1515,8 +1522,11 @@ crl_cache_cert_isvalid (ctrl_t ctrl, ksba_cert_t cert, case CRL_CACHE_DONTKNOW: err = gpg_error (GPG_ERR_NO_CRL_KNOWN); break; + case CRL_CACHE_NOTTRUSTED: + err = gpg_error (GPG_ERR_NOT_TRUSTED); + break; case CRL_CACHE_CANTUSE: - err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + err = gpg_error (GPG_ERR_INV_CRL_OBJ); break; default: log_fatal ("cache_isvalid returned invalid status code %d\n", result); @@ -2097,7 +2107,7 @@ crl_parse_insert (ctrl_t ctrl, ksba_crl_t crl, } } while (stopreason != KSBA_SR_READY); - assert (!err); + log_assert (!err); failure: @@ -2338,7 +2348,7 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) nextupdate); if (!err2) err2 = gpg_error (GPG_ERR_CRL_TOO_OLD); - invalidate_crl |= 1; + invalidate_crl |= INVCRL_TOO_OLD; } } @@ -2353,7 +2363,7 @@ crl_cache_insert (ctrl_t ctrl, const char *url, ksba_reader_t reader) log_error (_("unknown critical CRL extension %s\n"), oid); if (!err2) err2 = gpg_error (GPG_ERR_INV_CRL); - invalidate_crl |= 2; + invalidate_crl |= INVCRL_UNKNOWN_EXTN; } if (gpg_err_code (err) == GPG_ERR_EOF || gpg_err_code (err) == GPG_ERR_NO_DATA ) @@ -2492,6 +2502,7 @@ list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp) int rc; int warn = 0; const unsigned char *s; + unsigned int invalid; es_fputs ("--------------------------------------------------------\n", fp ); es_fprintf (fp, _("Begin CRL dump (retrieved via %s)\n"), e->url ); @@ -2516,13 +2527,20 @@ list_one_crl_entry (crl_cache_t cache, crl_cache_entry_t e, estream_t fp) !e->user_trust_req? "[system]" : e->check_trust_anchor? e->check_trust_anchor:"[missing]"); - if ((e->invalid & 1)) - es_fprintf (fp, _(" ERROR: The CRL will not be used " - "because it was still too old after an update!\n")); - if ((e->invalid & 2)) - es_fprintf (fp, _(" ERROR: The CRL will not be used " + invalid = e->invalid; + if ((invalid & INVCRL_TOO_OLD)) + { + invalid &= ~INVCRL_TOO_OLD; + es_fprintf (fp, _(" ERROR: The CRL will not be used " + "because it was still too old after an update!\n")); + } + if ((invalid & INVCRL_UNKNOWN_EXTN)) + { + invalid &= ~INVCRL_UNKNOWN_EXTN; + es_fprintf (fp, _(" ERROR: The CRL will not be used " "due to an unknown critical extension!\n")); - if ((e->invalid & ~3)) + } + if (invalid) /* INVCRL_GENERAL or some other bits are set. */ es_fprintf (fp, _(" ERROR: The CRL will not be used\n")); cdb = lock_db_file (cache, e); @@ -2714,8 +2732,6 @@ crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert) any_dist_point = 1; - if (opt.verbose) - log_info ("fetching CRL from '%s'\n", distpoint_uri); crl_close_reader (reader); err = crl_fetch (ctrl, distpoint_uri, &reader); if (err) diff --git a/dirmngr/crlcache.h b/dirmngr/crlcache.h index 0e60def8f..375943462 100644 --- a/dirmngr/crlcache.h +++ b/dirmngr/crlcache.h @@ -27,6 +27,7 @@ typedef enum CRL_CACHE_VALID = 0, CRL_CACHE_INVALID, CRL_CACHE_DONTKNOW, + CRL_CACHE_NOTTRUSTED, CRL_CACHE_CANTUSE } crl_cache_result_t; @@ -44,6 +45,7 @@ crl_sig_result_t; struct crl_cache_entry_s; typedef struct crl_cache_entry_s *crl_cache_entry_t; +/*-- crlcache.c --*/ void crl_cache_init (void); void crl_cache_deinit (void); @@ -67,4 +69,11 @@ gpg_error_t crl_cache_load (ctrl_t ctrl, const char *filename); gpg_error_t crl_cache_reload_crl (ctrl_t ctrl, ksba_cert_t cert); +/*-- fakecrl.c --*/ +crl_cache_result_t fakecrl_isvalid (ctrl_t ctrl, + const char *issuer_hash, + const char *cert_id); + + + #endif /* CRLCACHE_H */ diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c index a591a2b5a..5b6b648e2 100644 --- a/dirmngr/crlfetch.c +++ b/dirmngr/crlfetch.c @@ -175,6 +175,9 @@ crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader) if (!url) return gpg_error (GPG_ERR_INV_ARG); + if (opt.verbose) + log_info ("fetching CRL from '%s'\n", url); + err = http_parse_uri (&uri, url, 0); http_release_parsed_uri (uri); if (!err) /* Yes, our HTTP code groks that. */ diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index cbc693bd8..bb54f4edd 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -158,6 +158,7 @@ enum cmd_and_opt_values { oConnectTimeout, oConnectQuickTimeout, oListenBacklog, + oFakeCRL, aTest }; @@ -274,7 +275,7 @@ static gpgrt_opt_t opts[] = { " points to serverlist")), ARGPARSE_s_i (oLDAPTimeout, "ldaptimeout", N_("|N|set LDAP timeout to N seconds")), - + ARGPARSE_s_s (oFakeCRL, "fake-crl", "@"), ARGPARSE_header ("OCSP", N_("Configuration for OCSP")), @@ -324,6 +325,7 @@ static struct debug_flags_s debug_flags [] = { DBG_NETWORK_VALUE, "network" }, { DBG_LOOKUP_VALUE , "lookup" }, { DBG_EXTPROG_VALUE, "extprog" }, + { DBG_KEEPTMP_VALUE, "keeptmp" }, { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; @@ -534,7 +536,7 @@ set_debug (void) select the highest debug value and would then clutter their disk with debug files which may reveal confidential data. */ if (numok) - opt.debug &= ~(DBG_HASHING_VALUE); + opt.debug &= ~(DBG_HASHING_VALUE|DBG_KEEPTMP_VALUE); } else { @@ -708,6 +710,8 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT; ldapserver_list_needs_reset = 1; opt.debug_cache_expired_certs = 0; + xfree (opt.fake_crl); + opt.fake_crl = NULL; return 1; } @@ -870,6 +874,11 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.debug_cache_expired_certs = 0; break; + case oFakeCRL: + xfree (opt.fake_crl); + opt.fake_crl = *pargs->r.ret_str? xstrdup (pargs->r.ret_str) : NULL; + break; + default: return 0; /* Not handled. */ } @@ -1696,6 +1705,8 @@ dirmngr_deinit_default_ctrl (ctrl_t ctrl) xfree (ctrl->http_proxy); ctrl->http_proxy = NULL; + nvc_release (ctrl->rootdse); + ctrl->rootdse = NULL; } @@ -2030,8 +2041,9 @@ handle_signal (int signo) break; case SIGUSR1: - cert_cache_print_stats (); - domaininfo_print_stats (); + /* See also cmd_getinfo:"stats". */ + cert_cache_print_stats (NULL); + domaininfo_print_stats (NULL); break; case SIGUSR2: diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index e7591b998..1128e118b 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -36,6 +36,7 @@ #include "../common/sysutils.h" /* (gnupg_fd_t) */ #include "../common/asshelp.h" /* (assuan_context_t) */ #include "../common/i18n.h" +#include "../common/name-value.h" #include "dirmngr-status.h" #include "http.h" /* (parsed_uri_t) */ @@ -104,6 +105,7 @@ struct int force; /* Force loading outdated CRLs. */ + char *fake_crl; /* Name of a file with faked CRL entries. */ unsigned int connect_timeout; /* Timeout for connect. */ unsigned int connect_quick_timeout; /* Shorter timeout for connect. */ @@ -166,6 +168,7 @@ struct #define DBG_NETWORK_VALUE 2048 /* debug network I/O. */ #define DBG_LOOKUP_VALUE 8192 /* debug lookup details */ #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ +#define DBG_KEEPTMP_VALUE 32768 /* keep some temporary files */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) @@ -177,6 +180,7 @@ struct #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) +#define DBG_KEEPTMP (opt.debug & DBG_KEEPTMP_VALUE) /* A simple list of certificate references. FIXME: Better use certlist_t also for references (Store NULL at .cert) */ @@ -217,9 +221,12 @@ struct server_control_s int audit_events; /* Send audit events to client. */ char *http_proxy; /* The used http_proxy or NULL. */ + nvc_t rootdse; /* Container wit the rootDSE properties. */ + unsigned int timeout; /* Timeout for connect calls in ms. */ unsigned int http_no_crl:1; /* Do not check CRLs for https. */ + unsigned int rootdse_tried:1;/* Already tried to get the rootDSE. */ }; @@ -238,6 +245,8 @@ void ks_hkp_reload (void); void ks_hkp_init (void); /*-- server.c --*/ +void release_uri_item_list (uri_item_t list); + ldap_server_t get_ldapservers_from_ctrl (ctrl_t ctrl); ksba_cert_t get_cert_local (ctrl_t ctrl, const char *issuer); ksba_cert_t get_issuing_cert_local (ctrl_t ctrl, const char *issuer); @@ -264,7 +273,7 @@ gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force); /*-- domaininfo.c --*/ -void domaininfo_print_stats (void); +void domaininfo_print_stats (ctrl_t ctrl); int domaininfo_is_wkd_not_supported (const char *domain); void domaininfo_set_no_name (const char *domain); void domaininfo_set_wkd_supported (const char *domain); diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index c6a4dd11f..412d0ad1f 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -823,7 +823,7 @@ fetch_ldap (LDAP *ld, const char *base, int scope, const char *filter) /* Main processing. Take the filter and run the LDAP query. The * result is printed to stdout, errors are logged to the log stream. * To allow searching with a different base it is possible to extend - * the filer. For example: + * the filter. For example: * * ^CN=foo, OU=My Users&(objectClasses=*) * diff --git a/dirmngr/domaininfo.c b/dirmngr/domaininfo.c index b41aef366..b6043be53 100644 --- a/dirmngr/domaininfo.c +++ b/dirmngr/domaininfo.c @@ -81,7 +81,7 @@ hash_domain (const char *domain) void -domaininfo_print_stats (void) +domaininfo_print_stats (ctrl_t ctrl) { int bidx; domaininfo_t di; @@ -112,11 +112,12 @@ domaininfo_print_stats (void) if (minlen == -1 || len < minlen) minlen = len; } - log_info ("domaininfo: items=%d chainlen=%d..%d nn=%d nf=%d ns=%d s=%d\n", - count, - minlen > 0? minlen : 0, - maxlen, - no_name, wkd_not_found, wkd_not_supported, wkd_supported); + dirmngr_status_helpf + (ctrl, "domaininfo: items=%d chainlen=%d..%d nn=%d nf=%d ns=%d s=%d\n", + count, + minlen > 0? minlen : 0, + maxlen, + no_name, wkd_not_found, wkd_not_supported, wkd_supported); } diff --git a/dirmngr/fakecrl.c b/dirmngr/fakecrl.c new file mode 100644 index 000000000..43b68a57a --- /dev/null +++ b/dirmngr/fakecrl.c @@ -0,0 +1,63 @@ +/* fakecrl.c - Debug code to test revocations. + * Copyright (C) 2023 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* + * For regression testing it is useful to have a way to claim that + * certain certificates are revoked. We achieve this with the + * --fake-crl option which takes a file name as argument. The format + * of the file is: empty lines and lines starting with a hash sign are + * ignored. A line with the issuer DN in brackets starts entries for + * this issuer. All following lines up to the next line with a + * bracket list revoked certificates. For each revoked certificate + * the hexadecimal encoded serial number is listed, followed by the + * revocation date in ISO 14 byte notation, optionally followed by a + * reason keyword. Example: + *--------------------- + * # Sample Fake CRL + * [CN=Bayern-Softtoken-Issuing-CA-2019,OU=IT-DLZ,O=Freistaat Bayern,C=DE] + * 7FD62B1A9EA5BBC84971183080717004 20221125T074346 + * 11223344556677 20230101T000000 key_compromise + * 0000000000000042 20221206T121200 certificate_hold + * + * [CN=CA IVBB Deutsche Telekom AG 18,OU=Bund,O=PKI-1-Verwaltung,C=DE] + * 735D1B97389F 20230210T083947 + *--------------------- + */ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "dirmngr.h" +#include "crlcache.h" + + + +/* Returns 0 if the given certificate is not listed in the faked CRL + * or no fake CRL is configured. It is expected that the caller then + * consults the real CRL. */ +gpg_error_t +fakecrl_isvalid (ctrl_t ctrl, const char *issuer_hash, const char *cert_id) +{ + (void)ctrl; + (void)issuer_hash; + (void)cert_id; + return 0; +} diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index 7de4a3b95..002f1a7a5 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -34,6 +34,100 @@ # include "ldap-parse-uri.h" #endif + +/* Parse an URI and store it in a new parsed URI item object which is + * returned at R_PARSEDURI (with its next set to NULL). On error an + * error code is returned an NULL stored at R_PARSEDITEM. */ +gpg_error_t +ks_action_parse_uri (const char *uri, uri_item_t *r_parseduri) +{ + gpg_error_t err; + uri_item_t item; + char *tmpstr = NULL; +#if USE_LDAP + const char *s; +#endif + + *r_parseduri = NULL; + + if (!uri) + return gpg_error (GPG_ERR_INV_URI); + + item = xtrymalloc (sizeof *item + strlen (uri)); + if (!item) + return gpg_error_from_syserror (); + + item->next = NULL; + item->parsed_uri = NULL; + strcpy (item->uri, uri); + +#if USE_LDAP + if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/')) + { + /* Special ldap scheme given. This differs from a valid ldap + * scheme in that no double slash follows. We use + * http_parse_uri to put it as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri+5, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + } + else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/')) + { + /* No valid scheme given. We use http_parse_uri to put the + * string as opaque value into parsed_uri. */ + tmpstr = strconcat ("opaque:", uri, NULL); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + err = http_parse_uri (&item->parsed_uri, tmpstr, 0); + } + else if (ldap_uri_p (uri)) + { + int fixup = 0; + /* Fixme: We should get rid of that parser and replace it with + * our generic (http) URI parser. */ + + /* If no port has been specified and the scheme ist ldaps we use + * our idea of the default port because the standard LDAP URL + * parser would use 636 here. This is because we redefined + * ldaps to mean starttls. */ +#ifdef HAVE_W32_SYSTEM + if (!strcmp (uri, "ldap:///")) + fixup = 1; + else +#endif + if (!http_parse_uri (&item->parsed_uri,uri,HTTP_PARSE_NO_SCHEME_CHECK)) + { + if (!item->parsed_uri->port + && !strcmp (item->parsed_uri->scheme, "ldaps")) + fixup = 2; + http_release_parsed_uri (item->parsed_uri); + item->parsed_uri = NULL; + } + + err = ldap_parse_uri (&item->parsed_uri, uri); + if (!err && fixup == 1) + item->parsed_uri->ad_current = 1; + else if (!err && fixup == 2) + item->parsed_uri->port = 389; + } + else +#endif /* USE_LDAP */ + { + err = http_parse_uri (&item->parsed_uri, uri, HTTP_PARSE_NO_SCHEME_CHECK); + } + + xfree (tmpstr); + if (err) + xfree (item); + else + *r_parseduri = item; + return err; +} + + /* Called by the engine's help functions to print the actual help. */ gpg_error_t ks_print_help (ctrl_t ctrl, const char *text) @@ -243,7 +337,8 @@ ks_action_search (ctrl_t ctrl, uri_item_t keyservers, keyservers and write the result to the provided output stream. */ gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, - strlist_t patterns, unsigned int ks_get_flags, estream_t outfp) + strlist_t patterns, unsigned int ks_get_flags, + gnupg_isotime_t newer, estream_t outfp) { gpg_error_t err = 0; gpg_error_t first_err = 0; @@ -270,7 +365,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, || strcmp (uri->parsed_uri->scheme, "https") == 0); int is_ldap = 0; - if ((ks_get_flags & KS_GET_FLAG_ONLY_LDAP)) + if ((ks_get_flags & (KS_GET_FLAG_ONLY_LDAP|KS_GET_FLAG_ONLY_AD))) is_hkp_s = is_http_s = 0; #if USE_LDAP @@ -288,7 +383,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, #if USE_LDAP if (is_ldap) err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, ks_get_flags, - &infp); + newer, &infp); else #endif if (is_hkp_s) @@ -448,3 +543,53 @@ ks_action_put (ctrl_t ctrl, uri_item_t keyservers, err = first_err; return err; } + + + +/* Query the default LDAP server or the one given by URL using + * the filter expression FILTER. Write the result to OUTFP. */ +gpg_error_t +ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags, + const char *filter, char **attrs, + gnupg_isotime_t newer, estream_t outfp) +{ +#if USE_LDAP + gpg_error_t err; + estream_t infp = NULL; + uri_item_t puri; /* The broken down URI (only one item used). */ + + if (!url && (ks_get_flags & KS_GET_FLAG_ROOTDSE)) + url = "ldap://"; + + err = ks_action_parse_uri (url, &puri); + if (err) + return err; + + if ((ks_get_flags & KS_GET_FLAG_ROOTDSE)) + { + /* Reset authentication for a serverless connection. */ + puri->parsed_uri->ad_current = 0; + puri->parsed_uri->auth = NULL; + } + + if (!strcmp (puri->parsed_uri->scheme, "ldap") + || !strcmp (puri->parsed_uri->scheme, "ldaps") + || !strcmp (puri->parsed_uri->scheme, "ldapi") + || puri->parsed_uri->opaque) + { + err = ks_ldap_query (ctrl, puri->parsed_uri, ks_get_flags, filter, + attrs, newer, &infp); + if (!err) + err = copy_stream (infp, outfp); + } + else + err = gpg_error (GPG_ERR_CONFIGURATION); /* No LDAP server known. */ + + es_fclose (infp); + release_uri_item_list (puri); + return err; + +#else /* !USE_LDAP */ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h index e780fc7db..223aae2da 100644 --- a/dirmngr/ks-action.h +++ b/dirmngr/ks-action.h @@ -21,17 +21,22 @@ #ifndef DIRMNGR_KS_ACTION_H #define DIRMNGR_KS_ACTION_H 1 +gpg_error_t ks_action_parse_uri (const char *uri, uri_item_t *r_parseduri); gpg_error_t ks_action_help (ctrl_t ctrl, const char *url); gpg_error_t ks_action_resolve (ctrl_t ctrl, uri_item_t keyservers); gpg_error_t ks_action_search (ctrl_t ctrl, uri_item_t keyservers, strlist_t patterns, estream_t outfp); gpg_error_t ks_action_get (ctrl_t ctrl, uri_item_t keyservers, strlist_t patterns, unsigned int ks_get_flags, - estream_t outfp); + gnupg_isotime_t newer, estream_t outfp); gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp); gpg_error_t ks_action_put (ctrl_t ctrl, uri_item_t keyservers, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_action_query (ctrl_t ctrl, const char *ldapserver, + unsigned int ks_get_flags, + const char *filter, char **attr, + gnupg_isotime_t newer, estream_t outfp); #endif /*DIRMNGR_KS_ACTION_H*/ diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index 0912c6d02..1ffd30ecb 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -1,7 +1,7 @@ /* ks-engine-ldap.c - talk to a LDAP keyserver * Copyright (C) 2001, 2002, 2004, 2005, 2006 * 2007 Free Software Foundation, Inc. - * Copyright (C) 2015, 2020 g10 Code GmbH + * Copyright (C) 2015, 2020, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -32,6 +32,7 @@ #include "misc.h" #include "../common/userids.h" #include "../common/mbox-util.h" +#include "ks-action.h" #include "ks-engine.h" #include "ldap-misc.h" #include "ldap-parse-uri.h" @@ -43,6 +44,7 @@ #define SERVERINFO_PGPKEYV2 2 /* Needs "pgpKeyV2" instead of "pgpKey"*/ #define SERVERINFO_SCHEMAV2 4 /* Version 2 of the Schema. */ #define SERVERINFO_NTDS 8 /* Server is an Active Directory. */ +#define SERVERINFO_GENERIC 16 /* Connected in genric mode. */ /* The page size requested from the server. */ @@ -61,6 +63,7 @@ struct ks_engine_ldap_local_s LDAPMessage *message; LDAPMessage *msg_iter; /* Iterator for message. */ unsigned int serverinfo; + int scope; char *basedn; char *keyspec; char *filter; @@ -192,7 +195,12 @@ ks_ldap_help (ctrl_t ctrl, parsed_uri_t uri) static struct ks_engine_ldap_local_s * ks_ldap_new_state (void) { - return xtrycalloc (1, sizeof(struct ks_engine_ldap_local_s)); + struct ks_engine_ldap_local_s *state; + + state = xtrycalloc (1, sizeof(struct ks_engine_ldap_local_s)); + if (state) + state->scope = LDAP_SCOPE_SUBTREE; + return state; } @@ -217,6 +225,7 @@ ks_ldap_clear_state (struct ks_engine_ldap_local_s *state) } state->serverinfo = 0; xfree (state->basedn); + state->scope = LDAP_SCOPE_SUBTREE; state->basedn = NULL; xfree (state->keyspec); state->keyspec = NULL; @@ -240,6 +249,45 @@ ks_ldap_free_state (struct ks_engine_ldap_local_s *state) } +/* Helper for ks_ldap_get and ks_ldap_query. On return first_mode and + * next_mode are set accordingly. */ +static gpg_error_t +ks_ldap_prepare_my_state (ctrl_t ctrl, unsigned int ks_get_flags, + int *first_mode, int *next_mode) +{ + *first_mode = *next_mode = 0; + + if ((ks_get_flags & KS_GET_FLAG_FIRST)) + { + if (ctrl->ks_get_state) + ks_ldap_clear_state (ctrl->ks_get_state); + else if (!(ctrl->ks_get_state = ks_ldap_new_state ())) + return gpg_error_from_syserror (); + *first_mode = 1; + } + + if ((ks_get_flags & KS_GET_FLAG_NEXT)) + { + if (!ctrl->ks_get_state || !ctrl->ks_get_state->ldap_conn + || !ctrl->ks_get_state->message) + { + log_error ("ks-ldap: --next requested but no state\n"); + return gpg_error (GPG_ERR_INV_STATE); + } + *next_mode = 1; + } + + /* Do not keep an old state around if not needed. */ + if (!(*first_mode || *next_mode)) + { + ks_ldap_free_state (ctrl->ks_get_state); + ctrl->ks_get_state = NULL; + } + + return 0; +} + + /* Convert a keyspec to a filter. Return an error if the keyspec is bad or is not supported. The filter is escaped and returned in @@ -437,7 +485,9 @@ interrogate_ldap_dn (LDAP *ldap_conn, const char *basedn_search, * * URI describes the server to connect to and various options * including whether to use TLS and the username and password (see - * ldap_parse_uri for a description of the various fields). + * ldap_parse_uri for a description of the various fields). Be + * default a PGP keyserver is assumed; if GENERIC is true a generic + * ldap conenction is instead established. * * Returns: The ldap connection handle in *LDAP_CONNP, R_BASEDN is set * to the base DN for the PGP key space, several flags will be stored @@ -450,7 +500,7 @@ interrogate_ldap_dn (LDAP *ldap_conn, const char *basedn_search, * If it is NULL, then the server does not appear to be an OpenPGP * keyserver. */ static gpg_error_t -my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, +my_ldap_connect (parsed_uri_t uri, unsigned int generic, LDAP **ldap_connp, char **r_basedn, char **r_host, int *r_use_tls, unsigned int *r_serverinfo) { @@ -519,15 +569,15 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, } if (opt.verbose) - log_info ("ldap connect to '%s:%d:%s:%s:%s:%s%s%s'\n", + log_info ("ldap connect to '%s:%d:%s:%s:%s:%s%s%s'%s\n", host, port, basedn_arg ? basedn_arg : "", bindname ? bindname : "", password ? "*****" : "", use_tls == 1? "starttls" : use_tls == 2? "ldaptls" : "plain", use_ntds ? ",ntds":"", - use_areconly? ",areconly":""); - + use_areconly? ",areconly":"", + generic? " (generic)":""); /* If the uri specifies a secure connection and we don't support TLS, then fail; don't silently revert to an insecure @@ -535,7 +585,7 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, if (use_tls) { #ifndef HAVE_LDAP_START_TLS_S - log_error ("ldap: can't connect to the server: no TLS support."); + log_error ("ks-ldap: can't connect to the server: no TLS support."); err = GPG_ERR_LDAP_NOT_SUPPORTED; goto out; #endif @@ -607,6 +657,8 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, { int ver = opt.ldaptimeout; + /* fixme: also use LDAP_OPT_SEND_TIMEOUT? */ + lerr = ldap_set_option (ldap_conn, LDAP_OPT_TIMELIMIT, &ver); if (lerr != LDAP_SUCCESS) { @@ -699,7 +751,21 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, /* By default we don't bind as there is usually no need to. */ } - if (basedn_arg && *basedn_arg) + if (generic) + { + /* Generic use of this function for arbitrary LDAP servers. */ + *r_serverinfo |= SERVERINFO_GENERIC; + if (basedn_arg && *basedn_arg) + { + basedn = xtrystrdup (basedn_arg); + if (!basedn) + { + err = gpg_error_from_syserror (); + goto out; + } + } + } + else if (basedn_arg && *basedn_arg) { /* User specified base DN. In this case we know the server is a * real LDAP server. */ @@ -819,11 +885,15 @@ my_ldap_connect (parsed_uri_t uri, LDAP **ldap_connp, if (!err && opt.debug) { log_debug ("ldap_conn: %p\n", ldap_conn); - log_debug ("server_type: %s\n", ((*r_serverinfo & SERVERINFO_REALLDAP) - ? "LDAP" : "PGP.com keyserver") ); + log_debug ("server_type: %s\n", + ((*r_serverinfo & SERVERINFO_GENERIC) + ? "Generic" : + (*r_serverinfo & SERVERINFO_REALLDAP) + ? "LDAP" : "PGP.com keyserver") ); log_debug ("basedn: %s\n", basedn); - log_debug ("pgpkeyattr: %s\n", - (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); + if (!(*r_serverinfo & SERVERINFO_GENERIC)) + log_debug ("pgpkeyattr: %s\n", + (*r_serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2":"pgpKey"); } ldapserver_list_free (server); @@ -934,6 +1004,15 @@ extract_keys (estream_t output, } my_ldap_value_free (vals); + vals = ldap_get_values (ldap_conn, message, "modifyTimestamp"); + if (vals && vals[0]) + { + gnupg_isotime_t atime; + if (!rfc4517toisotime (atime, vals[0])) + es_fprintf (output, "chg:%s:\n", atime); + } + my_ldap_value_free (vals); + es_fprintf (output, "INFO %s END\n", certid); } @@ -1028,11 +1107,132 @@ return_one_keyblock (LDAP *ldap_conn, LDAPMessage *msg, unsigned int serverinfo, } -/* Helper for ks_ldap_get. Note that KEYSPEC is only used for - * diagnostics. */ +/* Helper for ks_ldap_query. Returns 0 if an attr was fetched and + * printed to FP. The error code GPG_ERR_NO_DATA is returned if no + * data was printed. Note that FP is updated by this function. */ +static gpg_error_t +return_all_attributes (LDAP *ld, LDAPMessage *msg, estream_t *fp) +{ + gpg_error_t err = 0; + BerElement *berctx = NULL; + char *attr = NULL; + const char *attrprefix; + struct berval **values = NULL; + int idx; + int any = 0; + const char *s; + const char *val; + size_t len; + char *mydn; + + mydn = ldap_get_dn (ld, msg); + if (!*fp) + { + *fp = es_fopenmem(0, "rw"); + if (!*fp) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + /* Always print the DN - note that by using only unbkown attributes + * it is pissible to list just the DNs with out addiional + * linefeeds. */ + es_fprintf (*fp, "Dn: %s\n", mydn? mydn : "[oops DN missing]"); + + for (npth_unprotect (), attr = ldap_first_attribute (ld, msg, &berctx), + npth_protect (); + attr; + npth_unprotect (), attr = ldap_next_attribute (ld, msg, berctx), + npth_protect ()) + { + npth_unprotect (); + values = ldap_get_values_len (ld, msg, attr); + npth_protect (); + + if (!values) + { + if (opt.verbose) + log_info ("attribute '%s' not found\n", attr); + ldap_memfree (attr); + attr = NULL; + continue; + } + + any = 1; + + if (opt.verbose > 1) + { + log_info ("found attribute '%s'\n", attr); + for (idx=0; values[idx]; idx++) + log_info (" length[%d]=%d\n", + idx, (int)values[0]->bv_len); + } + + if (!ascii_strcasecmp (attr, "Dn")) + attrprefix = "X-"; + else if (*attr == '#') + attrprefix = "X-hash-"; + else if (*attr == ' ') + attrprefix = "X-blank-"; + else + attrprefix = ""; + /* FIXME: We should remap all invalid chars in ATTR. */ + + for (idx=0; values[idx]; idx++) + { + es_fprintf (*fp, "%s%s: ", attrprefix, attr); + val = values[idx]->bv_val; + len = values[idx]->bv_len; + while (len && (s = memchr (val, '\n', len))) + { + s++; /* We als want to print the LF. */ + if (es_fwrite (val, s - val, 1, *fp) != 1) + goto fwrite_failed; + len -= (s-val); + val = s; + if (len && es_fwrite (" ", 1, 1, *fp) != 1) + goto fwrite_failed; + } + if (len && es_fwrite (val, len, 1, *fp) != 1) + goto fwrite_failed; + if (es_fwrite ("\n", 1, 1, *fp) != 1) /* Final LF. */ + goto fwrite_failed; + } + + ldap_value_free_len (values); + values = NULL; + ldap_memfree (attr); + attr = NULL; + } + + /* One final linefeed to prettify the output. */ + if (any && es_fwrite ("\n", 1, 1, *fp) != 1) + goto fwrite_failed; + + + leave: + if (values) + ldap_value_free_len (values); + ldap_memfree (attr); + if (mydn) + ldap_memfree (mydn); + ber_free (berctx, 0); + return err; + + fwrite_failed: + err = gpg_error_from_syserror (); + log_error ("error writing to stdout: %s\n", gpg_strerror (err)); + goto leave; +} + + +/* Helper for ks_ldap_get and ks_ldap_query. Note that KEYSPEC is + * only used for diagnostics. */ static gpg_error_t search_and_parse (ctrl_t ctrl, const char *keyspec, - LDAP *ldap_conn, char *basedn, char *filter, + LDAP *ldap_conn, char *basedn, int scope, char *filter, char **attrs, LDAPMessage **r_message) { gpg_error_t err = 0; @@ -1065,7 +1265,7 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, } npth_unprotect (); - l_err = ldap_search_ext_s (ldap_conn, basedn, LDAP_SCOPE_SUBTREE, + l_err = ldap_search_ext_s (ldap_conn, basedn, scope, filter, attrs, 0, srvctrls[0]? srvctrls : NULL, NULL, NULL, 0, r_message); @@ -1130,7 +1330,7 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, if (count < 1) { if (!ctrl->ks_get_state || ctrl->ks_get_state->pageno == 1) - log_info ("ks-ldap: key %s not found on keyserver\n", keyspec); + log_info ("ks-ldap: '%s' not found on LDAP server\n", keyspec); if (count == -1) err = ldap_to_gpg_err (ldap_conn); @@ -1150,20 +1350,92 @@ search_and_parse (ctrl_t ctrl, const char *keyspec, } +/* Fetch all entries from the RootDSE and return them as a name value + * object. */ +static nvc_t +fetch_rootdse (ctrl_t ctrl, parsed_uri_t uri) +{ + gpg_error_t err; + estream_t infp = NULL; + uri_item_t puri; /* The broken down URI (only one item used). */ + nvc_t nvc = NULL; + + /* FIXME: We need the unparsed URI here - use uri_item_t instead + * of fix the parser to fill in original */ + err = ks_action_parse_uri (uri && uri->original? uri->original : "ldap://", + &puri); + if (err) + return NULL; + + /* Reset authentication for a serverless. */ + puri->parsed_uri->ad_current = 0; + puri->parsed_uri->auth = NULL; + + if (!strcmp (puri->parsed_uri->scheme, "ldap") + || !strcmp (puri->parsed_uri->scheme, "ldaps") + || !strcmp (puri->parsed_uri->scheme, "ldapi") + || puri->parsed_uri->opaque) + { + err = ks_ldap_query (ctrl, puri->parsed_uri, KS_GET_FLAG_ROOTDSE, + "^&base&(objectclass=*)", NULL, NULL, &infp); + if (err) + log_error ("ldap: reading the rootDES failed: %s\n", + gpg_strerror (err)); + else if ((err = nvc_parse (&nvc, NULL, infp))) + log_error ("parsing the rootDES failed: %s\n", gpg_strerror (err)); + } + + es_fclose (infp); + release_uri_item_list (puri); + if (err) + { + nvc_release (nvc); + nvc = NULL; + } + return nvc; +} + + +/* Return the baseDN for URI which might have already been cached for + * this session. */ +static char * +basedn_from_rootdse (ctrl_t ctrl, parsed_uri_t uri) +{ + const char *s; + + if (!ctrl->rootdse && !ctrl->rootdse_tried) + { + ctrl->rootdse = fetch_rootdse (ctrl, uri); + ctrl->rootdse_tried = 1; + if (ctrl->rootdse) + { + log_debug ("Dump of all rootDSE attributes:\n"); + nvc_write (ctrl->rootdse, log_get_stream ()); + log_debug ("End of dump\n"); + } + } + s = nvc_get_string (ctrl->rootdse, "defaultNamingContext:"); + return s? xtrystrdup (s): NULL; +} + + + + /* Get the key described key the KEYSPEC string from the keyserver * identified by URI. On success R_FP has an open stream to read the * data. KS_GET_FLAGS conveys flags from the client. */ gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, - unsigned int ks_get_flags, estream_t *r_fp) + unsigned int ks_get_flags, gnupg_isotime_t newer, estream_t *r_fp) { - gpg_error_t err = 0; + gpg_error_t err; unsigned int serverinfo; char *host = NULL; int use_tls; char *filter = NULL; LDAP *ldap_conn = NULL; char *basedn = NULL; + int scope = LDAP_SCOPE_SUBTREE; estream_t fp = NULL; LDAPMessage *message = NULL; LDAPMessage *msg; @@ -1179,46 +1451,19 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, { "dummy", /* (to be be replaced.) */ "pgpcertid", "pgpuserid", "pgpkeyid", "pgprevoked", "pgpdisabled", - "pgpkeycreatetime", "modifytimestamp", "pgpkeysize", "pgpkeytype", + "pgpkeycreatetime", "modifyTimestamp", "pgpkeysize", "pgpkeytype", "gpgfingerprint", NULL }; - (void) ctrl; - if (dirmngr_use_tor ()) { return no_ldap_due_to_tor (ctrl); } - /* Make sure we got a state. */ - if ((ks_get_flags & KS_GET_FLAG_FIRST)) - { - if (ctrl->ks_get_state) - ks_ldap_clear_state (ctrl->ks_get_state); - else if (!(ctrl->ks_get_state = ks_ldap_new_state ())) - return gpg_error_from_syserror (); - first_mode = 1; - } - - if ((ks_get_flags & KS_GET_FLAG_NEXT)) - { - if (!ctrl->ks_get_state || !ctrl->ks_get_state->ldap_conn - || !ctrl->ks_get_state->message) - { - log_error ("ks_ldap: --next requested but no state\n"); - return gpg_error (GPG_ERR_INV_STATE); - } - next_mode = 1; - } - - /* Do not keep an old state around if not needed. */ - if (!(first_mode || next_mode)) - { - ks_ldap_free_state (ctrl->ks_get_state); - ctrl->ks_get_state = NULL; - } - + err = ks_ldap_prepare_my_state (ctrl, ks_get_flags, &first_mode, &next_mode); + if (err) + return err; if (next_mode) { @@ -1236,6 +1481,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, err = search_and_parse (ctrl, ctrl->ks_get_state->keyspec, ctrl->ks_get_state->ldap_conn, ctrl->ks_get_state->basedn, + ctrl->ks_get_state->scope, ctrl->ks_get_state->filter, attrs, &ctrl->ks_get_state->message); @@ -1284,7 +1530,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, else /* Not in --next mode. */ { /* Make sure we are talking to an OpenPGP LDAP server. */ - err = my_ldap_connect (uri, &ldap_conn, + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, &host, &use_tls, &serverinfo); if (err || !basedn) { @@ -1305,14 +1551,36 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, if (err) goto leave; + if (*newer) + { + char *tstr, *fstr; + + tstr = isotime2rfc4517 (newer); + if (!tstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + fstr = strconcat ("(&", filter, + "(modifyTimestamp>=", tstr, "))", NULL); + xfree (tstr); + if (!fstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (filter); + filter = fstr; + } + if (opt.debug) log_debug ("ks-ldap: using filter: %s\n", filter); /* Replace "dummy". */ attrs[0] = (serverinfo & SERVERINFO_PGPKEYV2)? "pgpKeyV2" : "pgpKey"; - err = search_and_parse (ctrl, keyspec, ldap_conn, basedn, filter, attrs, - &message); + err = search_and_parse (ctrl, keyspec, ldap_conn, basedn, scope, + filter, attrs, &message); if (err) goto leave; @@ -1363,6 +1631,7 @@ ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, ctrl->ks_get_state->message = message; message = NULL; ctrl->ks_get_state->serverinfo = serverinfo; + ctrl->ks_get_state->scope = scope; ctrl->ks_get_state->basedn = basedn; basedn = NULL; ctrl->ks_get_state->keyspec = keyspec? xtrystrdup (keyspec) : NULL; @@ -1423,7 +1692,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } /* Make sure we are talking to an OpenPGP LDAP server. */ - err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, NULL, NULL, &serverinfo); if (err || !basedn) { if (!err) @@ -1459,7 +1728,7 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, char *attrs[] = { "pgpcertid", "pgpuserid", "pgprevoked", "pgpdisabled", - "pgpkeycreatetime", "pgpkeyexpiretime", "modifytimestamp", + "pgpkeycreatetime", "pgpkeyexpiretime", "modifyTimestamp", "pgpkeysize", "pgpkeytype", "gpgfingerprint", NULL }; @@ -1613,19 +1882,17 @@ ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } my_ldap_value_free (vals); -#if 0 - /* This is not yet specified in the keyserver - protocol, but may be someday. */ es_fputc (':', fp); - vals = ldap_get_values (ldap_conn, each, "modifytimestamp"); - if(vals && vals[0] strlen (vals[0]) == 15) + vals = ldap_get_values (ldap_conn, each, "modifyTimestamp"); + if(vals && vals[0]) { - es_fprintf (fp, "%u", - (unsigned int) ldap2epochtime (vals[0])); + gnupg_isotime_t atime; + if (rfc4517toisotime (atime, vals[0])) + *atime = 0; + es_fprintf (fp, "%s", atime); } my_ldap_value_free (vals); -#endif es_fprintf (fp, "\n"); @@ -2312,7 +2579,7 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, return no_ldap_due_to_tor (ctrl); } - err = my_ldap_connect (uri, &ldap_conn, &basedn, NULL, NULL, &serverinfo); + err = my_ldap_connect (uri, 0, &ldap_conn, &basedn, NULL, NULL, &serverinfo); if (err || !basedn) { if (!err) @@ -2538,3 +2805,249 @@ ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, return err; } + + + +/* Get the data described by FILTER_ARG from URI. On success R_FP has + * an open stream to read the data. KS_GET_FLAGS conveys flags from + * the client. ATTRS is a NULL terminated list of attributes to + * return or NULL for all. */ +gpg_error_t +ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, unsigned int ks_get_flags, + const char *filter_arg, char **attrs, + gnupg_isotime_t newer, estream_t *r_fp) +{ + gpg_error_t err; + unsigned int serverinfo; + char *host = NULL; + int use_tls; + LDAP *ldap_conn = NULL; + char *basedn = NULL; + estream_t fp = NULL; + char *filter = NULL; + int scope = LDAP_SCOPE_SUBTREE; + LDAPMessage *message = NULL; + LDAPMessage *msg; + int anydata = 0; + int first_mode = 0; + int next_mode = 0; + int get_first; + + if (dirmngr_use_tor ()) + return no_ldap_due_to_tor (ctrl); + + if ((!filter_arg || !*filter_arg) && (ks_get_flags & KS_GET_FLAG_ROOTDSE)) + filter_arg = "^&base&(objectclass=*)"; + + err = ks_ldap_prepare_my_state (ctrl, ks_get_flags, &first_mode, &next_mode); + if (err) + goto leave; + + if (!next_mode) /* (In --next mode the filter is ignored.) */ + { + if (!filter_arg || !*filter_arg) + { + err = gpg_error (GPG_ERR_LDAP_FILTER); + goto leave; + } + err = ldap_parse_extfilter (filter_arg, 0, &basedn, &scope, &filter); + if (err) + goto leave; + if (newer && *newer) + { + char *tstr, *fstr; + + tstr = isotime2rfc4517 (newer); + if (!tstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (filter && *filter) + fstr = strconcat ("(&", filter, + "(modifyTimestamp>=", tstr, "))", NULL); + else + fstr = strconcat ("(modifyTimestamp>=", tstr, ")", NULL); + xfree (tstr); + if (!fstr) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (filter); + filter = fstr; + } + } + + + if (next_mode) + { + next_again: + if (!ctrl->ks_get_state->msg_iter && ctrl->ks_get_state->more_pages) + { + /* Get the next page of results. */ + if (ctrl->ks_get_state->message) + { + ldap_msgfree (ctrl->ks_get_state->message); + ctrl->ks_get_state->message = NULL; + } + err = search_and_parse (ctrl, ctrl->ks_get_state->keyspec, + ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->basedn, + ctrl->ks_get_state->scope, + ctrl->ks_get_state->filter, + attrs, + &ctrl->ks_get_state->message); + if (err) + goto leave; + ctrl->ks_get_state->msg_iter = ctrl->ks_get_state->message; + get_first = 1; + } + else + get_first = 0; + + while (ctrl->ks_get_state->msg_iter) + { + npth_unprotect (); + ctrl->ks_get_state->msg_iter + = get_first? ldap_first_entry (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter) + /* */ : ldap_next_entry (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter); + npth_protect (); + get_first = 0; + if (ctrl->ks_get_state->msg_iter) + { + err = return_all_attributes (ctrl->ks_get_state->ldap_conn, + ctrl->ks_get_state->msg_iter, + &fp); + if (!err) + break; /* Found. */ + else if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* Skip empty attributes. */ + else + goto leave; + } + } + + if (!ctrl->ks_get_state->msg_iter || !fp) + { + ctrl->ks_get_state->msg_iter = NULL; + if (ctrl->ks_get_state->more_pages) + goto next_again; + err = gpg_error (GPG_ERR_NO_DATA); + } + + } + else /* Not in --next mode. */ + { + /* Connect to the LDAP server in generic mode. */ + char *tmpbasedn; + + err = my_ldap_connect (uri, 1 /*generic*/, &ldap_conn, + &tmpbasedn, &host, &use_tls, &serverinfo); + if (err) + goto leave; + if (basedn) + xfree (tmpbasedn); /* Extended syntax overrides. */ + else if (tmpbasedn) + basedn = tmpbasedn; + else if (!(ks_get_flags & KS_GET_FLAG_ROOTDSE)) + { + /* No BaseDN known - get one. */ + basedn = basedn_from_rootdse (ctrl, uri); + } + + if (opt.debug) + { + log_debug ("ks-ldap: using basedn: %s\n", basedn); + log_debug ("ks-ldap: using filter: %s\n", filter); + } + + err = search_and_parse (ctrl, filter, ldap_conn, basedn, scope, filter, + attrs, &message); + if (err) + goto leave; + + + for (npth_unprotect (), + msg = ldap_first_entry (ldap_conn, message), + npth_protect (); + msg; + npth_unprotect (), + msg = ldap_next_entry (ldap_conn, msg), + npth_protect ()) + { + err = return_all_attributes (ldap_conn, msg, &fp); + if (!err) + { + anydata = 1; + if (first_mode) + break; + } + else if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* Skip empty/duplicate attributes. */ + else + goto leave; + } + + if (ctrl->ks_get_state) /* Save the iterator. */ + ctrl->ks_get_state->msg_iter = msg; + + if (!fp) /* Nothing was found. */ + err = gpg_error (GPG_ERR_NO_DATA); + + if (!err && anydata) + err = dirmngr_status_printf (ctrl, "SOURCE", "%s://%s", + use_tls? "ldaps" : "ldap", + host? host:""); + } + + + leave: + /* Store our state if needed. */ + if (!err && (ks_get_flags & KS_GET_FLAG_FIRST)) + { + log_assert (!ctrl->ks_get_state->ldap_conn); + ctrl->ks_get_state->ldap_conn = ldap_conn; + ldap_conn = NULL; + log_assert (!ctrl->ks_get_state->message); + ctrl->ks_get_state->message = message; + message = NULL; + ctrl->ks_get_state->serverinfo = serverinfo; + ctrl->ks_get_state->scope = scope; + ctrl->ks_get_state->basedn = basedn; + basedn = NULL; + ctrl->ks_get_state->keyspec = filter? xtrystrdup (filter) : NULL; + ctrl->ks_get_state->filter = filter; + filter = NULL; + } + if ((ks_get_flags & KS_GET_FLAG_NEXT)) + { + /* Keep the state in --next mode even with errors. */ + ldap_conn = NULL; + message = NULL; + } + + if (message) + ldap_msgfree (message); + + if (err) + es_fclose (fp); + else + { + if (fp) + es_fseek (fp, 0, SEEK_SET); + *r_fp = fp; + } + + xfree (basedn); + xfree (host); + + if (ldap_conn) + ldap_unbind (ldap_conn); + + xfree (filter); + + return err; +} diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h index be4e27e6f..03588a4d3 100644 --- a/dirmngr/ks-engine.h +++ b/dirmngr/ks-engine.h @@ -27,6 +27,8 @@ #define KS_GET_FLAG_ONLY_LDAP 1 #define KS_GET_FLAG_FIRST 2 #define KS_GET_FLAG_NEXT 4 +#define KS_GET_FLAG_ONLY_AD 8 /* Do this only if we have an AD. */ +#define KS_GET_FLAG_ROOTDSE 16 /* Get the rootDSE. */ /*-- ks-action.c --*/ @@ -74,10 +76,14 @@ gpg_error_t ks_ldap_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, estream_t *r_fp); gpg_error_t ks_ldap_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, unsigned int ks_get_flags, - estream_t *r_fp); + gnupg_isotime_t newer, estream_t *r_fp); gpg_error_t ks_ldap_put (ctrl_t ctrl, parsed_uri_t uri, void *data, size_t datalen, void *info, size_t infolen); +gpg_error_t ks_ldap_query (ctrl_t ctrl, parsed_uri_t uri, + unsigned int ks_get_flags, + const char *filter, char **attrs, + gnupg_isotime_t newer, estream_t *r_fp); #endif /*DIRMNGR_KS_ENGINE_H*/ diff --git a/dirmngr/ldap-misc.c b/dirmngr/ldap-misc.c index 90f1d1f3c..6b0939a3b 100644 --- a/dirmngr/ldap-misc.c +++ b/dirmngr/ldap-misc.c @@ -332,3 +332,90 @@ ldap_parse_extfilter (const char *string, int silent, } return err; } + + + +/* Scan an ISO timestamp and return a Generalized Time according to + * RFC-4517. The only supported format is "yyyymmddThhmmss[Z]" + * delimited by white space, nul, a colon or a comma. Returns a + * malloced string or NULL for an invalid string or on memory + * error. */ +char * +isotime2rfc4517 (const char *string) +{ + int year, month, day, hour, minu, sec; + + if (!isotime_p (string)) + { + errno = 0; + return NULL; + } + + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 9); + minu = atoi_2 (string + 11); + sec = atoi_2 (string + 13); + + /* Basic checks (1600 due to the LDAP time format base) */ + if (year < 1600 || month < 1 || month > 12 || day < 1 || day > 31 + || hour > 23 || minu > 59 || sec > 61 ) + { + errno = 0; + return NULL; + } + + return gpgrt_bsprintf ("%04d%02d%02d%02d%02d%02d.0Z", + year, month, day, hour, minu, sec); +} + + +/* Parse an LDAP Generalized Time string and update the provided + * isotime buffer. On error return and error code. */ +gpg_error_t +rfc4517toisotime (gnupg_isotime_t timebuf, const char *string) +{ + int i; + int year, month, day, hour, minu, sec; + const char *s; + + for (i=0, s=string; i < 10; i++, s++) /* Need yyyymmddhh */ + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 9); + minu = 0; + sec = 0; + if (digitp (s) && digitp (s+1)) + { + minu = atoi_2 (s); + s += 2; + if (digitp (s) && digitp (s+1)) + { + sec = atoi_2 (s); + s += 2; + } + } + if (*s == '.' || *s == ',') + { + s++; + if (!digitp (s)) /* At least one digit of the fraction required. */ + return gpg_error (GPG_ERR_INV_TIME); + s++; + while (digitp (s)) + s++; + } + if (*s == 'Z' && (!s[1] || spacep (s+1))) + ; /* stop here. */ + else if (*s == '-' || *s == '+') + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */ + else + return gpg_error (GPG_ERR_INV_TIME); + + snprintf (timebuf, sizeof (gnupg_isotime_t), "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minu, sec); + return 0; +} diff --git a/dirmngr/ldap-misc.h b/dirmngr/ldap-misc.h index d555caf49..03efe5fa7 100644 --- a/dirmngr/ldap-misc.h +++ b/dirmngr/ldap-misc.h @@ -38,6 +38,8 @@ gpg_err_code_t ldap_err_to_gpg_err (int code); gpg_err_code_t ldap_to_gpg_err (LDAP *ld); gpg_error_t ldap_parse_extfilter (const char *string, int silent, char **r_base, int *r_scope, char **r_filter); +char *isotime2rfc4517 (const char *string); +gpg_error_t rfc4517toisotime (gnupg_isotime_t timebuf, const char *string); #endif /*DIRMNGR_LDAP_MISC_H*/ diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index f9f6d5f1a..b80012d03 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -525,7 +525,7 @@ make_one_filter (const char *pattern, char **r_result) { /* We need just the BaseDN. This assumes that the Subject * is correcly stored in the DT. This is however not always - * the case and the actual DN is different ffrom the + * the case and the actual DN is different from the * subject. In this case we won't find anything. */ if (extfilt_need_escape (pattern) && !(pattern = pattern_buffer = extfilt_escape (pattern))) diff --git a/dirmngr/server.c b/dirmngr/server.c index fba2233d4..2c5a41b07 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -146,7 +146,7 @@ get_ldapservers_from_ctrl (ctrl_t ctrl) } /* Release an uri_item_t list. */ -static void +void release_uri_item_list (uri_item_t list) { while (list) @@ -1339,6 +1339,10 @@ cmd_isvalid (assuan_context_t ctx, char *line) } else if (only_ocsp) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (opt.fake_crl && (err = fakecrl_isvalid (ctrl, issuerhash, serialno))) + { + /* We already got the error code. */ + } else { switch (crl_cache_isvalid (ctrl, @@ -1360,8 +1364,11 @@ cmd_isvalid (assuan_context_t ctx, char *line) goto again; } break; + case CRL_CACHE_NOTTRUSTED: + err = gpg_error (GPG_ERR_NOT_TRUSTED); + break; case CRL_CACHE_CANTUSE: - err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + err = gpg_error (GPG_ERR_INV_CRL_OBJ); break; default: log_fatal ("crl_cache_isvalid returned invalid code\n"); @@ -1374,7 +1381,7 @@ cmd_isvalid (assuan_context_t ctx, char *line) /* If the line contains a SHA-1 fingerprint as the first argument, - return the FPR vuffer on success. The function checks that the + return the FPR buffer on success. The function checks that the fingerprint consists of valid characters and prints and error message if it does not and returns NULL. Fingerprints are considered optional and thus no explicit error is returned. NULL is @@ -1469,7 +1476,7 @@ cmd_checkcrl (assuan_context_t ctx, char *line) goto leave; } - assert (cert); + log_assert (cert); err = crl_cache_cert_isvalid (ctrl, cert, ctrl->force_crl_refresh); if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN) @@ -2140,15 +2147,6 @@ cmd_validate (assuan_context_t ctx, char *line) static gpg_error_t make_keyserver_item (const char *uri, uri_item_t *r_item) { - gpg_error_t err; - uri_item_t item; - char *tmpstr = NULL; -#if USE_LDAP - const char *s; -#endif - - *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 @@ -2180,78 +2178,7 @@ make_keyserver_item (const char *uri, uri_item_t *r_item) else if (!strcmp (uri, "http://http-keys.gnupg.net")) uri = "hkp://keyserver.ubuntu.com:80"; - item = xtrymalloc (sizeof *item + strlen (uri)); - if (!item) - return gpg_error_from_syserror (); - - item->next = NULL; - item->parsed_uri = NULL; - strcpy (item->uri, uri); - -#if USE_LDAP - if (!strncmp (uri, "ldap:", 5) && !(uri[5] == '/' && uri[6] == '/')) - { - /* Special ldap scheme given. This differs from a valid ldap - * scheme in that no double slash follows.. Use http_parse_uri - * to put it as opaque value into parsed_uri. */ - tmpstr = strconcat ("opaque:", uri+5, NULL); - if (!tmpstr) - err = gpg_error_from_syserror (); - else - err = http_parse_uri (&item->parsed_uri, tmpstr, 0); - } - else if ((s=strchr (uri, ':')) && !(s[1] == '/' && s[2] == '/')) - { - /* No valid scheme given. Use http_parse_uri to put the string - * as opaque value into parsed_uri. */ - tmpstr = strconcat ("opaque:", uri, NULL); - if (!tmpstr) - err = gpg_error_from_syserror (); - else - err = http_parse_uri (&item->parsed_uri, tmpstr, 0); - } - else if (ldap_uri_p (uri)) - { - int fixup = 0; - /* Fixme: We should get rid of that parser and replace it with - * our generic (http) URI parser. */ - - /* If no port has been specified and the scheme ist ldaps we use - * our idea of the default port because the standard LDAP URL - * parser would use 636 here. This is because we redefined - * ldaps to mean starttls. */ -#ifdef HAVE_W32_SYSTEM - if (!strcmp (uri, "ldap:///")) - fixup = 1; - else -#endif - if (!http_parse_uri (&item->parsed_uri,uri,HTTP_PARSE_NO_SCHEME_CHECK)) - { - if (!item->parsed_uri->port - && !strcmp (item->parsed_uri->scheme, "ldaps")) - fixup = 2; - http_release_parsed_uri (item->parsed_uri); - item->parsed_uri = NULL; - } - - err = ldap_parse_uri (&item->parsed_uri, uri); - if (!err && fixup == 1) - item->parsed_uri->ad_current = 1; - else if (!err && fixup == 2) - item->parsed_uri->port = 389; - } - else -#endif /* USE_LDAP */ - { - err = http_parse_uri (&item->parsed_uri, uri, HTTP_PARSE_NO_SCHEME_CHECK); - } - - xfree (tmpstr); - if (err) - xfree (item); - else - *r_item = item; - return err; + return ks_action_parse_uri (uri, r_item); } @@ -2534,22 +2461,28 @@ cmd_ks_search (assuan_context_t ctx, char *line) static const char hlp_ks_get[] = - "KS_GET [--quick] [--ldap] [--first|--next] {<pattern>}\n" + "KS_GET [--quick] [--newer=TIME] [--ldap] [--first|--next] {<pattern>}\n" "\n" "Get the keys matching PATTERN from the configured OpenPGP keyservers\n" "(see command KEYSERVER). Each pattern should be a keyid, a fingerprint,\n" "or an exact name indicated by the '=' prefix. Option --quick uses a\n" "shorter timeout; --ldap will use only ldap servers. With --first only\n" - "the first item is returned; --next is used to return the next item"; + "the first item is returned; --next is used to return the next item\n" + "Option --newer works only with certain LDAP servers."; static gpg_error_t cmd_ks_get (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - strlist_t list, sl; + strlist_t list = NULL; + strlist_t sl; + const char *s; char *p; estream_t outfp; unsigned int flags = 0; + gnupg_isotime_t opt_newer; + + *opt_newer = 0; if (has_option (line, "--quick")) ctrl->timeout = opt.connect_quick_timeout; @@ -2559,13 +2492,18 @@ cmd_ks_get (assuan_context_t ctx, char *line) flags |= KS_GET_FLAG_FIRST; if (has_option (line, "--next")) flags |= KS_GET_FLAG_NEXT; + if ((s = option_value (line, "--newer")) + && !string2isotime (opt_newer, s)) + { + err = set_error (GPG_ERR_SYNTAX, "invalid time format"); + goto leave; + } line = skip_options (line); /* Break the line into a strlist. Each pattern is by definition percent-plus escaped. However we only support keyids and fingerprints and thus the client has no need to apply the escaping. */ - list = NULL; for (p=line; *p; line = p) { while (*p && *p != ' ') @@ -2642,7 +2580,7 @@ cmd_ks_get (assuan_context_t ctx, char *line) ctrl->server_local->inhibit_data_logging_now = 0; ctrl->server_local->inhibit_data_logging_count = 0; err = ks_action_get (ctrl, ctrl->server_local->keyservers, - list, flags, outfp); + list, flags, opt_newer, outfp); es_fclose (outfp); ctrl->server_local->inhibit_data_logging = 0; } @@ -2762,6 +2700,96 @@ cmd_ks_put (assuan_context_t ctx, char *line) +static const char hlp_ad_query[] = + "AD_QUERY [--first|--next] [--] <filter_expression> \n" + "\n" + "Query properties from a Windows Active Directory.\n" + "Our extended filter syntax may be used for the filter\n" + "expression; see gnupg/dirmngr/ldap-misc.c. There are\n" + "a couple of other options available:\n\n" + " --rootdse - Query the root using serverless binding,\n" + " --attr=<attribs> - Comma delimited list of attributes\n" + " to return.\n" + ; +static gpg_error_t +cmd_ad_query (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned int flags = 0; + const char *filter; + estream_t outfp = NULL; + char *p; + char **opt_attr = NULL; + const char *s; + gnupg_isotime_t opt_newer; + + *opt_newer = 0; + + /* No options for now. */ + if (has_option (line, "--first")) + flags |= KS_GET_FLAG_FIRST; + if (has_option (line, "--next")) + flags |= KS_GET_FLAG_NEXT; + if (has_option (line, "--rootdse")) + flags |= KS_GET_FLAG_ROOTDSE; + if ((s = option_value (line, "--newer")) + && !string2isotime (opt_newer, s)) + { + err = set_error (GPG_ERR_SYNTAX, "invalid time format"); + goto leave; + } + err = get_option_value (line, "--attr", &p); + if (err) + goto leave; + if (p) + { + opt_attr = strtokenize (p, ","); + if (!opt_attr) + { + err = gpg_error_from_syserror (); + xfree (p); + goto leave; + } + xfree (p); + } + line = skip_options (line); + filter = line; + + if ((flags & KS_GET_FLAG_NEXT)) + { + if (*filter || (flags & ~KS_GET_FLAG_NEXT)) + { + err = PARM_ERROR ("No filter or other options allowed with --next"); + goto leave; + } + } + + /* Setup an output stream and perform the get. */ + outfp = es_fopencookie (ctx, "w", data_line_cookie_functions); + if (!outfp) + { + err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream"); + goto leave; + } + + ctrl->server_local->inhibit_data_logging = 1; + ctrl->server_local->inhibit_data_logging_now = 0; + ctrl->server_local->inhibit_data_logging_count = 0; + + err = ks_action_query (ctrl, + (flags & KS_GET_FLAG_ROOTDSE)? NULL : "ldap:///", + flags, filter, opt_attr, opt_newer, outfp); + + leave: + es_fclose (outfp); + xfree (opt_attr); + ctrl->server_local->inhibit_data_logging = 0; + return leave_cmd (ctx, err); +} + + + static const char hlp_loadswdb[] = "LOADSWDB [--force]\n" "\n" @@ -2785,13 +2813,14 @@ static const char hlp_getinfo[] = "Multi purpose command to return certain information. \n" "Supported values of WHAT are:\n" "\n" - "version - Return the version of the program.\n" - "pid - Return the process id of the server.\n" + "version - Return the version of the program\n" + "pid - Return the process id of the server\n" "tor - Return OK if running in Tor mode\n" "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" + "socket_name - Return the name of the socket\n" + "session_id - Return the current session_id\n" "workqueue - Inspect the work queue\n" + "stats - Print stats\n" "getenv NAME - Return value of envvar NAME\n"; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) @@ -2860,6 +2889,12 @@ cmd_getinfo (assuan_context_t ctx, char *line) workqueue_dump_queue (ctrl); err = 0; } + else if (!strcmp (line, "stats")) + { + cert_cache_print_stats (ctrl); + domaininfo_print_stats (ctrl); + err = 0; + } else if (!strncmp (line, "getenv", 6) && (line[6] == ' ' || line[6] == '\t' || !line[6])) { @@ -2959,6 +2994,7 @@ register_commands (assuan_context_t ctx) { "KS_GET", cmd_ks_get, hlp_ks_get }, { "KS_FETCH", cmd_ks_fetch, hlp_ks_fetch }, { "KS_PUT", cmd_ks_put, hlp_ks_put }, + { "AD_QUERY", cmd_ad_query, hlp_ad_query }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "LOADSWDB", cmd_loadswdb, hlp_loadswdb }, { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr }, @@ -3218,7 +3254,8 @@ dirmngr_status_help (ctrl_t ctrl, const char *text) /* Print a help status line using a printf like format. The function - * splits text at LFs. */ + * splits text at LFs. With CTRL beeing NULL, the function behaves + * like log_info. */ gpg_error_t dirmngr_status_helpf (ctrl_t ctrl, const char *format, ...) { @@ -3227,12 +3264,20 @@ dirmngr_status_helpf (ctrl_t ctrl, const char *format, ...) char *buf; va_start (arg_ptr, format); - buf = es_vbsprintf (format, arg_ptr); - err = buf? 0 : gpg_error_from_syserror (); + if (ctrl) + { + buf = es_vbsprintf (format, arg_ptr); + err = buf? 0 : gpg_error_from_syserror (); + if (!err) + err = dirmngr_status_help (ctrl, buf); + es_free (buf); + } + else + { + log_logv (GPGRT_LOGLVL_INFO, format, arg_ptr); + err = 0; + } va_end (arg_ptr); - if (!err) - err = dirmngr_status_help (ctrl, buf); - es_free (buf); return err; } diff --git a/dirmngr/validate.c b/dirmngr/validate.c index 399cca3a4..02db3c270 100644 --- a/dirmngr/validate.c +++ b/dirmngr/validate.c @@ -255,6 +255,7 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) int any_revoked = 0; int any_no_crl = 0; int any_crl_too_old = 0; + int any_not_trusted = 0; chain_item_t ci; log_assert (ctrl->check_revocations_nest_level >= 0); @@ -266,7 +267,8 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) return gpg_error(GPG_ERR_BAD_CERT_CHAIN); } ctrl->check_revocations_nest_level++; - + if (opt.verbose) + log_info ("[%d] start checking CRLs\n", ctrl->check_revocations_nest_level); for (ci=chain; ci; ci = ci->next) { @@ -293,17 +295,19 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) if (!err) err = crl_cache_cert_isvalid (ctrl, ci->cert, 0); } + if (opt.verbose) + log_info ("[%d] result of checking this CRL: %s\n", + ctrl->check_revocations_nest_level, gpg_strerror (err)); switch (gpg_err_code (err)) { case 0: err = 0; break; case GPG_ERR_CERT_REVOKED: any_revoked = 1; err = 0; break; case GPG_ERR_NO_CRL_KNOWN: any_no_crl = 1; err = 0; break; + case GPG_ERR_NOT_TRUSTED: any_not_trusted = 1; err = 0; break; case GPG_ERR_CRL_TOO_OLD: any_crl_too_old = 1; err = 0; break; default: break; } } - ctrl->check_revocations_nest_level--; - if (err) ; @@ -311,10 +315,16 @@ check_revocations (ctrl_t ctrl, chain_item_t chain) err = gpg_error (GPG_ERR_CERT_REVOKED); else if (any_no_crl) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); + else if (any_not_trusted) + err = gpg_error (GPG_ERR_NOT_TRUSTED); else if (any_crl_too_old) err = gpg_error (GPG_ERR_CRL_TOO_OLD); else err = 0; + if (opt.verbose) + log_info ("[%d] result of checking all CRLs: %s\n", + ctrl->check_revocations_nest_level, gpg_strerror (err)); + ctrl->check_revocations_nest_level--; return err; } diff --git a/dirmngr/workqueue.c b/dirmngr/workqueue.c index 2974f5d08..dcac48024 100644 --- a/dirmngr/workqueue.c +++ b/dirmngr/workqueue.c @@ -59,7 +59,7 @@ workqueue_dump_queue (ctrl_t ctrl) wqitem_t item; unsigned int count; - /* Temporarily detach the entiere workqueue so that other threads don't + /* Temporarily detach the entire workqueue so that other threads don't * get into our way. */ saved_workqueue = workqueue; workqueue = NULL; @@ -74,8 +74,8 @@ workqueue_dump_queue (ctrl_t ctrl) item->func? item->func (NULL, NULL): "nop", item->args, strlen (item->args) > 100? "[...]":""); - /* Restore then workqueue. Actually we append the saved queue do a - * possibly updated workqueue. */ + /* Restore the workqueue. Actually we append the saved queue to + * handle a possibly updated workqueue. */ if (!(item=workqueue)) workqueue = saved_workqueue; else |