diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h index 9c26c09e6..b27b8e6fb 100644 --- a/dirmngr/dirmngr.h +++ b/dirmngr/dirmngr.h @@ -1,268 +1,269 @@ /* dirmngr.h - Common definitions for the dirmngr * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2004, 2015 g10 Code GmbH * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0+ */ #ifndef DIRMNGR_H #define DIRMNGR_H #include "./dirmngr-err.h" #define map_assuan_err(a) \ map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) #include #include #include #include "../common/util.h" #include "../common/membuf.h" #include "../common/sysutils.h" /* (gnupg_fd_t) */ #include "../common/asshelp.h" /* (assuan_context_t) */ #include "../common/i18n.h" #include "dirmngr-status.h" #include "http.h" /* (parsed_uri_t) */ /* This objects keeps information about a particular LDAP server and is used as item of a single linked list of servers. */ struct ldap_server_s { struct ldap_server_s* next; char *host; int port; char *user; char *pass; char *base; + unsigned int use_ldaps:1; }; typedef struct ldap_server_s *ldap_server_t; /* This objects is used to build a list of URI consisting of the original and the parsed URI. */ struct uri_item_s { struct uri_item_s *next; parsed_uri_t parsed_uri; /* The broken down URI. */ char uri[1]; /* The original URI. */ }; typedef struct uri_item_s *uri_item_t; /* A list of fingerprints. */ struct fingerprint_list_s; typedef struct fingerprint_list_s *fingerprint_list_t; struct fingerprint_list_s { fingerprint_list_t next; char hexfpr[20+20+1]; }; /* A large struct named "opt" to keep global flags. */ struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int dry_run; /* don't change any persistent data */ int batch; /* batch mode */ const char *homedir_cache; /* Dir for cache files (/var/cache/dirmngr). */ char *config_filename; /* Name of a config file, which will be reread on a HUP if it is not NULL. */ char *ldap_wrapper_program; /* Override value for the LDAP wrapper program. */ char *http_wrapper_program; /* Override value for the HTTP wrapper program. */ int running_detached; /* We are running in detached mode. */ int allow_version_check; /* --allow-version-check is active. */ int force; /* Force loading outdated CRLs. */ unsigned int connect_timeout; /* Timeout for connect. */ unsigned int connect_quick_timeout; /* Shorter timeout for connect. */ int disable_http; /* Do not use HTTP at all. */ int disable_ldap; /* Do not use LDAP at all. */ int disable_ipv4; /* Do not use legacy IP addresses. */ int disable_ipv6; /* Do not use standard IP addresses. */ int honor_http_proxy; /* Honor the http_proxy env variable. */ const char *http_proxy; /* The default HTTP proxy. */ const char *ldap_proxy; /* Use given LDAP proxy. */ int only_ldap_proxy; /* Only use the LDAP proxy; no fallback. */ int ignore_http_dp; /* Ignore HTTP CRL distribution points. */ int ignore_ldap_dp; /* Ignore LDAP CRL distribution points. */ int ignore_ocsp_service_url; /* Ignore OCSP service URLs as given in the certificate. */ /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One OID per string. */ strlist_t ignored_cert_extensions; int allow_ocsp; /* Allow using OCSP. */ int max_replies; unsigned int ldaptimeout; ldap_server_t ldapservers; int add_new_ldapservers; const char *ocsp_responder; /* Standard OCSP responder's URL. */ fingerprint_list_t ocsp_signer; /* The list of fingerprints with allowed standard OCSP signer certificates. */ unsigned int ocsp_max_clock_skew; /* Allowed seconds of clocks skew. */ unsigned int ocsp_max_period; /* Seconds a response is at maximum considered valid after thisUpdate. */ unsigned int ocsp_current_period; /* Seconds a response is considered current after nextUpdate. */ strlist_t keyserver; /* List of default keyservers. */ } opt; #define DBG_X509_VALUE 1 /* debug x.509 parsing */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_DNS_VALUE 16 /* debug DNS calls. */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_NETWORK_VALUE 2048 /* debug network I/O. */ #define DBG_LOOKUP_VALUE 8192 /* debug lookup details */ #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_DNS (opt.debug & DBG_DNS_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) /* A simple list of certificate references. FIXME: Better use certlist_t also for references (Store NULL at .cert) */ struct cert_ref_s { struct cert_ref_s *next; unsigned char fpr[20]; }; typedef struct cert_ref_s *cert_ref_t; /* Forward references; access only through server.c. */ struct server_local_s; #if SIZEOF_UNSIGNED_LONG == 8 # define SERVER_CONTROL_MAGIC 0x6469726d6e677220 #else # define SERVER_CONTROL_MAGIC 0x6469726d #endif /* Connection control structure. */ struct server_control_s { unsigned long magic;/* Always has SERVER_CONTROL_MAGIC. */ int refcount; /* Count additional references to this object. */ int no_server; /* We are not running under server control. */ int status_fd; /* Only for non-server mode. */ struct server_local_s *server_local; int force_crl_refresh; /* Always load a fresh CRL. */ int check_revocations_nest_level; /* Internal to check_revovations. */ cert_ref_t ocsp_certs; /* Certificates from the current OCSP response. */ int audit_events; /* Send audit events to client. */ char *http_proxy; /* The used http_proxy or NULL. */ unsigned int timeout; /* Timeout for connect calls in ms. */ unsigned int http_no_crl:1; /* Do not check CRLs for https. */ }; /*-- dirmngr.c --*/ void dirmngr_exit( int ); /* Wrapper for exit() */ void dirmngr_init_default_ctrl (ctrl_t ctrl); void dirmngr_deinit_default_ctrl (ctrl_t ctrl); void dirmngr_sighup_action (void); const char* dirmngr_get_current_socket_name (void); int dirmngr_use_tor (void); /*-- Various housekeeping functions. --*/ void ks_hkp_housekeeping (time_t curtime); void ks_hkp_reload (void); void ks_hkp_init (void); /*-- server.c --*/ 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); ksba_cert_t get_cert_local_ski (ctrl_t ctrl, const char *name, ksba_sexp_t keyid); gpg_error_t get_istrusted_from_client (ctrl_t ctrl, const char *hexfpr); int dirmngr_assuan_log_monitor (assuan_context_t ctx, unsigned int cat, const char *msg); void start_command_handler (gnupg_fd_t fd, unsigned int session_id); gpg_error_t dirmngr_tick (ctrl_t ctrl); /*-- http-ntbtls.c --*/ /* Note that we don't use a callback for gnutls. */ gpg_error_t gnupg_http_tls_verify_cb (void *opaque, http_t http, http_session_t session, unsigned int flags, void *tls_context); /*-- loadswdb.c --*/ gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force); /*-- domaininfo.c --*/ void domaininfo_print_stats (void); 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); void domaininfo_set_wkd_not_supported (const char *domain); void domaininfo_set_wkd_not_found (const char *domain); /*-- workqueue.c --*/ typedef const char *(*wqtask_t)(ctrl_t ctrl, const char *args); void workqueue_dump_queue (ctrl_t ctrl); gpg_error_t workqueue_add_task (wqtask_t func, const char *args, unsigned int session_id, int need_network); void workqueue_run_global_tasks (ctrl_t ctrl, int with_network); void workqueue_run_post_session_tasks (unsigned int session_id); #endif /*DIRMNGR_H*/ diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c index dd7e4bda5..72d88b9be 100644 --- a/dirmngr/dirmngr_ldap.c +++ b/dirmngr/dirmngr_ldap.c @@ -1,771 +1,813 @@ /* dirmngr-ldap.c - The LDAP helper for dirmngr. * Copyright (C) 2004 g10 Code GmbH * Copyright (C) 2010 Free Software Foundation, Inc. * * 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 . */ #include #include #include #include #include #include #ifdef HAVE_SIGNAL_H # include #endif #include #include #include #ifndef USE_LDAPWRAPPER # include #endif #ifdef HAVE_W32_SYSTEM # include # include # include # include # include "ldap-url.h" #else /* For OpenLDAP, to enable the API that we're using. */ # define LDAP_DEPRECATED 1 # include #endif #include #include "../common/logging.h" #include "../common/argparse.h" #include "../common/stringhelp.h" #include "../common/mischelp.h" #include "../common/strlist.h" #include "../common/i18n.h" #include "../common/util.h" #include "../common/init.h" /* With the ldap wrapper, there is no need for the npth_unprotect and leave functions; thus we redefine them to nops. If we are not using the ldap wrapper process we need to include the prototype for our module's main function. */ #ifdef USE_LDAPWRAPPER static void npth_unprotect (void) { } static void npth_protect (void) { } #else # include "./ldap-wrapper.h" #endif #ifdef HAVE_W32CE_SYSTEM # include "w32-ldap-help.h" # define my_ldap_init(a,b) \ _dirmngr_ldap_init ((a), (b)) # define my_ldap_simple_bind_s(a,b,c) \ _dirmngr_ldap_simple_bind_s ((a),(b),(c)) # define my_ldap_search_st(a,b,c,d,e,f,g,h) \ _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h)) # define my_ldap_first_attribute(a,b,c) \ _dirmngr_ldap_first_attribute ((a),(b),(c)) # define my_ldap_next_attribute(a,b,c) \ _dirmngr_ldap_next_attribute ((a),(b),(c)) # define my_ldap_get_values_len(a,b,c) \ _dirmngr_ldap_get_values_len ((a),(b),(c)) # define my_ldap_free_attr(a) \ xfree ((a)) #else # define my_ldap_init(a,b) ldap_init ((a), (b)) # define my_ldap_simple_bind_s(a,b,c) ldap_simple_bind_s ((a), (b), (c)) # define my_ldap_search_st(a,b,c,d,e,f,g,h) \ ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h)) # define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c)) # define my_ldap_next_attribute(a,b,c) ldap_next_attribute ((a),(b),(c)) # define my_ldap_get_values_len(a,b,c) ldap_get_values_len ((a),(b),(c)) # define my_ldap_free_attr(a) ldap_memfree ((a)) #endif #ifdef HAVE_W32_SYSTEM typedef LDAP_TIMEVAL my_ldap_timeval_t; #else typedef struct timeval my_ldap_timeval_t; #endif #define DEFAULT_LDAP_TIMEOUT 15 /* Arbitrary long timeout. */ /* Constants for the options. */ enum { oQuiet = 'q', oVerbose = 'v', oTimeout = 500, oMulti, oProxy, oHost, oPort, oUser, oPass, oEnvPass, oDN, oFilter, oAttr, + oTls, oOnlySearchTimeout, oLogWithPID }; /* The list of options as used by the argparse.c code. */ static ARGPARSE_OPTS opts[] = { { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, { oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")}, { oMulti, "multi", 0, N_("return all values in" " a record oriented format")}, { oProxy, "proxy", 2, N_("|NAME|ignore host part and connect through NAME")}, + { oTls, "tls", 0, N_("force a TLS connection")}, { oHost, "host", 2, N_("|NAME|connect to host NAME")}, { oPort, "port", 1, N_("|N|connect to port N")}, { oUser, "user", 2, N_("|NAME|use user NAME for authentication")}, { oPass, "pass", 2, N_("|PASS|use password PASS" " for authentication")}, { oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")}, { oDN, "dn", 2, N_("|STRING|query DN STRING")}, { oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")}, { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")}, { oOnlySearchTimeout, "only-search-timeout", 0, "@"}, { oLogWithPID,"log-with-pid", 0, "@"}, ARGPARSE_end () }; /* A structure with module options. This is not a static variable because if we are not build as a standalone binary, each thread using this module needs to handle its own values. */ struct my_opt_s { int quiet; int verbose; my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */ unsigned int alarm_timeout; /* And for the alarm based timeout. */ int multi; + int force_tls; estream_t outstream; /* Send output to this stream. */ /* Note that we can't use const for the strings because ldap_* are not defined that way. */ char *proxy; /* Host and Port override. */ char *user; /* Authentication user. */ char *pass; /* Authentication password. */ char *host; /* Override host. */ int port; /* Override port. */ char *dn; /* Override DN. */ char *filter;/* Override filter. */ char *attr; /* Override attribute. */ }; typedef struct my_opt_s *my_opt_t; /* Prototypes. */ #ifndef HAVE_W32_SYSTEM static void catch_alarm (int dummy); #endif static int process_url (my_opt_t myopt, const char *url); /* Function called by argparse.c to display information. */ #ifdef USE_LDAPWRAPPER static const char * my_strusage (int level) { const char *p; switch(level) { case 11: p = "dirmngr_ldap (@GNUPG@)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 49: p = PACKAGE_BUGREPORT; break; case 1: case 40: p = _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n"); break; case 41: p = _("Syntax: dirmngr_ldap [options] [URL]\n" "Internal LDAP helper for Dirmngr\n" "Interface and options may change without notice\n"); break; default: p = NULL; } return p; } #endif /*!USE_LDAPWRAPPER*/ int #ifdef USE_LDAPWRAPPER main (int argc, char **argv) #else ldap_wrapper_main (char **argv, estream_t outstream) #endif { #ifndef USE_LDAPWRAPPER int argc; #endif ARGPARSE_ARGS pargs; int any_err = 0; char *p; int only_search_timeout = 0; struct my_opt_s my_opt_buffer; my_opt_t myopt = &my_opt_buffer; char *malloced_buffer1 = NULL; memset (&my_opt_buffer, 0, sizeof my_opt_buffer); early_system_init (); #ifdef USE_LDAPWRAPPER set_strusage (my_strusage); log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX); /* Setup I18N and common subsystems. */ i18n_init(); init_common_subsystems (&argc, &argv); es_set_binary (es_stdout); myopt->outstream = es_stdout; #else /*!USE_LDAPWRAPPER*/ myopt->outstream = outstream; for (argc=0; argv[argc]; argc++) ; #endif /*!USE_LDAPWRAPPER*/ /* LDAP defaults */ myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT; myopt->timeout.tv_usec = 0; myopt->alarm_timeout = 0; /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1; /* Do not remove the args. */ while (arg_parse (&pargs, opts) ) { switch (pargs.r_opt) { case oVerbose: myopt->verbose++; break; case oQuiet: myopt->quiet++; break; case oTimeout: myopt->timeout.tv_sec = pargs.r.ret_int; myopt->timeout.tv_usec = 0; myopt->alarm_timeout = pargs.r.ret_int; break; case oOnlySearchTimeout: only_search_timeout = 1; break; case oMulti: myopt->multi = 1; break; case oUser: myopt->user = pargs.r.ret_str; break; case oPass: myopt->pass = pargs.r.ret_str; break; case oEnvPass: myopt->pass = getenv ("DIRMNGR_LDAP_PASS"); break; case oProxy: myopt->proxy = pargs.r.ret_str; break; + case oTls: myopt->force_tls = 1; break; case oHost: myopt->host = pargs.r.ret_str; break; case oPort: myopt->port = pargs.r.ret_int; break; case oDN: myopt->dn = pargs.r.ret_str; break; case oFilter: myopt->filter = pargs.r.ret_str; break; case oAttr: myopt->attr = pargs.r.ret_str; break; case oLogWithPID: { unsigned int oldflags; log_get_prefix (&oldflags); log_set_prefix (NULL, oldflags | GPGRT_LOG_WITH_PID); } break; default : #ifdef USE_LDAPWRAPPER pargs.err = ARGPARSE_PRINT_ERROR; #else pargs.err = ARGPARSE_PRINT_WARNING; /* No exit() please. */ #endif break; } } if (only_search_timeout) myopt->alarm_timeout = 0; if (myopt->proxy) { malloced_buffer1 = xtrystrdup (myopt->proxy); if (!malloced_buffer1) { log_error ("error copying string: %s\n", strerror (errno)); return 1; } myopt->host = malloced_buffer1; p = strchr (myopt->host, ':'); if (p) { *p++ = 0; myopt->port = atoi (p); } if (!myopt->port) myopt->port = 389; /* make sure ports gets overridden. */ } if (myopt->port < 0 || myopt->port > 65535) log_error (_("invalid port number %d\n"), myopt->port); #ifdef USE_LDAPWRAPPER if (log_get_errorcount (0)) exit (2); if (argc < 1) usage (1); #else /* All passed arguments should be fine in this case. */ log_assert (argc); #endif #ifdef USE_LDAPWRAPPER if (myopt->alarm_timeout) { #ifndef HAVE_W32_SYSTEM # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION) struct sigaction act; act.sa_handler = catch_alarm; sigemptyset (&act.sa_mask); act.sa_flags = 0; if (sigaction (SIGALRM,&act,NULL)) # else if (signal (SIGALRM, catch_alarm) == SIG_ERR) # endif log_fatal ("unable to register timeout handler\n"); #endif } #endif /*USE_LDAPWRAPPER*/ for (; argc; argc--, argv++) if (process_url (myopt, *argv)) any_err = 1; xfree (malloced_buffer1); return any_err; } #ifndef HAVE_W32_SYSTEM static void catch_alarm (int dummy) { (void)dummy; _exit (10); } #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) { if (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 resettable timer. */ timer = CreateWaitableTimer (NULL, TRUE, NULL); /* Initially 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 } } /* Helper for fetch_ldap(). */ static int print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr) { LDAPMessage *item; int any = 0; for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect (); item; npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ()) { BerElement *berctx; char *attr; if (myopt->verbose > 1) log_info (_("scanning result for attribute '%s'\n"), want_attr? want_attr : "[all]"); if (myopt->multi) { /* Write item marker. */ if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1) { log_error (_("error writing to stdout: %s\n"), strerror (errno)); return -1; } } for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx), npth_protect (); attr; npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx), npth_protect ()) { struct berval **values; int idx; if (myopt->verbose > 1) log_info (_(" available attribute '%s'\n"), attr); set_timeout (myopt); /* I case we want only one attribute we do a case insensitive compare without the optional extension (i.e. ";binary"). Case insensitive is not really correct but the best we can do. */ if (want_attr) { char *cp1, *cp2; int cmpres; cp1 = strchr (want_attr, ';'); if (cp1) *cp1 = 0; cp2 = strchr (attr, ';'); if (cp2) *cp2 = 0; cmpres = ascii_strcasecmp (want_attr, attr); if (cp1) *cp1 = ';'; if (cp2) *cp2 = ';'; if (cmpres) { my_ldap_free_attr (attr); continue; /* Not found: Try next attribute. */ } } npth_unprotect (); values = my_ldap_get_values_len (ld, item, attr); npth_protect (); if (!values) { if (myopt->verbose) log_info (_("attribute '%s' not found\n"), attr); my_ldap_free_attr (attr); continue; } if (myopt->verbose) { log_info (_("found attribute '%s'\n"), attr); if (myopt->verbose > 1) for (idx=0; values[idx]; idx++) log_info (" length[%d]=%d\n", idx, (int)values[0]->bv_len); } if (myopt->multi) { /* Write attribute marker. */ unsigned char tmp[5]; size_t n = strlen (attr); tmp[0] = 'A'; tmp[1] = (n >> 24); tmp[2] = (n >> 16); tmp[3] = (n >> 8); tmp[4] = (n); if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1 || es_fwrite (attr, n, 1, myopt->outstream) != 1) { log_error (_("error writing to stdout: %s\n"), strerror (errno)); ldap_value_free_len (values); my_ldap_free_attr (attr); ber_free (berctx, 0); return -1; } } for (idx=0; values[idx]; idx++) { if (myopt->multi) { /* Write value marker. */ unsigned char tmp[5]; size_t n = values[0]->bv_len; tmp[0] = 'V'; tmp[1] = (n >> 24); tmp[2] = (n >> 16); tmp[3] = (n >> 8); tmp[4] = (n); if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1) { log_error (_("error writing to stdout: %s\n"), strerror (errno)); ldap_value_free_len (values); my_ldap_free_attr (attr); ber_free (berctx, 0); return -1; } } if (es_fwrite (values[0]->bv_val, values[0]->bv_len, 1, myopt->outstream) != 1) { log_error (_("error writing to stdout: %s\n"), strerror (errno)); ldap_value_free_len (values); my_ldap_free_attr (attr); ber_free (berctx, 0); return -1; } any = 1; if (!myopt->multi) break; /* Print only the first value. */ } ldap_value_free_len (values); my_ldap_free_attr (attr); if (want_attr || !myopt->multi) break; /* We only want to return the first attribute. */ } ber_free (berctx, 0); } if (myopt->verbose > 1 && any) log_info ("result has been printed\n"); return any?0:-1; } /* Helper for the URL based LDAP query. */ static int fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp) { LDAP *ld; LDAPMessage *msg; int rc = 0; char *host, *dn, *filter, *attrs[2], *attr; int port; int ret; host = myopt->host? myopt->host : ludp->lud_host; port = myopt->port? myopt->port : ludp->lud_port; dn = myopt->dn? myopt->dn : ludp->lud_dn; filter = myopt->filter? myopt->filter : ludp->lud_filter; attrs[0] = myopt->attr? myopt->attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL; attrs[1] = NULL; attr = attrs[0]; - if (!port) + if (!port && myopt->force_tls) + port = 636; + else if (!port) port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389; if (myopt->verbose) { log_info (_("processing url '%s'\n"), url); + if (myopt->force_tls) + log_info ("forcing tls\n"); + else + log_info ("not forcing tls\n"); + if (myopt->user) log_info (_(" user '%s'\n"), myopt->user); if (myopt->pass) log_info (_(" pass '%s'\n"), *myopt->pass?"*****":""); if (host) log_info (_(" host '%s'\n"), host); log_info (_(" port %d\n"), port); if (dn) log_info (_(" DN '%s'\n"), dn); if (filter) log_info (_(" filter '%s'\n"), filter); if (myopt->multi && !myopt->attr && ludp->lud_attrs) { int i; for (i=0; ludp->lud_attrs[i]; i++) log_info (_(" attr '%s'\n"), ludp->lud_attrs[i]); } else if (attr) log_info (_(" attr '%s'\n"), attr); } if (!host || !*host) { log_error (_("no host name in '%s'\n"), url); return -1; } if (!myopt->multi && !attr) { log_error (_("no attribute given for query '%s'\n"), url); return -1; } if (!myopt->multi && !myopt->attr && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1]) log_info (_("WARNING: using first attribute only\n")); - set_timeout (myopt); - npth_unprotect (); - ld = my_ldap_init (host, port); - npth_protect (); - if (!ld) + + if (myopt->force_tls + || (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))) { - log_error (_("LDAP init to '%s:%d' failed: %s\n"), - host, port, strerror (errno)); - return -1; + char *uri; + + uri = xtryasprintf ("ldaps://%s:%d", host, port); + if (!uri) + { + log_error (_("error allocating memory: %s\n"), + gpg_strerror (gpg_error_from_syserror ())); + return -1; + } + ret = ldap_initialize (&ld, uri); + if (ret) + { + log_error (_("LDAP init to '%s' failed: %s\n"), + uri, ldap_err2string (ret)); + xfree (uri); + return -1; + } + else if (myopt->verbose) + log_info (_("LDAP init to '%s' done\n"), uri); + xfree (uri); + } + else + { + /* Keep the old way so to avoid regressions. Eventually we + * should really consider the supplied scheme and use only + * ldap_initialize. */ + npth_unprotect (); + ld = my_ldap_init (host, port); + npth_protect (); + if (!ld) + { + log_error (_("LDAP init to '%s:%d' failed: %s\n"), + host, port, strerror (errno)); + return -1; + } } + npth_unprotect (); /* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */ ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass); npth_protect (); #ifdef LDAP_VERSION3 if (ret == LDAP_PROTOCOL_ERROR) { /* Protocol error could mean that the server only supports v3. */ int version = LDAP_VERSION3; if (myopt->verbose) log_info ("protocol error; retrying bind with v3 protocol\n"); npth_unprotect (); ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version); ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass); npth_protect (); } #endif if (ret) { log_error (_("binding to '%s:%d' failed: %s\n"), host, port, ldap_err2string (ret)); ldap_unbind (ld); return -1; } set_timeout (myopt); npth_unprotect (); rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter, myopt->multi && !myopt->attr && ludp->lud_attrs? ludp->lud_attrs:attrs, 0, &myopt->timeout, &msg); npth_protect (); if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi) { if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1) { log_error (_("error writing to stdout: %s\n"), strerror (errno)); return -1; } } else if (rc) { #ifdef HAVE_W32CE_SYSTEM log_error ("searching '%s' failed: %d\n", url, rc); #else log_error (_("searching '%s' failed: %s\n"), url, ldap_err2string (rc)); #endif if (rc != LDAP_NO_SUCH_OBJECT) { /* FIXME: Need deinit (ld)? */ /* Hmmm: Do we need to released MSG in case of an error? */ return -1; } } rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr); ldap_msgfree (msg); ldap_unbind (ld); return rc; } /* Main processing. Take the URL and run the LDAP query. The result is printed to stdout, errors are logged to the log stream. */ static int process_url (my_opt_t myopt, const char *url) { int rc; LDAPURLDesc *ludp = NULL; if (!ldap_is_ldap_url (url)) { log_error (_("'%s' is not an LDAP URL\n"), url); return -1; } if (ldap_url_parse (url, &ludp)) { log_error (_("'%s' is an invalid LDAP URL\n"), url); return -1; } rc = fetch_ldap (myopt, url, ludp); ldap_free_urldesc (ludp); return rc; } diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c index a04bb97a2..ad6b0889b 100644 --- a/dirmngr/ldap.c +++ b/dirmngr/ldap.c @@ -1,874 +1,876 @@ /* ldap.c - LDAP access * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH * * This file is part of DirMngr. * * DirMngr 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 2 of the License, or * (at your option) any later version. * * DirMngr 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include "dirmngr.h" #include "../common/exechelp.h" #include "crlfetch.h" #include "ldapserver.h" #include "misc.h" #include "ldap-wrapper.h" #include "../common/host2net.h" #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "01234567890" \ "$-_.+!*'()," #define USERCERTIFICATE "userCertificate" #define CACERTIFICATE "caCertificate" #define X509CACERT "x509caCert" #define USERSMIMECERTIFICATE "userSMIMECertificate" /* Definition for the context of the cert fetch functions. */ struct cert_fetch_context_s { ksba_reader_t reader; /* The reader used (shallow copy). */ unsigned char *tmpbuf; /* Helper buffer. */ size_t tmpbufsize; /* Allocated size of tmpbuf. */ int truncated; /* Flag to indicate a truncated output. */ }; /* Add HOST and PORT to our list of LDAP servers. Fixme: We should better use an extra list of servers. */ static void add_server_to_servers (const char *host, int port) { ldap_server_t server; ldap_server_t last = NULL; const char *s; if (!port) port = 389; for (server=opt.ldapservers; server; server = server->next) { if (!strcmp (server->host, host) && server->port == port) return; /* already in list... */ last = server; } /* We assume that the host names are all supplied by our configuration files and thus are sane. To keep this assumption we must reject all invalid host names. */ for (s=host; *s; s++) if (!strchr ("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "01234567890.-", *s)) { log_error (_("invalid char 0x%02x in host name - not added\n"), *s); return; } log_info (_("adding '%s:%d' to the ldap server list\n"), host, port); server = xtrycalloc (1, sizeof *s); if (!server) log_error (_("malloc failed: %s\n"), strerror (errno)); else { server->host = xstrdup (host); server->port = port; if (last) last->next = server; else opt.ldapservers = server; } } /* Perform an LDAP query. Returns an gpg error code or 0 on success. The function returns a new reader object at READER. */ static gpg_error_t run_ldap_wrapper (ctrl_t ctrl, int ignore_timeout, int multi_mode, const char *proxy, const char *host, int port, const char *user, const char *pass, const char *dn, const char *filter, const char *attr, const char *url, ksba_reader_t *reader) { const char *argv[40]; int argc; char portbuf[30], timeoutbuf[30]; *reader = NULL; argc = 0; if (pass) /* Note, that the password must be the first item. */ { argv[argc++] = "--pass"; argv[argc++] = pass; } if (DBG_LOOKUP) argv[argc++] = "-vv"; else if (DBG_EXTPROG) argv[argc++] = "-v"; argv[argc++] = "--log-with-pid"; if (multi_mode) argv[argc++] = "--multi"; if (opt.ldaptimeout) { sprintf (timeoutbuf, "%u", opt.ldaptimeout); argv[argc++] = "--timeout"; argv[argc++] = timeoutbuf; if (ignore_timeout) argv[argc++] = "--only-search-timeout"; } if (proxy) { argv[argc++] = "--proxy"; argv[argc++] = proxy; } if (host) { argv[argc++] = "--host"; argv[argc++] = host; } if (port) { sprintf (portbuf, "%d", port); argv[argc++] = "--port"; argv[argc++] = portbuf; } if (user) { argv[argc++] = "--user"; argv[argc++] = user; } if (dn) { argv[argc++] = "--dn"; argv[argc++] = dn; } if (filter) { argv[argc++] = "--filter"; argv[argc++] = filter; } if (attr) { argv[argc++] = "--attr"; argv[argc++] = attr; } argv[argc++] = url? url : "ldap://"; argv[argc] = NULL; return ldap_wrapper (ctrl, reader, argv); } /* Perform a LDAP query using a given URL. On success a new ksba reader is returned. If HOST or PORT are not 0, they are used to override the values from the URL. */ gpg_error_t url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port, ksba_reader_t *reader) { gpg_error_t err; err = run_ldap_wrapper (ctrl, 1, /* Ignore explicit timeout because CRLs might be very large. */ 0, opt.ldap_proxy, host, port, NULL, NULL, NULL, NULL, NULL, url, reader); /* FIXME: This option might be used for DoS attacks. Because it will enlarge the list of servers to consult without a limit and all LDAP queries w/o a host are will then try each host in turn. */ if (!err && opt.add_new_ldapservers && !opt.ldap_proxy) { if (host) add_server_to_servers (host, port); else if (url) { char *tmp = host_and_port_from_url (url, &port); if (tmp) { add_server_to_servers (tmp, port); xfree (tmp); } } } /* If the lookup failed and we are not only using the proxy, we try again using our default list of servers. */ if (err && !(opt.ldap_proxy && opt.only_ldap_proxy)) { struct ldapserver_iter iter; if (DBG_LOOKUP) log_debug ("no hostname in URL or query failed; " "trying all default hostnames\n"); for (ldapserver_iter_begin (&iter, ctrl); err && ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 0, NULL, server->host, server->port, NULL, NULL, NULL, NULL, NULL, url, reader); if (!err) break; } } return err; } /* Perform an LDAP query on all configured servers. On error the error code of the last try is returned. */ gpg_error_t attr_fetch_ldap (ctrl_t ctrl, const char *dn, const char *attr, ksba_reader_t *reader) { gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION); struct ldapserver_iter iter; *reader = NULL; /* FIXME; we might want to look at the Base SN to try matching servers first. */ for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 0, opt.ldap_proxy, server->host, server->port, server->user, server->pass, dn, "objectClass=*", attr, NULL, reader); if (!err) break; /* Probably found a result. Ready. */ } return err; } /* Parse PATTERN and return a new strlist to be used for the actual LDAP query. Bit 0 of the flags field is set if that pattern is actually a base specification. Caller must release the returned strlist. NULL is returned on error. * Possible patterns: * * KeyID * Fingerprint * OpenPGP userid * x Email address Indicated by a left angle bracket. * Exact word match in user id or subj. name * x Subj. DN indicated bu a leading slash * Issuer DN * Serial number + subj. DN * x Substring match indicated by a leading '*; is also the default. */ strlist_t parse_one_pattern (const char *pattern) { strlist_t result = NULL; char *p; switch (*pattern) { case '<': /* Email. */ { pattern++; result = xmalloc (sizeof *result + 5 + strlen (pattern)); result->next = NULL; result->flags = 0; p = stpcpy (stpcpy (result->d, "mail="), pattern); if (p[-1] == '>') *--p = 0; if (!*result->d) /* Error. */ { xfree (result); result = NULL; } break; } case '/': /* Subject DN. */ pattern++; if (*pattern) { result = xmalloc (sizeof *result + strlen (pattern)); result->next = NULL; result->flags = 1; /* Base spec. */ strcpy (result->d, pattern); } break; case '#': /* Issuer DN. */ pattern++; if (*pattern == '/') /* Just issuer DN. */ { pattern++; } else /* Serial number + issuer DN */ { } break; case '*': pattern++; /* fall through */ default: /* Take as substring match. */ { const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))"; if (*pattern) { result = xmalloc (sizeof *result + strlen (format) + 3 * strlen (pattern)); result->next = NULL; result->flags = 0; sprintf (result->d, format, pattern, pattern, pattern); } } break; } return result; } /* Take the string STRING and escape it according to the URL rules. Return a newly allocated string. */ static char * escape4url (const char *string) { const char *s; char *buf, *p; size_t n; if (!string) string = ""; for (s=string,n=0; *s; s++) if (strchr (UNENCODED_URL_CHARS, *s)) n++; else n += 3; buf = malloc (n+1); if (!buf) return NULL; for (s=string,p=buf; *s; s++) if (strchr (UNENCODED_URL_CHARS, *s)) *p++ = *s; else { sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } *p = 0; return buf; } /* Create a LDAP URL from DN and FILTER and return it in URL. We don't need the host and port because this will be specified using the override options. */ static gpg_error_t make_url (char **url, const char *dn, const char *filter) { gpg_error_t err; char *u_dn, *u_filter; char const attrs[] = (USERCERTIFICATE "," /* USERSMIMECERTIFICATE "," */ CACERTIFICATE "," X509CACERT ); *url = NULL; u_dn = escape4url (dn); if (!u_dn) return gpg_error_from_errno (errno); u_filter = escape4url (filter); if (!u_filter) { err = gpg_error_from_errno (errno); xfree (u_dn); return err; } *url = strconcat ("ldap:///", u_dn, "?", attrs, "?sub?", u_filter, NULL); if (!*url) err = gpg_error_from_syserror (); else err = 0; xfree (u_dn); xfree (u_filter); return err; } /* Prepare an LDAP query to return the attribute ATTR for the DN. All configured default servers are queried until one responds. This function returns an error code or 0 and a CONTEXT on success. */ gpg_error_t start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn, const char *attr) { gpg_error_t err; struct ldapserver_iter iter; *context = xtrycalloc (1, sizeof **context); if (!*context) return gpg_error_from_errno (errno); /* FIXME; we might want to look at the Base SN to try matching servers first. */ err = gpg_error (GPG_ERR_CONFIGURATION); for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter); ldapserver_iter_next (&iter)) { ldap_server_t server = iter.server; err = run_ldap_wrapper (ctrl, 0, 1, opt.ldap_proxy, server->host, server->port, server->user, server->pass, dn, "objectClass=*", attr, NULL, &(*context)->reader); if (!err) break; /* Probably found a result. */ } if (err) { xfree (*context); *context = NULL; } return err; } /* Prepare an LDAP query to return certificates matching PATTERNS using the SERVER. This function returns an error code or 0 and a CONTEXT on success. */ gpg_error_t start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context, strlist_t patterns, const ldap_server_t server) { gpg_error_t err; char *proxy = NULL; char *host = NULL; int port; char *user = NULL; char *pass = NULL; const char *base; char *argv[50]; int argc = 0; int argc_malloced = 0; char portbuf[30], timeoutbuf[30]; - + int use_ldaps = 0; *context = NULL; if (opt.ldap_proxy && !(proxy = xtrystrdup (opt.ldap_proxy))) { err = gpg_error_from_syserror (); goto leave; } if (server) { if (server->host && !(host = xtrystrdup (server->host))) { err = gpg_error_from_syserror (); goto leave; } port = server->port; if (server->user && !(user = xtrystrdup (server->user))) { err = gpg_error_from_syserror (); goto leave; } if (server->pass && !(pass = xtrystrdup (server->pass))) { err = gpg_error_from_syserror (); goto leave; } base = server->base; - + use_ldaps = server->use_ldaps; } else /* Use a default server. */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (!base) base = ""; if (pass) /* Note: Must be the first item. */ { argv[argc++] = "--pass"; argv[argc++] = pass; } if (DBG_LOOKUP) argv[argc++] = "-vv"; else if (DBG_EXTPROG) argv[argc++] = "-v"; argv[argc++] = "--log-with-pid"; argv[argc++] = "--multi"; if (opt.ldaptimeout) { snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout); argv[argc++] = "--timeout"; argv[argc++] = timeoutbuf; } if (opt.ldap_proxy) { argv[argc++] = "--proxy"; argv[argc++] = proxy; } + if (use_ldaps) + argv[argc++] = "--tls"; if (host) { argv[argc++] = "--host"; argv[argc++] = host; } if (port) { snprintf (portbuf, sizeof portbuf, "%d", port); argv[argc++] = "--port"; argv[argc++] = portbuf; } if (user) { argv[argc++] = "--user"; argv[argc++] = user; } /* All entries in argv from this index on are malloc'ed. */ argc_malloced = argc; for (; patterns; patterns = patterns->next) { strlist_t sl; char *url; if (argc >= DIM (argv) - 1) { /* Too many patterns. It does not make sense to allow an arbitrary number of patters because the length of the command line is limited anyway. */ /* fixme: cleanup. */ return gpg_error (GPG_ERR_RESOURCE_LIMIT); } sl = parse_one_pattern (patterns->d); if (!sl) { log_error (_("start_cert_fetch: invalid pattern '%s'\n"), patterns->d); err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } if ((sl->flags & 1)) err = make_url (&url, sl->d, "objectClass=*"); else err = make_url (&url, base, sl->d); free_strlist (sl); if (err) goto leave; argv[argc++] = url; } argv[argc] = NULL; *context = xtrycalloc (1, sizeof **context); if (!*context) { err = gpg_error_from_errno (errno); goto leave; } err = ldap_wrapper (ctrl, &(*context)->reader, (const char**)argv); if (err) { xfree (*context); *context = NULL; } leave: for (; argc_malloced < argc; argc_malloced++) xfree (argv[argc_malloced]); xfree (proxy); xfree (host); xfree (user); xfree (pass); return err; } /* 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) { gpg_error_t err; size_t nread; while (count) { err = ksba_reader_read (reader, buffer, count, &nread); if (err) return err; buffer += nread; count -= nread; } return 0; } /* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no (more) certificates are available or any other error code. GPG_ERR_TRUNCATED may be returned to indicate that the result has been truncated. */ gpg_error_t fetch_next_cert_ldap (cert_fetch_context_t context, unsigned char **value, size_t *valuelen) { gpg_error_t err; unsigned char hdr[5]; char *p, *pend; unsigned long n; int okay = 0; /* int is_cms = 0; */ *value = NULL; *valuelen = 0; err = 0; while (!err) { err = read_buffer (context->reader, hdr, 5); if (err) break; n = buf32_to_ulong (hdr+1); if (*hdr == 'V' && okay) { #if 0 /* That code is not yet ready. */ if (is_cms) { /* The certificate needs to be parsed from CMS data. */ ksba_cms_t cms; ksba_stop_reason_t stopreason; int i; err = ksba_cms_new (&cms); if (err) goto leave; err = ksba_cms_set_reader_writer (cms, context->reader, NULL); if (err) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); goto leave; } do { err = ksba_cms_parse (cms, &stopreason); if (err) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (err)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_error ("userSMIMECertificate is not " "a certs-only message\n"); } while (stopreason != KSBA_SR_READY); for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { check_and_store (ctrl, stats, cert, 0); ksba_cert_release (cert); cert = NULL; } if (!i) log_error ("no certificate found\n"); else any = 1; } else #endif { *value = xtrymalloc (n); if (!*value) return gpg_error_from_errno (errno); *valuelen = n; err = read_buffer (context->reader, *value, n); break; /* Ready or error. */ } } else if (!n && *hdr == 'A') okay = 0; else if (n) { if (n > context->tmpbufsize) { xfree (context->tmpbuf); context->tmpbufsize = 0; context->tmpbuf = xtrymalloc (n+1); if (!context->tmpbuf) return gpg_error_from_errno (errno); context->tmpbufsize = n; } err = read_buffer (context->reader, context->tmpbuf, n); if (err) break; if (*hdr == 'A') { p = context->tmpbuf; p[n] = 0; /*(we allocated one extra byte for this.)*/ /* fixme: is_cms = 0; */ if ( (pend = strchr (p, ';')) ) *pend = 0; /* Strip off the extension. */ if (!ascii_strcasecmp (p, USERCERTIFICATE)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", USERCERTIFICATE); okay = 1; } else if (!ascii_strcasecmp (p, CACERTIFICATE)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", CACERTIFICATE); okay = 1; } else if (!ascii_strcasecmp (p, X509CACERT)) { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", CACERTIFICATE); okay = 1; } /* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */ /* { */ /* if (DBG_LOOKUP) */ /* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */ /* USERSMIMECERTIFICATE); */ /* okay = 1; */ /* is_cms = 1; */ /* } */ else { if (DBG_LOOKUP) log_debug ("fetch_next_cert_ldap: got attribute '%s'" " - ignored\n", p); okay = 0; } } else if (*hdr == 'E') { p = context->tmpbuf; p[n] = 0; /*(we allocated one extra byte for this.)*/ if (!strcmp (p, "truncated")) { context->truncated = 1; log_info (_("ldap_search hit the size limit of" " the server\n")); } } } } if (err) { xfree (*value); *value = NULL; *valuelen = 0; if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated) { context->truncated = 0; /* So that the next call would return EOF. */ err = gpg_error (GPG_ERR_TRUNCATED); } } return err; } void end_cert_fetch_ldap (cert_fetch_context_t context) { if (context) { ksba_reader_t reader = context->reader; xfree (context->tmpbuf); xfree (context); ldap_wrapper_release_context (reader); ksba_reader_release (reader); } } diff --git a/dirmngr/ldapserver.c b/dirmngr/ldapserver.c index 913e94f16..20a2bb18f 100644 --- a/dirmngr/ldapserver.c +++ b/dirmngr/ldapserver.c @@ -1,132 +1,161 @@ /* dirmngr.c - LDAP access Copyright (C) 2008 g10 Code GmbH This file is part of DirMngr. DirMngr 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 2 of the License, or (at your option) any later version. DirMngr 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include "dirmngr.h" #include "ldapserver.h" /* Release the list of SERVERS. As usual it is okay to call this function with SERVERS passed as NULL. */ void ldapserver_list_free (ldap_server_t servers) { while (servers) { ldap_server_t tmp = servers->next; xfree (servers->host); xfree (servers->user); if (servers->pass) memset (servers->pass, 0, strlen (servers->pass)); xfree (servers->pass); xfree (servers->base); xfree (servers); servers = tmp; } } /* Parse a single LDAP server configuration line. Returns the server or NULL in case of errors. The configuration line is assumed to be colon separated with these fields: 1. field: Hostname 2. field: Portnumber 3. field: Username 4. field: Password 5. field: Base DN + 6. field: Flags FILENAME and LINENO are used for diagnostic purposes only. */ ldap_server_t ldapserver_parse_one (char *line, const char *filename, unsigned int lineno) { char *p; char *endp; + const char *s; ldap_server_t server; int fieldno; int fail = 0; + int i; /* Parse the colon separated fields. */ server = xcalloc (1, sizeof *server); for (fieldno = 1, p = line; p; p = endp, fieldno++ ) { endp = strchr (p, ':'); if (endp) *endp++ = '\0'; trim_spaces (p); switch (fieldno) { case 1: if (*p) server->host = xstrdup (p); else { log_error (_("%s:%u: no hostname given\n"), filename, lineno); fail = 1; } break; case 2: if (*p) server->port = atoi (p); break; case 3: if (*p) server->user = xstrdup (p); break; case 4: if (*p && !server->user) { log_error (_("%s:%u: password given without user\n"), filename, lineno); fail = 1; } else if (*p) server->pass = xstrdup (p); break; case 5: if (*p) server->base = xstrdup (p); break; + case 6: + { + char **flags = NULL; + + flags = strtokenize (p, ","); + if (!flags) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + for (i=0; (s = flags[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "ldaps")) + server->use_ldaps = 1; + else if (!ascii_strcasecmp (s, "ldap")) + server->use_ldaps = 0; + else + log_info (_("%s:%u: ignoring unknown flag '%s'\n"), + filename, lineno, s); + } + + xfree (flags); + } + break; + default: /* (We silently ignore extra fields.) */ break; } } if (fail) { log_info (_("%s:%u: skipping this line\n"), filename, lineno); ldapserver_list_free (server); server = NULL; } return server; } diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index c841de77e..a6fafbb14 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -1,1181 +1,1185 @@ @c Copyright (C) 2002 Klar"alvdalens Datakonsult AB @c Copyright (C) 2004, 2005, 2006, 2007 g10 Code GmbH @c This is part of the GnuPG manual. @c For copying conditions, see the file gnupg.texi. @include defs.inc @node Invoking DIRMNGR @chapter Invoking DIRMNGR @cindex DIRMNGR command options @cindex command options @cindex options, DIRMNGR command @manpage dirmngr.8 @ifset manverb .B dirmngr \- CRL and OCSP daemon @end ifset @mansect synopsis @ifset manverb .B dirmngr .RI [ options ] .I command .RI [ args ] @end ifset @mansect description Since version 2.1 of GnuPG, @command{dirmngr} takes care of accessing the OpenPGP keyservers. As with previous versions it is also used as a server for managing and downloading certificate revocation lists (CRLs) for X.509 certificates, downloading X.509 certificates, and providing access to OCSP providers. Dirmngr is invoked internally by @command{gpg}, @command{gpgsm}, or via the @command{gpg-connect-agent} tool. @manpause @noindent @xref{Option Index},for an index to @command{DIRMNGR}'s commands and options. @mancont @menu * Dirmngr Commands:: List of all commands. * Dirmngr Options:: List of all options. * Dirmngr Configuration:: Configuration files. * Dirmngr Signals:: Use of signals. * Dirmngr Examples:: Some usage examples. * Dirmngr Protocol:: The protocol dirmngr uses. @end menu @node Dirmngr Commands @section Commands @mansect commands Commands are not distinguished from options except for the fact that only one command is allowed. @table @gnupgtabopt @item --version @opindex version Print the program version and licensing information. Note that you cannot abbreviate this command. @item --help, -h @opindex help Print a usage message summarizing the most useful command-line options. Note that you cannot abbreviate this command. @item --dump-options @opindex dump-options Print a list of all available options and commands. Note that you cannot abbreviate this command. @item --server @opindex server Run in server mode and wait for commands on the @code{stdin}. The default mode is to create a socket and listen for commands there. This is only used for testing. @item --daemon @opindex daemon Run in background daemon mode and listen for commands on a socket. This is the way @command{dirmngr} is started on demand by the other GnuPG components. To force starting @command{dirmngr} it is in general best to use @code{gpgconf --launch dirmngr}. @item --supervised @opindex supervised Run in the foreground, sending logs to stderr, and listening on file descriptor 3, which must already be bound to a listening socket. This is useful when running under systemd or other similar process supervision schemes. This option is not supported on Windows. @item --list-crls @opindex list-crls List the contents of the CRL cache on @code{stdout}. This is probably only useful for debugging purposes. @item --load-crl @var{file} @opindex load-crl This command requires a filename as additional argument, and it will make Dirmngr try to import the CRL in @var{file} into it's cache. Note, that this is only possible if Dirmngr is able to retrieve the CA's certificate directly by its own means. In general it is better to use @code{gpgsm}'s @code{--call-dirmngr loadcrl filename} command so that @code{gpgsm} can help dirmngr. @item --fetch-crl @var{url} @opindex fetch-crl This command requires an URL as additional argument, and it will make dirmngr try to retrieve and import the CRL from that @var{url} into it's cache. This is mainly useful for debugging purposes. The @command{dirmngr-client} provides the same feature for a running dirmngr. @item --shutdown @opindex shutdown This commands shuts down an running instance of Dirmngr. This command has currently no effect. @item --flush @opindex flush This command removes all CRLs from Dirmngr's cache. Client requests will thus trigger reading of fresh CRLs. @end table @mansect options @node Dirmngr Options @section Option Summary Note that all long options with the exception of @option{--options} and @option{--homedir} may also be given in the configuration file after stripping off the two leading dashes. @table @gnupgtabopt @item --options @var{file} @opindex options Reads configuration from @var{file} instead of from the default per-user configuration file. The default configuration file is named @file{dirmngr.conf} and expected in the home directory. @item --homedir @var{dir} @opindex options Set the name of the home directory to @var{dir}. This option is only effective when used on the command line. The default is the directory named @file{.gnupg} directly below the home directory of the user unless the environment variable @code{GNUPGHOME} has been set in which case its value will be used. Many kinds of data are stored within this directory. @item -v @item --verbose @opindex v @opindex verbose Outputs additional information while running. You can increase the verbosity by giving several verbose commands to @sc{dirmngr}, such as @option{-vv}. @item --log-file @var{file} @opindex log-file Append all logging output to @var{file}. This is very helpful in seeing what the agent actually does. Use @file{socket://} to log to socket. @item --debug-level @var{level} @opindex debug-level Select the debug level for investigating problems. @var{level} may be a numeric value or by a keyword: @table @code @item none No debugging at all. A value of less than 1 may be used instead of the keyword. @item basic Some basic debug messages. A value between 1 and 2 may be used instead of the keyword. @item advanced More verbose debug messages. A value between 3 and 5 may be used instead of the keyword. @item expert Even more detailed messages. A value between 6 and 8 may be used instead of the keyword. @item guru All of the debug messages you can get. A value greater than 8 may be used instead of the keyword. The creation of hash tracing files is only enabled if the keyword is used. @end table How these messages are mapped to the actual debugging flags is not specified and may change with newer releases of this program. They are however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given in C syntax (e.g. 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @item --debug-all @opindex debug-all Same as @code{--debug=0xffffffff} @item --tls-debug @var{level} @opindex tls-debug Enable debugging of the TLS layer at @var{level}. The details of the debug level depend on the used TLS library and are not set in stone. @item --debug-wait @var{n} @opindex debug-wait When running in server mode, wait @var{n} seconds before entering the actual processing loop and print the pid. This gives time to attach a debugger. @item --disable-check-own-socket @opindex disable-check-own-socket On some platforms @command{dirmngr} is able to detect the removal of its socket file and shutdown itself. This option disable this self-test for debugging purposes. @item -s @itemx --sh @itemx -c @itemx --csh @opindex s @opindex sh @opindex c @opindex csh Format the info output in daemon mode for use with the standard Bourne shell respective the C-shell. The default is to guess it based on the environment variable @code{SHELL} which is in almost all cases sufficient. @item --force @opindex force Enabling this option forces loading of expired CRLs; this is only useful for debugging. @item --use-tor @itemx --no-use-tor @opindex use-tor @opindex no-use-tor The option @option{--use-tor} switches Dirmngr and thus GnuPG into ``Tor mode'' to route all network access via Tor (an anonymity network). Certain other features are disabled in this mode. The effect of @option{--use-tor} cannot be overridden by any other command or even by reloading dirmngr. The use of @option{--no-use-tor} disables the use of Tor. The default is to use Tor if it is available on startup or after reloading dirmngr. @item --standard-resolver @opindex standard-resolver This option forces the use of the system's standard DNS resolver code. This is mainly used for debugging. Note that on Windows a standard resolver is not used and all DNS access will return the error ``Not Implemented'' if this function is used. @item --recursive-resolver @opindex recursive-resolver When possible use a recursive resolver instead of a stub resolver. @item --resolver-timeout @var{n} @opindex resolver-timeout Set the timeout for the DNS resolver to N seconds. The default are 30 seconds. @item --connect-timeout @var{n} @item --connect-quick-timeout @var{n} @opindex connect-timeout @opindex connect-quick-timeout Set the timeout for HTTP and generic TCP connection attempts to N seconds. The value set with the quick variant is used when the --quick option has been given to certain Assuan commands. The quick value is capped at the value of the regular connect timeout. The default values are 15 and 2 seconds. Note that the timeout values are for each connection attempt; the connection code will attempt to connect all addresses listed for a server. @item --listen-backlog @var{n} @opindex listen-backlog Set the size of the queue for pending connections. The default is 64. @item --allow-version-check @opindex allow-version-check Allow Dirmngr to connect to @code{https://versions.gnupg.org} to get the list of current software versions. If this option is enabled the list is retrieved in case the local copy does not exist or is older than 5 to 7 days. See the option @option{--query-swdb} of the command @command{gpgconf} for more details. Note, that regardless of this option a version check can always be triggered using this command: @example gpg-connect-agent --dirmngr 'loadswdb --force' /bye @end example @item --keyserver @var{name} @opindex keyserver Use @var{name} as your keyserver. This is the server that @command{gpg} communicates with to receive keys, send keys, and search for keys. The format of the @var{name} is a URI: `scheme:[//]keyservername[:port]' The scheme is the type of keyserver: "hkp" for the HTTP (or compatible) keyservers, "ldap" for the LDAP keyservers, or "mailto" for the Graff email keyserver. Note that your particular installation of GnuPG may have other keyserver types available as well. Keyserver schemes are case-insensitive. After the keyserver name, optional keyserver configuration options may be provided. These are the same as the @option{--keyserver-options} of @command{gpg}, but apply only to this particular keyserver. Most keyservers synchronize with each other, so there is generally no need to send keys to more than one server. The keyserver @code{hkp://keys.gnupg.net} uses round robin DNS to give a different keyserver each time you use it. If exactly two keyservers are configured and only one is a Tor hidden service (.onion), Dirmngr selects the keyserver to use depending on whether Tor is locally running or not. The check for a running Tor is done for each new connection. If no keyserver is explicitly configured, dirmngr will use the built-in default of hkps://hkps.pool.sks-keyservers.net. @item --nameserver @var{ipaddr} @opindex nameserver In ``Tor mode'' Dirmngr uses a public resolver via Tor to resolve DNS names. If the default public resolver, which is @code{8.8.8.8}, shall not be used a different one can be given using this option. Note that a numerical IP address must be given (IPv6 or IPv4) and that no error checking is done for @var{ipaddr}. @item --disable-ipv4 @item --disable-ipv6 @opindex disable-ipv4 @opindex disable-ipv6 Disable the use of all IPv4 or IPv6 addresses. @item --disable-ldap @opindex disable-ldap Entirely disables the use of LDAP. @item --disable-http @opindex disable-http Entirely disables the use of HTTP. @item --ignore-http-dp @opindex ignore-http-dp When looking for the location of a CRL, the to be tested certificate usually contains so called @dfn{CRL Distribution Point} (DP) entries which are URLs describing the way to access the CRL. The first found DP entry is used. With this option all entries using the @acronym{HTTP} scheme are ignored when looking for a suitable DP. @item --ignore-ldap-dp @opindex ignore-ldap-dp This is similar to @option{--ignore-http-dp} but ignores entries using the @acronym{LDAP} scheme. Both options may be combined resulting in ignoring DPs entirely. @item --ignore-ocsp-service-url @opindex ignore-ocsp-service-url Ignore all OCSP URLs contained in the certificate. The effect is to force the use of the default responder. @item --honor-http-proxy @opindex honor-http-proxy If the environment variable @env{http_proxy} has been set, use its value to access HTTP servers. @item --http-proxy @var{host}[:@var{port}] @opindex http-proxy @efindex http_proxy Use @var{host} and @var{port} to access HTTP servers. The use of this option overrides the environment variable @env{http_proxy} regardless whether @option{--honor-http-proxy} has been set. @item --ldap-proxy @var{host}[:@var{port}] @opindex ldap-proxy Use @var{host} and @var{port} to connect to LDAP servers. If @var{port} is omitted, port 389 (standard LDAP port) is used. This overrides any specified host and port part in a LDAP URL and will also be used if host and port have been omitted from the URL. @item --only-ldap-proxy @opindex only-ldap-proxy Never use anything else but the LDAP "proxy" as configured with @option{--ldap-proxy}. Usually @command{dirmngr} tries to use other configured LDAP server if the connection using the "proxy" failed. @item --ldapserverlist-file @var{file} @opindex ldapserverlist-file Read a list of LDAP servers to consult for CRLs and certificates from file. This servers from this list are used after any servers set by a client for its session. The default value for @var{file} is @file{dirmngr_ldapservers.conf}. This server list file contains one LDAP server per line in the format -@sc{hostname:port:username:password:base_dn} +@sc{hostname:port:username:password:base_dn:flags} Lines starting with a @samp{#} are comments. +The only defined flag is @code{ldaps} to specify that a TLS +connections shall be used. Flags are comma delimited; unknown flags +are ignored. + Note that as usual all strings entered are expected to be UTF-8 encoded. Obviously this will lead to problems if the password has originally been encoded as Latin-1. There is no other solution here than to put such a password in the binary encoding into the file (i.e. non-ascii characters won't show up readable).@footnote{The @command{gpgconf} tool might be helpful for frontends as it enables editing this configuration file using percent-escaped strings.} @item --ldaptimeout @var{secs} @opindex ldaptimeout Specify the number of seconds to wait for an LDAP query before timing out. The default are 15 seconds. 0 will never timeout. @item --add-servers @opindex add-servers This option makes dirmngr add any servers it discovers when validating certificates against CRLs to the internal list of servers to consult for certificates and CRLs. This option is useful when trying to validate a certificate that has a CRL distribution point that points to a server that is not already listed in the ldapserverlist. Dirmngr will always go to this server and try to download the CRL, but chances are high that the certificate used to sign the CRL is located on the same server. So if dirmngr doesn't add that new server to list, it will often not be able to verify the signature of the CRL unless the @code{--add-servers} option is used. Note: The current version of dirmngr has this option disabled by default. @item --allow-ocsp @opindex allow-ocsp This option enables OCSP support if requested by the client. OCSP requests are rejected by default because they may violate the privacy of the user; for example it is possible to track the time when a user is reading a mail. @item --ocsp-responder @var{url} @opindex ocsp-responder Use @var{url} as the default OCSP Responder if the certificate does not contain information about an assigned responder. Note, that @code{--ocsp-signer} must also be set to a valid certificate. @item --ocsp-signer @var{fpr}|@var{file} @opindex ocsp-signer Use the certificate with the fingerprint @var{fpr} to check the responses of the default OCSP Responder. Alternatively a filename can be given in which case the response is expected to be signed by one of the certificates described in that file. Any argument which contains a slash, dot or tilde is considered a filename. Usual filename expansion takes place: A tilde at the start followed by a slash is replaced by the content of @env{HOME}, no slash at start describes a relative filename which will be searched at the home directory. To make sure that the @var{file} is searched in the home directory, either prepend the name with "./" or use a name which contains a dot. If a response has been signed by a certificate described by these fingerprints no further check upon the validity of this certificate is done. The format of the @var{FILE} is a list of SHA-1 fingerprint, one per line with optional colons between the bytes. Empty lines and lines prefix with a hash mark are ignored. @item --ocsp-max-clock-skew @var{n} @opindex ocsp-max-clock-skew The number of seconds a skew between the OCSP responder and them local clock is accepted. Default is 600 (10 minutes). @item --ocsp-max-period @var{n} @opindex ocsp-max-period Seconds a response is at maximum considered valid after the time given in the thisUpdate field. Default is 7776000 (90 days). @item --ocsp-current-period @var{n} @opindex ocsp-current-period The number of seconds an OCSP response is considered valid after the time given in the NEXT_UPDATE datum. Default is 10800 (3 hours). @item --max-replies @var{n} @opindex max-replies Do not return more that @var{n} items in one query. The default is 10. @item --ignore-cert-extension @var{oid} @opindex ignore-cert-extension Add @var{oid} to the list of ignored certificate extensions. The @var{oid} is expected to be in dotted decimal form, like @code{2.5.29.3}. This option may be used more than once. Critical flagged certificate extensions matching one of the OIDs in the list are treated as if they are actually handled and thus the certificate won't be rejected due to an unknown critical extension. Use this option with care because extensions are usually flagged as critical for a reason. @item --hkp-cacert @var{file} Use the root certificates in @var{file} for verification of the TLS certificates used with @code{hkps} (keyserver access over TLS). If the file is in PEM format a suffix of @code{.pem} is expected for @var{file}. This option may be given multiple times to add more root certificates. Tilde expansion is supported. If no @code{hkp-cacert} directive is present, dirmngr will make a reasonable choice: if the keyserver in question is the special pool @code{hkps.pool.sks-keyservers.net}, it will use the bundled root certificate for that pool. Otherwise, it will use the system CAs. @end table @c @c Dirmngr Configuration @c @mansect files @node Dirmngr Configuration @section Configuration Dirmngr makes use of several directories when running in daemon mode: There are a few configuration files whih control the operation of dirmngr. By default they may all be found in the current home directory (@pxref{option --homedir}). @table @file @item dirmngr.conf @efindex dirmngr.conf This is the standard configuration file read by @command{dirmngr} on startup. It may contain any valid long option; the leading two dashes may not be entered and the option may not be abbreviated. This file is also read after a @code{SIGHUP} however not all options will actually have an effect. This default name may be changed on the command line (@pxref{option --options}). You should backup this file. @item /etc/gnupg/trusted-certs This directory should be filled with certificates of Root CAs you are trusting in checking the CRLs and signing OCSP Responses. Usually these are the same certificates you use with the applications making use of dirmngr. It is expected that each of these certificate files contain exactly one @acronym{DER} encoded certificate in a file with the suffix @file{.crt} or @file{.der}. @command{dirmngr} reads those certificates on startup and when given a SIGHUP. Certificates which are not readable or do not make up a proper X.509 certificate are ignored; see the log file for details. Applications using dirmngr (e.g. gpgsm) can request these certificates to complete a trust chain in the same way as with the extra-certs directory (see below). Note that for OCSP responses the certificate specified using the option @option{--ocsp-signer} is always considered valid to sign OCSP requests. @item /etc/gnupg/extra-certs This directory may contain extra certificates which are preloaded into the internal cache on startup. Applications using dirmngr (e.g. gpgsm) can request cached certificates to complete a trust chain. This is convenient in cases you have a couple intermediate CA certificates or certificates usually used to sign OCSP responses. These certificates are first tried before going out to the net to look for them. These certificates must also be @acronym{DER} encoded and suffixed with @file{.crt} or @file{.der}. @item ~/.gnupg/crls.d This directory is used to store cached CRLs. The @file{crls.d} part will be created by dirmngr if it does not exists but you need to make sure that the upper directory exists. @end table @manpause To be able to see what's going on you should create the configure file @file{~/gnupg/dirmngr.conf} with at least one line: @example log-file ~/dirmngr.log @end example To be able to perform OCSP requests you probably want to add the line: @example allow-ocsp @end example To make sure that new options are read and that after the installation of a new GnuPG versions the installed dirmngr is running, you may want to kill an existing dirmngr first: @example gpgconf --kill dirmngr @end example You may check the log file to see whether all desired root certificates have been loaded correctly. @c @c Dirmngr Signals @c @mansect signals @node Dirmngr Signals @section Use of signals A running @command{dirmngr} may be controlled by signals, i.e. using the @command{kill} command to send a signal to the process. Here is a list of supported signals: @table @gnupgtabopt @item SIGHUP @cpindex SIGHUP This signal flushes all internally cached CRLs as well as any cached certificates. Then the certificate cache is reinitialized as on startup. Options are re-read from the configuration file. Instead of sending this signal it is better to use @example gpgconf --reload dirmngr @end example @item SIGTERM @cpindex SIGTERM Shuts down the process but waits until all current requests are fulfilled. If the process has received 3 of these signals and requests are still pending, a shutdown is forced. You may also use @example gpgconf --kill dirmngr @end example instead of this signal @item SIGINT @cpindex SIGINT Shuts down the process immediately. @item SIGUSR1 @cpindex SIGUSR1 This prints some caching statistics to the log file. @end table @c @c Examples @c @mansect examples @node Dirmngr Examples @section Examples Here is an example on how to show dirmngr's internal table of OpenPGP keyserver addresses. The output is intended for debugging purposes and not part of a defined API. @example gpg-connect-agent --dirmngr 'keyserver --hosttable' /bye @end example To inhibit the use of a particular host you have noticed in one of the keyserver pools, you may use @example gpg-connect-agent --dirmngr 'keyserver --dead pgpkeys.bnd.de' /bye @end example The description of the @code{keyserver} command can be printed using @example gpg-connect-agent --dirmngr 'help keyserver' /bye @end example @c @c Assuan Protocol @c @manpause @node Dirmngr Protocol @section Dirmngr's Assuan Protocol Assuan is the IPC protocol used to access dirmngr. This is a description of the commands implemented by dirmngr. @menu * Dirmngr LOOKUP:: Look up a certificate via LDAP * Dirmngr ISVALID:: Validate a certificate using a CRL or OCSP. * Dirmngr CHECKCRL:: Validate a certificate using a CRL. * Dirmngr CHECKOCSP:: Validate a certificate using OCSP. * Dirmngr CACHECERT:: Put a certificate into the internal cache. * Dirmngr VALIDATE:: Validate a certificate for debugging. @end menu @node Dirmngr LOOKUP @subsection Return the certificate(s) found Lookup certificate. To allow multiple patterns (which are ORed) quoting is required: Spaces are to be translated into "+" or into "%20"; obviously this requires that the usual escape quoting rules are applied. The server responds with: @example S: D S: END S: D S: END S: OK @end example In this example 2 certificates are returned. The server may return any number of certificates; OK will also be returned when no certificates were found. The dirmngr might return a status line @example S: S TRUNCATED @end example To indicate that the output was truncated to N items due to a limitation of the server or by an arbitrary set limit. The option @option{--url} may be used if instead of a search pattern a complete URL to the certificate is known: @example C: LOOKUP --url CN%3DWerner%20Koch,o%3DIntevation%20GmbH,c%3DDE?userCertificate @end example If the option @option{--cache-only} is given, no external lookup is done so that only certificates from the cache are returned. With the option @option{--single}, the first and only the first match will be returned. Unless option @option{--cache-only} is also used, no local lookup will be done in this case. @node Dirmngr ISVALID @subsection Validate a certificate using a CRL or OCSP @example ISVALID [--only-ocsp] [--force-default-responder] @var{certid}|@var{certfpr} @end example Check whether the certificate described by the @var{certid} has been revoked. Due to caching, the Dirmngr is able to answer immediately in most cases. The @var{certid} is a hex encoded string consisting of two parts, delimited by a single dot. The first part is the SHA-1 hash of the issuer name and the second part the serial number. Alternatively the certificate's SHA-1 fingerprint @var{certfpr} may be given in which case an OCSP request is done before consulting the CRL. If the option @option{--only-ocsp} is given, no fallback to a CRL check will be used. If the option @option{--force-default-responder} is given, only the default OCSP responder will be used and any other methods of obtaining an OCSP responder URL won't be used. @noindent Common return values are: @table @code @item GPG_ERR_NO_ERROR (0) This is the positive answer: The certificate is not revoked and we have an up-to-date revocation list for that certificate. If OCSP was used the responder confirmed that the certificate has not been revoked. @item GPG_ERR_CERT_REVOKED This is the negative answer: The certificate has been revoked. Either it is in a CRL and that list is up to date or an OCSP responder informed us that it has been revoked. @item GPG_ERR_NO_CRL_KNOWN No CRL is known for this certificate or the CRL is not valid or out of date. @item GPG_ERR_NO_DATA The OCSP responder returned an ``unknown'' status. This means that it is not aware of the certificate's status. @item GPG_ERR_NOT_SUPPORTED This is commonly seen if OCSP support has not been enabled in the configuration. @end table If DirMngr has not enough information about the given certificate (which is the case for not yet cached certificates), it will inquire the missing data: @example S: INQUIRE SENDCERT C: D C: END @end example A client should be aware that DirMngr may ask for more than one certificate. If Dirmngr has a certificate but the signature of the certificate could not been validated because the root certificate is not known to dirmngr as trusted, it may ask back to see whether the client trusts this the root certificate: @example S: INQUIRE ISTRUSTED C: D 1 C: END @end example Only this answer will let Dirmngr consider the certificate as valid. @node Dirmngr CHECKCRL @subsection Validate a certificate using a CRL Check whether the certificate with FINGERPRINT (SHA-1 hash of the entire X.509 certificate blob) is valid or not by consulting the CRL responsible for this certificate. If the fingerprint has not been given or the certificate is not known, the function inquires the certificate using: @example S: INQUIRE TARGETCERT C: D C: END @end example Thus the caller is expected to return the certificate for the request (which should match FINGERPRINT) as a binary blob. Processing then takes place without further interaction; in particular dirmngr tries to locate other required certificate by its own mechanism which includes a local certificate store as well as a list of trusted root certificates. @noindent The return code is 0 for success; i.e. the certificate has not been revoked or one of the usual error codes from libgpg-error. @node Dirmngr CHECKOCSP @subsection Validate a certificate using OCSP @example CHECKOCSP [--force-default-responder] [@var{fingerprint}] @end example Check whether the certificate with @var{fingerprint} (the SHA-1 hash of the entire X.509 certificate blob) is valid by consulting the appropriate OCSP responder. If the fingerprint has not been given or the certificate is not known by Dirmngr, the function inquires the certificate using: @example S: INQUIRE TARGETCERT C: D C: END @end example Thus the caller is expected to return the certificate for the request (which should match @var{fingerprint}) as a binary blob. Processing then takes place without further interaction; in particular dirmngr tries to locate other required certificates by its own mechanism which includes a local certificate store as well as a list of trusted root certificates. If the option @option{--force-default-responder} is given, only the default OCSP responder is used. This option is the per-command variant of the global option @option{--ignore-ocsp-service-url}. @noindent The return code is 0 for success; i.e. the certificate has not been revoked or one of the usual error codes from libgpg-error. @node Dirmngr CACHECERT @subsection Put a certificate into the internal cache Put a certificate into the internal cache. This command might be useful if a client knows in advance certificates required for a test and wants to make sure they get added to the internal cache. It is also helpful for debugging. To get the actual certificate, this command immediately inquires it using @example S: INQUIRE TARGETCERT C: D C: END @end example Thus the caller is expected to return the certificate for the request as a binary blob. @noindent The return code is 0 for success; i.e. the certificate has not been successfully cached or one of the usual error codes from libgpg-error. @node Dirmngr VALIDATE @subsection Validate a certificate for debugging Validate a certificate using the certificate validation function used internally by dirmngr. This command is only useful for debugging. To get the actual certificate, this command immediately inquires it using @example S: INQUIRE TARGETCERT C: D C: END @end example Thus the caller is expected to return the certificate for the request as a binary blob. @mansect see also @ifset isman @command{gpgsm}(1), @command{dirmngr-client}(1) @end ifset @include see-also-note.texi @c @c !!! UNDER CONSTRUCTION !!! @c @c @c @section Verifying a Certificate @c @c There are several ways to request services from Dirmngr. Almost all of @c them are done using the Assuan protocol. What we describe here is the @c Assuan command CHECKCRL as used for example by the dirmnr-client tool if @c invoked as @c @c @example @c dirmngr-client foo.crt @c @end example @c @c This command will send an Assuan request to an already running Dirmngr @c instance. foo.crt is expected to be a standard X.509 certificate and @c dirmngr will receive the Assuan command @c @c @example @c CHECKCRL @var [{fingerprint}] @c @end example @c @c @var{fingerprint} is optional and expected to be the SHA-1 has of the @c DER encoding of the certificate under question. It is to be HEX @c encoded. The rationale for sending the fingerprint is that it allows @c dirmngr to reply immediately if it has already cached such a request. If @c this is not the case and no certificate has been found in dirmngr's @c internal certificate storage, dirmngr will request the certificate using @c the Assuan inquiry @c @c @example @c INQUIRE TARGETCERT @c @end example @c @c The caller (in our example dirmngr-client) is then expected to return @c the certificate for the request (which should match @var{fingerprint}) @c as a binary blob. @c @c Dirmngr now passes control to @code{crl_cache_cert_isvalid}. This @c function checks whether a CRL item exists for target certificate. These @c CRL items are kept in a database of already loaded and verified CRLs. @c This mechanism is called the CRL cache. Obviously timestamps are kept @c there with each item to cope with the expiration date of the CRL. The @c possible return values are: @code{0} to indicate that a valid CRL is @c available for the certificate and the certificate itself is not listed @c in this CRL, @code{GPG_ERR_CERT_REVOKED} to indicate that the certificate is @c listed in the CRL or @code{GPG_ERR_NO_CRL_KNOWN} in cases where no CRL or no @c information is available. The first two codes are immediately returned to @c the caller and the processing of this request has been done. @c @c Only the @code{GPG_ERR_NO_CRL_KNOWN} needs more attention: Dirmngr now @c calls @code{clr_cache_reload_crl} and if this succeeds calls @c @code{crl_cache_cert_isvald) once more. All further errors are @c immediately returned to the caller. @c @c @code{crl_cache_reload_crl} is the actual heart of the CRL management. @c It locates the corresponding CRL for the target certificate, reads and @c verifies this CRL and stores it in the CRL cache. It works like this: @c @c * Loop over all crlDPs in the target certificate. @c * If the crlDP is invalid immediately terminate the loop. @c * Loop over all names in the current crlDP. @c * If the URL scheme is unknown or not enabled @c (--ignore-http-dp, --ignore-ldap-dp) continues with @c the next name. @c * @code{crl_fetch} is called to actually retrieve the CRL. @c In case of problems this name is ignore and we continue with @c the next name. Note that @code{crl_fetch} does only return @c a descriptor for the CRL for further reading so does the CRL @c does not yet end up in memory. @c * @code{crl_cache_insert} is called with that descriptor to @c actually read the CRL into the cache. See below for a @c description of this function. If there is any error (e.g. read @c problem, CRL not correctly signed or verification of signature @c not possible), this descriptor is rejected and we continue @c with the next name. If the CRL has been successfully loaded, @c the loop is terminated. @c * If no crlDP has been found in the previous loop use a default CRL. @c Note, that if any crlDP has been found but loading of the CRL failed, @c this condition is not true. @c * Try to load a CRL from all configured servers (ldapservers.conf) @c in turn. The first server returning a CRL is used. @c * @code(crl_cache_insert) is then used to actually insert the CRL @c into the cache. If this failed we give up immediately without @c checking the rest of the servers from the first step. @c * Ready. @c @c @c The @code{crl_cache_insert} function takes care of reading the bulk of @c the CRL, parsing it and checking the signature. It works like this: A @c new database file is created using a temporary file name. The CRL @c parsing machinery is started and all items of the CRL are put into @c this database file. At the end the issuer certificate of the CRL @c needs to be retrieved. Three cases are to be distinguished: @c @c a) An authorityKeyIdentifier with an issuer and serialno exits: The @c certificate is retrieved using @code{find_cert_bysn}. If @c the certificate is in the certificate cache, it is directly @c returned. Then the requester (i.e. the client who requested the @c CRL check) is asked via the Assuan inquiry ``SENDCERT'' whether @c he can provide this certificate. If this succeed the returned @c certificate gets cached and returned. Note, that dirmngr does not @c verify in any way whether the expected certificate is returned. @c It is in the interest of the client to return a useful certificate @c as otherwise the service request will fail due to a bad signature. @c The last way to get the certificate is by looking it up at @c external resources. This is done using the @code{ca_cert_fetch} @c and @code{fetch_next_ksba_cert} and comparing the returned @c certificate to match the requested issuer and seriano (This is @c needed because the LDAP layer may return several certificates as @c LDAP as no standard way to retrieve by serial number). @c @c b) An authorityKeyIdentifier with a key ID exists: The certificate is @c retrieved using @code{find_cert_bysubject}. If the certificate is @c in the certificate cache, it is directly returned. Then the @c requester is asked via the Assuan inquiry ``SENDCERT_SKI'' whether @c he can provide this certificate. If this succeed the returned @c certificate gets cached and returned. Note, that dirmngr does not @c verify in any way whether the expected certificate is returned. @c It is in the interest of the client to return a useful certificate @c as otherwise the service request will fail due to a bad signature. @c The last way to get the certificate is by looking it up at @c external resources. This is done using the @code{ca_cert_fetch} @c and @code{fetch_next_ksba_cert} and comparing the returned @c certificate to match the requested subject and key ID. @c @c c) No authorityKeyIdentifier exits: The certificate is retrieved @c using @code{find_cert_bysubject} without the key ID argument. If @c the certificate is in the certificate cache the first one with a @c matching subject is directly returned. Then the requester is @c asked via the Assuan inquiry ``SENDCERT'' and an exact @c specification of the subject whether he can @c provide this certificate. If this succeed the returned @c certificate gets cached and returned. Note, that dirmngr does not @c verify in any way whether the expected certificate is returned. @c It is in the interest of the client to return a useful certificate @c as otherwise the service request will fail due to a bad signature. @c The last way to get the certificate is by looking it up at @c external resources. This is done using the @code{ca_cert_fetch} @c and @code{fetch_next_ksba_cert} and comparing the returned @c certificate to match the requested subject; the first certificate @c with a matching subject is then returned. @c @c If no certificate was found, the function returns with the error @c GPG_ERR_MISSING_CERT. Now the signature is verified. If this fails, @c the erro is returned. On success the @code{validate_cert_chain} is @c used to verify that the certificate is actually valid. @c @c Here we may encounter a recursive situation: @c @code{validate_cert_chain} needs to look at other certificates and @c also at CRLs to check whether these other certificates and well, the @c CRL issuer certificate itself are not revoked. FIXME: We need to make @c sure that @code{validate_cert_chain} does not try to lookup the CRL we @c are currently processing. This would be a catch-22 and may indicate a @c broken PKI. However, due to overlapping expiring times and imprecise @c clocks this may actually happen. @c @c For historical reasons the Assuan command ISVALID is a bit different @c to CHECKCRL but this is mainly due to different calling conventions. @c In the end the same fucntionality is used, albeit hidden by a couple @c of indirection and argument and result code mangling. It furthere @c ingetrages OCSP checking depending on options are the way it is @c called. GPGSM still uses this command but might eventually switch over @c to CHECKCRL and CHECKOCSP so that ISVALID can be retired. @c @c @c @section Validating a certificate @c @c We describe here how the internal function @code{validate_cert_chain} @c works. Note that mainly testing purposes this functionality may be @c called directly using @cmd{dirmngr-client --validate @file{foo.crt}}. @c @c The function takes the target certificate and a mode argument as @c parameters and returns an error code and optionally the closes @c expiration time of all certificates in the chain. @c @c We first check that the certificate may be used for the requested @c purpose (i.e. OCSP or CRL signing). If this is not the case @c GPG_ERR_WRONG_KEY_USAGE is returned. @c @c The next step is to find the trust anchor (root certificate) and to @c assemble the chain in memory: Starting with the target certificate, @c the expiration time is checked against the current date, unknown @c critical extensions are detected and certificate policies are matched @c (We only allow 2.289.9.9 but I have no clue about that OID and from @c where I got it - it does not even seem to be assigned - debug cruft?). @c @c Now if this certificate is a self-signed one, we have reached the @c trust anchor. In this case we check that the signature is good, the @c certificate is allowed to act as a CA, that it is a trusted one (by @c checking whether it is has been put into the trusted-certs @c configuration directory) and finally prepend into to our list @c representing the certificate chain. This steps ends then. @c @c If it is not a self-signed certificate, we check that the chain won't @c get too long (current limit is 100), if this is the case we terminate @c with the error GPG_ERR_BAD_CERT_CHAIN. @c @c Now the issuer's certificate is looked up: If an @c authorityKeyIdentifier is available, this one is used to locate the @c certificate either using issuer and serialnumber or subject DN @c (i.e. the issuer's DN) and the keyID. The functions @c @code{find_cert_bysn) and @code{find_cert_bysubject} are used @c respectively. The have already been described above under the @c description of @code{crl_cache_insert}. If no certificate was found @c or with no authorityKeyIdentifier, only the cache is consulted using @c @code{get_cert_bysubject}. The latter is done under the assumption @c that a matching certificate has explicitly been put into the @c certificate cache. If the issuer's certificate could not be found, @c the validation terminates with the error code @code{GPG_ERR_MISSING_CERT}. @c @c If the issuer's certificate has been found, the signature of the @c actual certificate is checked and in case this fails the error @c #code{GPG_ERR_BAD_CERT_CHAIN} is returned. If the signature checks out, the @c maximum chain length of the issuing certificate is checked as well as @c the capability of the certificate (i.e. whether he may be used for @c certificate signing). Then the certificate is prepended to our list @c representing the certificate chain. Finally the loop is continued now @c with the issuer's certificate as the current certificate. @c @c After the end of the loop and if no error as been encountered @c (i.e. the certificate chain has been assempled correctly), a check is @c done whether any certificate expired or a critical policy has not been @c met. In any of these cases the validation terminates with an @c appropriate error. @c @c Finally the function @code{check_revocations} is called to verify no @c certificate in the assempled chain has been revoked: This is an @c recursive process because a CRL has to be checked for each certificate @c in the chain except for the root certificate, of which we already know @c that it is trusted and we avoid checking a CRL here due to common @c setup problems and the assumption that a revoked root certificate has @c been removed from the list of trusted certificates. @c @c @c @c @c @section Looking up certificates through LDAP. @c @c This describes the LDAP layer to retrieve certificates. @c the functions @code{ca_cert_fetch} and @code{fetch_next_ksba_cert} are @c used for this. The first one starts a search and the second one is @c used to retrieve certificate after certificate. @c diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 0745f8626..130b217a5 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -1,1628 +1,1642 @@ @c Copyright (C) 2002 Free Software Foundation, Inc. @c This is part of the GnuPG manual. @c For copying conditions, see the file gnupg.texi. @include defs.inc @node Invoking GPGSM @chapter Invoking GPGSM @cindex GPGSM command options @cindex command options @cindex options, GPGSM command @manpage gpgsm.1 @ifset manverb .B gpgsm \- CMS encryption and signing tool @end ifset @mansect synopsis @ifset manverb .B gpgsm .RB [ \-\-homedir .IR dir ] .RB [ \-\-options .IR file ] .RI [ options ] .I command .RI [ args ] @end ifset @mansect description @command{gpgsm} is a tool similar to @command{gpg} to provide digital encryption and signing services on X.509 certificates and the CMS protocol. It is mainly used as a backend for S/MIME mail processing. @command{gpgsm} includes a full featured certificate management and complies with all rules defined for the German Sphinx project. @manpause @xref{Option Index}, for an index to @command{GPGSM}'s commands and options. @mancont @menu * GPGSM Commands:: List of all commands. * GPGSM Options:: List of all options. * GPGSM Configuration:: Configuration files. * GPGSM Examples:: Some usage examples. Developer information: * Unattended Usage:: Using @command{gpgsm} from other programs. * GPGSM Protocol:: The protocol the server mode uses. @end menu @c ******************************************* @c *************** **************** @c *************** COMMANDS **************** @c *************** **************** @c ******************************************* @mansect commands @node GPGSM Commands @section Commands Commands are not distinguished from options except for the fact that only one command is allowed. @menu * General GPGSM Commands:: Commands not specific to the functionality. * Operational GPGSM Commands:: Commands to select the type of operation. * Certificate Management:: How to manage certificates. @end menu @c ******************************************* @c ********** GENERAL COMMANDS ************* @c ******************************************* @node General GPGSM Commands @subsection Commands not specific to the function @table @gnupgtabopt @item --version @opindex version Print the program version and licensing information. Note that you cannot abbreviate this command. @item --help, -h @opindex help Print a usage message summarizing the most useful command-line options. Note that you cannot abbreviate this command. @item --warranty @opindex warranty Print warranty information. Note that you cannot abbreviate this command. @item --dump-options @opindex dump-options Print a list of all available options and commands. Note that you cannot abbreviate this command. @end table @c ******************************************* @c ******** OPERATIONAL COMMANDS *********** @c ******************************************* @node Operational GPGSM Commands @subsection Commands to select the type of operation @table @gnupgtabopt @item --encrypt @opindex encrypt Perform an encryption. The keys the data is encrypted to must be set using the option @option{--recipient}. @item --decrypt @opindex decrypt Perform a decryption; the type of input is automatically determined. It may either be in binary form or PEM encoded; automatic determination of base-64 encoding is not done. @item --sign @opindex sign Create a digital signature. The key used is either the fist one found in the keybox or those set with the @option{--local-user} option. @item --verify @opindex verify Check a signature file for validity. Depending on the arguments a detached signature may also be checked. @item --server @opindex server Run in server mode and wait for commands on the @code{stdin}. @item --call-dirmngr @var{command} [@var{args}] @opindex call-dirmngr Behave as a Dirmngr client issuing the request @var{command} with the optional list of @var{args}. The output of the Dirmngr is printed stdout. Please note that file names given as arguments should have an absolute file name (i.e. commencing with @code{/}) because they are passed verbatim to the Dirmngr and the working directory of the Dirmngr might not be the same as the one of this client. Currently it is not possible to pass data via stdin to the Dirmngr. @var{command} should not contain spaces. This is command is required for certain maintaining tasks of the dirmngr where a dirmngr must be able to call back to @command{gpgsm}. See the Dirmngr manual for details. @item --call-protect-tool @var{arguments} @opindex call-protect-tool Certain maintenance operations are done by an external program call @command{gpg-protect-tool}; this is usually not installed in a directory listed in the PATH variable. This command provides a simple wrapper to access this tool. @var{arguments} are passed verbatim to this command; use @samp{--help} to get a list of supported operations. @end table @c ******************************************* @c ******* CERTIFICATE MANAGEMENT ********** @c ******************************************* @node Certificate Management @subsection How to manage the certificates and keys @table @gnupgtabopt @item --generate-key @opindex generate-key @itemx --gen-key @opindex gen-key This command allows the creation of a certificate signing request or a self-signed certificate. It is commonly used along with the @option{--output} option to save the created CSR or certificate into a file. If used with the @option{--batch} a parameter file is used to create the CSR or certificate and it is further possible to create non-self-signed certificates. @item --list-keys @itemx -k @opindex list-keys List all available certificates stored in the local key database. Note that the displayed data might be reformatted for better human readability and illegal characters are replaced by safe substitutes. @item --list-secret-keys @itemx -K @opindex list-secret-keys List all available certificates for which a corresponding a secret key is available. @item --list-external-keys @var{pattern} @opindex list-keys List certificates matching @var{pattern} using an external server. This utilizes the @code{dirmngr} service. @item --list-chain @opindex list-chain Same as @option{--list-keys} but also prints all keys making up the chain. @item --dump-cert @itemx --dump-keys @opindex dump-cert @opindex dump-keys List all available certificates stored in the local key database using a format useful mainly for debugging. @item --dump-chain @opindex dump-chain Same as @option{--dump-keys} but also prints all keys making up the chain. @item --dump-secret-keys @opindex dump-secret-keys List all available certificates for which a corresponding a secret key is available using a format useful mainly for debugging. @item --dump-external-keys @var{pattern} @opindex dump-external-keys List certificates matching @var{pattern} using an external server. This utilizes the @code{dirmngr} service. It uses a format useful mainly for debugging. @item --keydb-clear-some-cert-flags @opindex keydb-clear-some-cert-flags This is a debugging aid to reset certain flags in the key database which are used to cache certain certificate statuses. It is especially useful if a bad CRL or a weird running OCSP responder did accidentally revoke certificate. There is no security issue with this command because @command{gpgsm} always make sure that the validity of a certificate is checked right before it is used. @item --delete-keys @var{pattern} @opindex delete-keys Delete the keys matching @var{pattern}. Note that there is no command to delete the secret part of the key directly. In case you need to do this, you should run the command @code{gpgsm --dump-secret-keys KEYID} before you delete the key, copy the string of hex-digits in the ``keygrip'' line and delete the file consisting of these hex-digits and the suffix @code{.key} from the @file{private-keys-v1.d} directory below our GnuPG home directory (usually @file{~/.gnupg}). @item --export [@var{pattern}] @opindex export Export all certificates stored in the Keybox or those specified by the optional @var{pattern}. Those pattern consist of a list of user ids (@pxref{how-to-specify-a-user-id}). When used along with the @option{--armor} option a few informational lines are prepended before each block. There is one limitation: As there is no commonly agreed upon way to pack more than one certificate into an ASN.1 structure, the binary export (i.e. without using @option{armor}) works only for the export of one certificate. Thus it is required to specify a @var{pattern} which yields exactly one certificate. Ephemeral certificate are only exported if all @var{pattern} are given as fingerprints or keygrips. @item --export-secret-key-p12 @var{key-id} @opindex export-secret-key-p12 Export the private key and the certificate identified by @var{key-id} using the PKCS#12 format. When used with the @code{--armor} option a few informational lines are prepended to the output. Note, that the PKCS#12 format is not very secure and proper transport security should be used to convey the exported key. (@xref{option --p12-charset}.) @item --export-secret-key-p8 @var{key-id} @itemx --export-secret-key-raw @var{key-id} @opindex export-secret-key-p8 @opindex export-secret-key-raw Export the private key of the certificate identified by @var{key-id} with any encryption stripped. The @code{...-raw} command exports in PKCS#1 format; the @code{...-p8} command exports in PKCS#8 format. When used with the @code{--armor} option a few informational lines are prepended to the output. These commands are useful to prepare a key for use on a TLS server. @item --import [@var{files}] @opindex import Import the certificates from the PEM or binary encoded files as well as from signed-only messages. This command may also be used to import a secret key from a PKCS#12 file. @item --learn-card @opindex learn-card Read information about the private keys from the smartcard and import the certificates from there. This command utilizes the @command{gpg-agent} and in turn the @command{scdaemon}. @item --change-passphrase @var{user_id} @opindex change-passphrase @itemx --passwd @var{user_id} @opindex passwd Change the passphrase of the private key belonging to the certificate specified as @var{user_id}. Note, that changing the passphrase/PIN of a smartcard is not yet supported. @end table @c ******************************************* @c *************** **************** @c *************** OPTIONS **************** @c *************** **************** @c ******************************************* @mansect options @node GPGSM Options @section Option Summary @command{GPGSM} features a bunch of options to control the exact behaviour and to change the default configuration. @menu * Configuration Options:: How to change the configuration. * Certificate Options:: Certificate related options. * Input and Output:: Input and Output. * CMS Options:: How to change how the CMS is created. * Esoteric Options:: Doing things one usually do not want to do. @end menu @c ******************************************* @c ******** CONFIGURATION OPTIONS ********** @c ******************************************* @node Configuration Options @subsection How to change the configuration These options are used to change the configuration and are usually found in the option file. @table @gnupgtabopt @anchor{gpgsm-option --options} @item --options @var{file} @opindex options Reads configuration from @var{file} instead of from the default per-user configuration file. The default configuration file is named @file{gpgsm.conf} and expected in the @file{.gnupg} directory directly below the home directory of the user. @include opt-homedir.texi @item -v @item --verbose @opindex v @opindex verbose Outputs additional information while running. You can increase the verbosity by giving several verbose commands to @command{gpgsm}, such as @samp{-vv}. @item --keyserver @var{string} @opindex keyserver Add an LDAP server to use for certificate and CRL lookup. This option can be given multiple times to configure more than one LDAP server. Note that the @command{dirmngr} can in addition be configured with a default list of LDAP servers to be used after those configured with this option. The syntax of @var{string} is: -@sc{hostname:port:username:password:base_dn} +@sc{hostname:port:username:password:base_dn:flags} + +The only defined flag is @code{ldaps} to specify that a TLS +connections shall be used. Flags are comma delimited; unknown flags +are ignored. Note that all parts of that string are expected to be UTF-8 encoded. This may lead to problems if the @sc{password} has originally been -encoded as Latin-1; in such a case better configure this LDAP server +encoded as Latin-1; in such a case better configure tsuch an LDAP server using the global configuration of @command{dirmngr}. +Here is an example which uses the default port, no username, no +password, and requests a TLS connection: + +@c man:.RS +@example +--keyserver ldap.pca.dfn.de::::o=DFN-Verein,c=DE:ldaps +@end example +@c man:.RE + + @item --policy-file @var{filename} @opindex policy-file Change the default name of the policy file to @var{filename}. The default name is @file{policies.txt}. @item --agent-program @var{file} @opindex agent-program Specify an agent program to be used for secret key operations. The default value is determined by running the command @command{gpgconf}. Note that the pipe symbol (@code{|}) is used for a regression test suite hack and may thus not be used in the file name. @item --dirmngr-program @var{file} @opindex dirmngr-program Specify a dirmngr program to be used for @acronym{CRL} checks. The default value is @file{@value{BINDIR}/dirmngr}. @item --prefer-system-dirmngr @opindex prefer-system-dirmngr This option is obsolete and ignored. @item --disable-dirmngr Entirely disable the use of the Dirmngr. @item --no-autostart @opindex no-autostart Do not start the gpg-agent or the dirmngr if it has not yet been started and its service is required. This option is mostly useful on machines where the connection to gpg-agent has been redirected to another machines. If dirmngr is required on the remote machine, it may be started manually using @command{gpgconf --launch dirmngr}. @item --no-secmem-warning @opindex no-secmem-warning Do not print a warning when the so called "secure memory" cannot be used. @item --log-file @var{file} @opindex log-file When running in server mode, append all logging output to @var{file}. Use @file{socket://} to log to socket. @end table @c ******************************************* @c ******** CERTIFICATE OPTIONS ************ @c ******************************************* @node Certificate Options @subsection Certificate related options @table @gnupgtabopt @item --enable-policy-checks @itemx --disable-policy-checks @opindex enable-policy-checks @opindex disable-policy-checks By default policy checks are enabled. These options may be used to change it. @item --enable-crl-checks @itemx --disable-crl-checks @opindex enable-crl-checks @opindex disable-crl-checks By default the @acronym{CRL} checks are enabled and the DirMngr is used to check for revoked certificates. The disable option is most useful with an off-line network connection to suppress this check. @item --enable-trusted-cert-crl-check @itemx --disable-trusted-cert-crl-check @opindex enable-trusted-cert-crl-check @opindex disable-trusted-cert-crl-check By default the @acronym{CRL} for trusted root certificates are checked like for any other certificates. This allows a CA to revoke its own certificates voluntary without the need of putting all ever issued certificates into a CRL. The disable option may be used to switch this extra check off. Due to the caching done by the Dirmngr, there will not be any noticeable performance gain. Note, that this also disables possible OCSP checks for trusted root certificates. A more specific way of disabling this check is by adding the ``relax'' keyword to the root CA line of the @file{trustlist.txt} @item --force-crl-refresh @opindex force-crl-refresh Tell the dirmngr to reload the CRL for each request. For better performance, the dirmngr will actually optimize this by suppressing the loading for short time intervals (e.g. 30 minutes). This option is useful to make sure that a fresh CRL is available for certificates hold in the keybox. The suggested way of doing this is by using it along with the option @option{--with-validation} for a key listing command. This option should not be used in a configuration file. @item --enable-ocsp @itemx --disable-ocsp @opindex enable-ocsp @opindex disable-ocsp By default @acronym{OCSP} checks are disabled. The enable option may be used to enable OCSP checks via Dirmngr. If @acronym{CRL} checks are also enabled, CRLs will be used as a fallback if for some reason an OCSP request will not succeed. Note, that you have to allow OCSP requests in Dirmngr's configuration too (option @option{--allow-ocsp}) and configure Dirmngr properly. If you do not do so you will get the error code @samp{Not supported}. @item --auto-issuer-key-retrieve @opindex auto-issuer-key-retrieve If a required certificate is missing while validating the chain of certificates, try to load that certificate from an external location. This usually means that Dirmngr is employed to search for the certificate. Note that this option makes a "web bug" like behavior possible. LDAP server operators can see which keys you request, so by sending you a message signed by a brand new key (which you naturally will not have on your local keybox), the operator can tell both your IP address and the time when you verified the signature. @anchor{gpgsm-option --validation-model} @item --validation-model @var{name} @opindex validation-model This option changes the default validation model. The only possible values are "shell" (which is the default), "chain" which forces the use of the chain model and "steed" for a new simplified model. The chain model is also used if an option in the @file{trustlist.txt} or an attribute of the certificate requests it. However the standard model (shell) is in that case always tried first. @item --ignore-cert-extension @var{oid} @opindex ignore-cert-extension Add @var{oid} to the list of ignored certificate extensions. The @var{oid} is expected to be in dotted decimal form, like @code{2.5.29.3}. This option may be used more than once. Critical flagged certificate extensions matching one of the OIDs in the list are treated as if they are actually handled and thus the certificate will not be rejected due to an unknown critical extension. Use this option with care because extensions are usually flagged as critical for a reason. @end table @c ******************************************* @c *********** INPUT AND OUTPUT ************ @c ******************************************* @node Input and Output @subsection Input and Output @table @gnupgtabopt @item --armor @itemx -a @opindex armor Create PEM encoded output. Default is binary output. @item --base64 @opindex base64 Create Base-64 encoded output; i.e. PEM without the header lines. @item --assume-armor @opindex assume-armor Assume the input data is PEM encoded. Default is to autodetect the encoding but this is may fail. @item --assume-base64 @opindex assume-base64 Assume the input data is plain base-64 encoded. @item --assume-binary @opindex assume-binary Assume the input data is binary encoded. @anchor{option --p12-charset} @item --p12-charset @var{name} @opindex p12-charset @command{gpgsm} uses the UTF-8 encoding when encoding passphrases for PKCS#12 files. This option may be used to force the passphrase to be encoded in the specified encoding @var{name}. This is useful if the application used to import the key uses a different encoding and thus will not be able to import a file generated by @command{gpgsm}. Commonly used values for @var{name} are @code{Latin1} and @code{CP850}. Note that @command{gpgsm} itself automagically imports any file with a passphrase encoded to the most commonly used encodings. @item --default-key @var{user_id} @opindex default-key Use @var{user_id} as the standard key for signing. This key is used if no other key has been defined as a signing key. Note, that the first @option{--local-users} option also sets this key if it has not yet been set; however @option{--default-key} always overrides this. @item --local-user @var{user_id} @item -u @var{user_id} @opindex local-user Set the user(s) to be used for signing. The default is the first secret key found in the database. @item --recipient @var{name} @itemx -r @opindex recipient Encrypt to the user id @var{name}. There are several ways a user id may be given (@pxref{how-to-specify-a-user-id}). @item --output @var{file} @itemx -o @var{file} @opindex output Write output to @var{file}. The default is to write it to stdout. @anchor{gpgsm-option --with-key-data} @item --with-key-data @opindex with-key-data Displays extra information with the @code{--list-keys} commands. Especially a line tagged @code{grp} is printed which tells you the keygrip of a key. This string is for example used as the file name of the secret key. Implies @code{--with-colons}. @anchor{gpgsm-option --with-validation} @item --with-validation @opindex with-validation When doing a key listing, do a full validation check for each key and print the result. This is usually a slow operation because it requires a CRL lookup and other operations. When used along with @option{--import}, a validation of the certificate to import is done and only imported if it succeeds the test. Note that this does not affect an already available certificate in the DB. This option is therefore useful to simply verify a certificate. @item --with-md5-fingerprint For standard key listings, also print the MD5 fingerprint of the certificate. @item --with-keygrip Include the keygrip in standard key listings. Note that the keygrip is always listed in @option{--with-colons} mode. @item --with-secret @opindex with-secret Include info about the presence of a secret key in public key listings done with @code{--with-colons}. @end table @c ******************************************* @c ************* CMS OPTIONS *************** @c ******************************************* @node CMS Options @subsection How to change how the CMS is created @table @gnupgtabopt @item --include-certs @var{n} @opindex include-certs Using @var{n} of -2 includes all certificate except for the root cert, -1 includes all certs, 0 does not include any certs, 1 includes only the signers cert and all other positive values include up to @var{n} certificates starting with the signer cert. The default is -2. @item --cipher-algo @var{oid} @opindex cipher-algo Use the cipher algorithm with the ASN.1 object identifier @var{oid} for encryption. For convenience the strings @code{3DES}, @code{AES} and @code{AES256} may be used instead of their OIDs. The default is @code{AES} (2.16.840.1.101.3.4.1.2). @item --digest-algo @code{name} Use @code{name} as the message digest algorithm. Usually this algorithm is deduced from the respective signing certificate. This option forces the use of the given algorithm and may lead to severe interoperability problems. @end table @c ******************************************* @c ******** ESOTERIC OPTIONS *************** @c ******************************************* @node Esoteric Options @subsection Doing things one usually do not want to do @table @gnupgtabopt @item --extra-digest-algo @var{name} @opindex extra-digest-algo Sometimes signatures are broken in that they announce a different digest algorithm than actually used. @command{gpgsm} uses a one-pass data processing model and thus needs to rely on the announced digest algorithms to properly hash the data. As a workaround this option may be used to tell @command{gpgsm} to also hash the data using the algorithm @var{name}; this slows processing down a little bit but allows verification of such broken signatures. If @command{gpgsm} prints an error like ``digest algo 8 has not been enabled'' you may want to try this option, with @samp{SHA256} for @var{name}. @item --faked-system-time @var{epoch} @opindex faked-system-time This option is only useful for testing; it sets the system time back or forth to @var{epoch} which is the number of seconds elapsed since the year 1970. Alternatively @var{epoch} may be given as a full ISO time string (e.g. "20070924T154812"). @item --with-ephemeral-keys @opindex with-ephemeral-keys Include ephemeral flagged keys in the output of key listings. Note that they are included anyway if the key specification for a listing is given as fingerprint or keygrip. @item --debug-level @var{level} @opindex debug-level Select the debug level for investigating problems. @var{level} may be a numeric value or by a keyword: @table @code @item none No debugging at all. A value of less than 1 may be used instead of the keyword. @item basic Some basic debug messages. A value between 1 and 2 may be used instead of the keyword. @item advanced More verbose debug messages. A value between 3 and 5 may be used instead of the keyword. @item expert Even more detailed messages. A value between 6 and 8 may be used instead of the keyword. @item guru All of the debug messages you can get. A value greater than 8 may be used instead of the keyword. The creation of hash tracing files is only enabled if the keyword is used. @end table How these messages are mapped to the actual debugging flags is not specified and may change with newer releases of this program. They are however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given in C syntax (e.g. 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. Note, that all flags set using this option may get overridden by @code{--debug-level}. @item --debug-all @opindex debug-all Same as @code{--debug=0xffffffff} @item --debug-allow-core-dump @opindex debug-allow-core-dump Usually @command{gpgsm} tries to avoid dumping core by well written code and by disabling core dumps for security reasons. However, bugs are pretty durable beasts and to squash them it is sometimes useful to have a core dump. This option enables core dumps unless the Bad Thing happened before the option parsing. @item --debug-no-chain-validation @opindex debug-no-chain-validation This is actually not a debugging option but only useful as such. It lets @command{gpgsm} bypass all certificate chain validation checks. @item --debug-ignore-expiration @opindex debug-ignore-expiration This is actually not a debugging option but only useful as such. It lets @command{gpgsm} ignore all notAfter dates, this is used by the regression tests. @item --passphrase-fd @code{n} @opindex passphrase-fd Read the passphrase from file descriptor @code{n}. Only the first line will be read from file descriptor @code{n}. If you use 0 for @code{n}, the passphrase will be read from STDIN. This can only be used if only one passphrase is supplied. Note that this passphrase is only used if the option @option{--batch} has also been given. @item --pinentry-mode @code{mode} @opindex pinentry-mode Set the pinentry mode to @code{mode}. Allowed values for @code{mode} are: @table @asis @item default Use the default of the agent, which is @code{ask}. @item ask Force the use of the Pinentry. @item cancel Emulate use of Pinentry's cancel button. @item error Return a Pinentry error (``No Pinentry''). @item loopback Redirect Pinentry queries to the caller. Note that in contrast to Pinentry the user is not prompted again if he enters a bad password. @end table @item --request-origin @var{origin} @opindex request-origin Tell gpgsm to assume that the operation ultimately originated at @var{origin}. Depending on the origin certain restrictions are applied and the Pinentry may include an extra note on the origin. Supported values for @var{origin} are: @code{local} which is the default, @code{remote} to indicate a remote origin or @code{browser} for an operation requested by a web browser. @item --no-common-certs-import @opindex no-common-certs-import Suppress the import of common certificates on keybox creation. @end table All the long options may also be given in the configuration file after stripping off the two leading dashes. @c ******************************************* @c *************** **************** @c *************** USER ID **************** @c *************** **************** @c ******************************************* @mansect how to specify a user id @ifset isman @include specify-user-id.texi @end ifset @c ******************************************* @c *************** **************** @c *************** FILES **************** @c *************** **************** @c ******************************************* @mansect files @node GPGSM Configuration @section Configuration files There are a few configuration files to control certain aspects of @command{gpgsm}'s operation. Unless noted, they are expected in the current home directory (@pxref{option --homedir}). @table @file @item gpgsm.conf @efindex gpgsm.conf This is the standard configuration file read by @command{gpgsm} on startup. It may contain any valid long option; the leading two dashes may not be entered and the option may not be abbreviated. This default name may be changed on the command line (@pxref{gpgsm-option --options}). You should backup this file. @item policies.txt @efindex policies.txt This is a list of allowed CA policies. This file should list the object identifiers of the policies line by line. Empty lines and lines starting with a hash mark are ignored. Policies missing in this file and not marked as critical in the certificate will print only a warning; certificates with policies marked as critical and not listed in this file will fail the signature verification. You should backup this file. For example, to allow only the policy 2.289.9.9, the file should look like this: @c man:.RS @example # Allowed policies 2.289.9.9 @end example @c man:.RE @item qualified.txt @efindex qualified.txt This is the list of root certificates used for qualified certificates. They are defined as certificates capable of creating legally binding signatures in the same way as handwritten signatures are. Comments start with a hash mark and empty lines are ignored. Lines do have a length limit but this is not a serious limitation as the format of the entries is fixed and checked by @command{gpgsm}: A non-comment line starts with optional whitespace, followed by exactly 40 hex characters, white space and a lowercased 2 letter country code. Additional data delimited with by a white space is current ignored but might late be used for other purposes. Note that even if a certificate is listed in this file, this does not mean that the certificate is trusted; in general the certificates listed in this file need to be listed also in @file{trustlist.txt}. This is a global file an installed in the sysconf directory (e.g. @file{@value{SYSCONFDIR}/qualified.txt}). Every time @command{gpgsm} uses a certificate for signing or verification this file will be consulted to check whether the certificate under question has ultimately been issued by one of these CAs. If this is the case the user will be informed that the verified signature represents a legally binding (``qualified'') signature. When creating a signature using such a certificate an extra prompt will be issued to let the user confirm that such a legally binding signature shall really be created. Because this software has not yet been approved for use with such certificates, appropriate notices will be shown to indicate this fact. @item help.txt @efindex help.txt This is plain text file with a few help entries used with @command{pinentry} as well as a large list of help items for @command{gpg} and @command{gpgsm}. The standard file has English help texts; to install localized versions use filenames like @file{help.LL.txt} with LL denoting the locale. GnuPG comes with a set of predefined help files in the data directory (e.g. @file{@value{DATADIR}/gnupg/help.de.txt}) and allows overriding of any help item by help files stored in the system configuration directory (e.g. @file{@value{SYSCONFDIR}/help.de.txt}). For a reference of the help file's syntax, please see the installed @file{help.txt} file. @item com-certs.pem @efindex com-certs.pem This file is a collection of common certificates used to populated a newly created @file{pubring.kbx}. An administrator may replace this file with a custom one. The format is a concatenation of PEM encoded X.509 certificates. This global file is installed in the data directory (e.g. @file{@value{DATADIR}/com-certs.pem}). @end table @c man:.RE Note that on larger installations, it is useful to put predefined files into the directory @file{/etc/skel/.gnupg/} so that newly created users start up with a working configuration. For existing users a small helper script is provided to create these files (@pxref{addgnupghome}). For internal purposes @command{gpgsm} creates and maintains a few other files; they all live in the current home directory (@pxref{option --homedir}). Only @command{gpgsm} may modify these files. @table @file @item pubring.kbx @efindex pubring.kbx This a database file storing the certificates as well as meta information. For debugging purposes the tool @command{kbxutil} may be used to show the internal structure of this file. You should backup this file. @item random_seed @efindex random_seed This content of this file is used to maintain the internal state of the random number generator across invocations. The same file is used by other programs of this software too. @item S.gpg-agent @efindex S.gpg-agent If this file exists @command{gpgsm} will first try to connect to this socket for accessing @command{gpg-agent} before starting a new @command{gpg-agent} instance. Under Windows this socket (which in reality be a plain file describing a regular TCP listening port) is the standard way of connecting the @command{gpg-agent}. @end table @c ******************************************* @c *************** **************** @c *************** EXAMPLES **************** @c *************** **************** @c ******************************************* @mansect examples @node GPGSM Examples @section Examples @example $ gpgsm -er goo@@bar.net ciphertext @end example @c ******************************************* @c *************** ************** @c *************** UNATTENDED ************** @c *************** ************** @c ******************************************* @manpause @node Unattended Usage @section Unattended Usage @command{gpgsm} is often used as a backend engine by other software. To help with this a machine interface has been defined to have an unambiguous way to do this. This is most likely used with the @code{--server} command but may also be used in the standard operation mode by using the @code{--status-fd} option. @menu * Automated signature checking:: Automated signature checking. * CSR and certificate creation:: CSR and certificate creation. @end menu @node Automated signature checking @subsection Automated signature checking It is very important to understand the semantics used with signature verification. Checking a signature is not as simple as it may sound and so the operation is a bit complicated. In most cases it is required to look at several status lines. Here is a table of all cases a signed message may have: @table @asis @item The signature is valid This does mean that the signature has been successfully verified, the certificates are all sane. However there are two subcases with important information: One of the certificates may have expired or a signature of a message itself as expired. It is a sound practise to consider such a signature still as valid but additional information should be displayed. Depending on the subcase @command{gpgsm} will issue these status codes: @table @asis @item signature valid and nothing did expire @code{GOODSIG}, @code{VALIDSIG}, @code{TRUST_FULLY} @item signature valid but at least one certificate has expired @code{EXPKEYSIG}, @code{VALIDSIG}, @code{TRUST_FULLY} @item signature valid but expired @code{EXPSIG}, @code{VALIDSIG}, @code{TRUST_FULLY} Note, that this case is currently not implemented. @end table @item The signature is invalid This means that the signature verification failed (this is an indication of a transfer error, a program error or tampering with the message). @command{gpgsm} issues one of these status codes sequences: @table @code @item @code{BADSIG} @item @code{GOODSIG}, @code{VALIDSIG} @code{TRUST_NEVER} @end table @item Error verifying a signature For some reason the signature could not be verified, i.e. it cannot be decided whether the signature is valid or invalid. A common reason for this is a missing certificate. @end table @node CSR and certificate creation @subsection CSR and certificate creation The command @option{--generate-key} may be used along with the option @option{--batch} to either create a certificate signing request (CSR) or an X.509 certificate. This is controlled by a parameter file; the format of this file is as follows: @itemize @bullet @item Text only, line length is limited to about 1000 characters. @item UTF-8 encoding must be used to specify non-ASCII characters. @item Empty lines are ignored. @item Leading and trailing while space is ignored. @item A hash sign as the first non white space character indicates a comment line. @item Control statements are indicated by a leading percent sign, the arguments are separated by white space from the keyword. @item Parameters are specified by a keyword, followed by a colon. Arguments are separated by white space. @item The first parameter must be @samp{Key-Type}, control statements may be placed anywhere. @item The order of the parameters does not matter except for @samp{Key-Type} which must be the first parameter. The parameters are only used for the generated CSR/certificate; parameters from previous sets are not used. Some syntactically checks may be performed. @item Key generation takes place when either the end of the parameter file is reached, the next @samp{Key-Type} parameter is encountered or at the control statement @samp{%commit} is encountered. @end itemize @noindent Control statements: @table @asis @item %echo @var{text} Print @var{text} as diagnostic. @item %dry-run Suppress actual key generation (useful for syntax checking). @item %commit Perform the key generation. Note that an implicit commit is done at the next @asis{Key-Type} parameter. @c %certfile <filename> @c [Not yet implemented!] @c Do not write the certificate to the keyDB but to <filename>. @c This must be given before the first @c commit to take place, duplicate specification of the same filename @c is ignored, the last filename before a commit is used. @c The filename is used until a new filename is used (at commit points) @c and all keys are written to that file. If a new filename is given, @c this file is created (and overwrites an existing one). @c Both control statements must be given. @end table @noindent General Parameters: @table @asis @item Key-Type: @var{algo} Starts a new parameter block by giving the type of the primary key. The algorithm must be capable of signing. This is a required parameter. The only supported value for @var{algo} is @samp{rsa}. @item Key-Length: @var{nbits} The requested length of a generated key in bits. Defaults to 3072. @item Key-Grip: @var{hexstring} This is optional and used to generate a CSR or certificate for an already existing key. Key-Length will be ignored when given. @item Key-Usage: @var{usage-list} Space or comma delimited list of key usage, allowed values are @samp{encrypt}, @samp{sign} and @samp{cert}. This is used to generate the keyUsage extension. Please make sure that the algorithm is capable of this usage. Default is to allow encrypt and sign. @item Name-DN: @var{subject-name} This is the Distinguished Name (DN) of the subject in RFC-2253 format. @item Name-Email: @var{string} This is an email address for the altSubjectName. This parameter is optional but may occur several times to add several email addresses to a certificate. @item Name-DNS: @var{string} The is an DNS name for the altSubjectName. This parameter is optional but may occur several times to add several DNS names to a certificate. @item Name-URI: @var{string} This is an URI for the altSubjectName. This parameter is optional but may occur several times to add several URIs to a certificate. @end table @noindent Additional parameters used to create a certificate (in contrast to a certificate signing request): @table @asis @item Serial: @var{sn} If this parameter is given an X.509 certificate will be generated. @var{sn} is expected to be a hex string representing an unsigned integer of arbitrary length. The special value @samp{random} can be used to create a 64 bit random serial number. @item Issuer-DN: @var{issuer-name} This is the DN name of the issuer in RFC-2253 format. If it is not set it will default to the subject DN and a special GnuPG extension will be included in the certificate to mark it as a standalone certificate. @item Creation-Date: @var{iso-date} @itemx Not-Before: @var{iso-date} Set the notBefore date of the certificate. Either a date like @samp{1986-04-26} or @samp{1986-04-26 12:00} or a standard ISO timestamp like @samp{19860426T042640} may be used. The time is considered to be UTC. If it is not given the current date is used. @item Expire-Date: @var{iso-date} @itemx Not-After: @var{iso-date} Set the notAfter date of the certificate. Either a date like @samp{2063-04-05} or @samp{2063-04-05 17:00} or a standard ISO timestamp like @samp{20630405T170000} may be used. The time is considered to be UTC. If it is not given a default value in the not too far future is used. @item Signing-Key: @var{keygrip} This gives the keygrip of the key used to sign the certificate. If it is not given a self-signed certificate will be created. For compatibility with future versions, it is suggested to prefix the keygrip with a @samp{&}. @item Hash-Algo: @var{hash-algo} Use @var{hash-algo} for this CSR or certificate. The supported hash algorithms are: @samp{sha1}, @samp{sha256}, @samp{sha384} and @samp{sha512}; they may also be specified with uppercase letters. The default is @samp{sha256}. @end table @c ******************************************* @c *************** ***************** @c *************** ASSSUAN ***************** @c *************** ***************** @c ******************************************* @node GPGSM Protocol @section The Protocol the Server Mode Uses Description of the protocol used to access @command{GPGSM}. @command{GPGSM} does implement the Assuan protocol and in addition provides a regular command line interface which exhibits a full client to this protocol (but uses internal linking). To start @command{gpgsm} as a server the command line the option @code{--server} must be used. Additional options are provided to select the communication method (i.e. the name of the socket). We assume that the connection has already been established; see the Assuan manual for details. @menu * GPGSM ENCRYPT:: Encrypting a message. * GPGSM DECRYPT:: Decrypting a message. * GPGSM SIGN:: Signing a message. * GPGSM VERIFY:: Verifying a message. * GPGSM GENKEY:: Generating a key. * GPGSM LISTKEYS:: List available keys. * GPGSM EXPORT:: Export certificates. * GPGSM IMPORT:: Import certificates. * GPGSM DELETE:: Delete certificates. * GPGSM GETAUDITLOG:: Retrieve an audit log. * GPGSM GETINFO:: Information about the process * GPGSM OPTION:: Session options. @end menu @node GPGSM ENCRYPT @subsection Encrypting a Message Before encryption can be done the recipient must be set using the command: @example RECIPIENT @var{userID} @end example Set the recipient for the encryption. @var{userID} should be the internal representation of the key; the server may accept any other way of specification. If this is a valid and trusted recipient the server does respond with OK, otherwise the return is an ERR with the reason why the recipient cannot be used, the encryption will then not be done for this recipient. If the policy is not to encrypt at all if not all recipients are valid, the client has to take care of this. All @code{RECIPIENT} commands are cumulative until a @code{RESET} or an successful @code{ENCRYPT} command. @example INPUT FD[=@var{n}] [--armor|--base64|--binary] @end example Set the file descriptor for the message to be encrypted to @var{n}. Obviously the pipe must be open at that point, the server establishes its own end. If the server returns an error the client should consider this session failed. If @var{n} is not given, this commands uses the last file descriptor passed to the application. @xref{fun-assuan_sendfd, ,the assuan_sendfd function,assuan,the Libassuan manual}, on how to do descriptor passing. The @code{--armor} option may be used to advice the server that the input data is in @acronym{PEM} format, @code{--base64} advices that a raw base-64 encoding is used, @code{--binary} advices of raw binary input (@acronym{BER}). If none of these options is used, the server tries to figure out the used encoding, but this may not always be correct. @example OUTPUT FD[=@var{n}] [--armor|--base64] @end example Set the file descriptor to be used for the output (i.e. the encrypted message). Obviously the pipe must be open at that point, the server establishes its own end. If the server returns an error the client should consider this session failed. The option @option{--armor} encodes the output in @acronym{PEM} format, the @option{--base64} option applies just a base-64 encoding. No option creates binary output (@acronym{BER}). The actual encryption is done using the command @example ENCRYPT @end example It takes the plaintext from the @code{INPUT} command, writes to the ciphertext to the file descriptor set with the @code{OUTPUT} command, take the recipients from all the recipients set so far. If this command fails the clients should try to delete all output currently done or otherwise mark it as invalid. @command{GPGSM} does ensure that there will not be any security problem with leftover data on the output in this case. This command should in general not fail, as all necessary checks have been done while setting the recipients. The input and output pipes are closed. @node GPGSM DECRYPT @subsection Decrypting a message Input and output FDs are set the same way as in encryption, but @code{INPUT} refers to the ciphertext and @code{OUTPUT} to the plaintext. There is no need to set recipients. @command{GPGSM} automatically strips any @acronym{S/MIME} headers from the input, so it is valid to pass an entire MIME part to the INPUT pipe. The decryption is done by using the command @example DECRYPT @end example It performs the decrypt operation after doing some check on the internal state (e.g. that all needed data has been set). Because it utilizes the GPG-Agent for the session key decryption, there is no need to ask the client for a protecting passphrase - GpgAgent takes care of this by requesting this from the user. @node GPGSM SIGN @subsection Signing a Message Signing is usually done with these commands: @example INPUT FD[=@var{n}] [--armor|--base64|--binary] @end example This tells @command{GPGSM} to read the data to sign from file descriptor @var{n}. @example OUTPUT FD[=@var{m}] [--armor|--base64] @end example Write the output to file descriptor @var{m}. If a detached signature is requested, only the signature is written. @example SIGN [--detached] @end example Sign the data set with the @code{INPUT} command and write it to the sink set by @code{OUTPUT}. With @code{--detached}, a detached signature is created (surprise). The key used for signing is the default one or the one specified in the configuration file. To get finer control over the keys, it is possible to use the command @example SIGNER @var{userID} @end example to set the signer's key. @var{userID} should be the internal representation of the key; the server may accept any other way of specification. If this is a valid and trusted recipient the server does respond with OK, otherwise the return is an ERR with the reason why the key cannot be used, the signature will then not be created using this key. If the policy is not to sign at all if not all keys are valid, the client has to take care of this. All @code{SIGNER} commands are cumulative until a @code{RESET} is done. Note that a @code{SIGN} does not reset this list of signers which is in contrast to the @code{RECIPIENT} command. @node GPGSM VERIFY @subsection Verifying a Message To verify a message the command: @example VERIFY @end example is used. It does a verify operation on the message send to the input FD. The result is written out using status lines. If an output FD was given, the signed text will be written to that. If the signature is a detached one, the server will inquire about the signed material and the client must provide it. @node GPGSM GENKEY @subsection Generating a Key This is used to generate a new keypair, store the secret part in the @acronym{PSE} and the public key in the key database. We will probably add optional commands to allow the client to select whether a hardware token is used to store the key. Configuration options to @command{GPGSM} can be used to restrict the use of this command. @example GENKEY @end example @command{GPGSM} checks whether this command is allowed and then does an INQUIRY to get the key parameters, the client should then send the key parameters in the native format: @example S: INQUIRE KEY_PARAM native C: D foo:fgfgfg C: D bar C: END @end example Please note that the server may send Status info lines while reading the data lines from the client. After this the key generation takes place and the server eventually does send an ERR or OK response. Status lines may be issued as a progress indicator. @node GPGSM LISTKEYS @subsection List available keys @anchor{gpgsm-cmd listkeys} To list the keys in the internal database or using an external key provider, the command: @example LISTKEYS @var{pattern} @end example is used. To allow multiple patterns (which are ORed during the search) quoting is required: Spaces are to be translated into "+" or into "%20"; in turn this requires that the usual escape quoting rules are done. @example LISTSECRETKEYS @var{pattern} @end example Lists only the keys where a secret key is available. The list commands are affected by the option @example OPTION list-mode=@var{mode} @end example where mode may be: @table @code @item 0 Use default (which is usually the same as 1). @item 1 List only the internal keys. @item 2 List only the external keys. @item 3 List internal and external keys. @end table Note that options are valid for the entire session. @node GPGSM EXPORT @subsection Export certificates To export certificate from the internal key database the command: @example EXPORT [--data [--armor] [--base64]] [--] @var{pattern} @end example is used. To allow multiple patterns (which are ORed) quoting is required: Spaces are to be translated into "+" or into "%20"; in turn this requires that the usual escape quoting rules are done. If the @option{--data} option has not been given, the format of the output depends on what was set with the @code{OUTPUT} command. When using @acronym{PEM} encoding a few informational lines are prepended. If the @option{--data} has been given, a target set via @code{OUTPUT} is ignored and the data is returned inline using standard @code{D}-lines. This avoids the need for an extra file descriptor. In this case the options @option{--armor} and @option{--base64} may be used in the same way as with the @code{OUTPUT} command. @node GPGSM IMPORT @subsection Import certificates To import certificates into the internal key database, the command @example IMPORT [--re-import] @end example is used. The data is expected on the file descriptor set with the @code{INPUT} command. Certain checks are performed on the certificate. Note that the code will also handle PKCS#12 files and import private keys; a helper program is used for that. With the option @option{--re-import} the input data is expected to a be a linefeed separated list of fingerprints. The command will re-import the corresponding certificates; that is they are made permanent by removing their ephemeral flag. @node GPGSM DELETE @subsection Delete certificates To delete a certificate the command @example DELKEYS @var{pattern} @end example is used. To allow multiple patterns (which are ORed) quoting is required: Spaces are to be translated into "+" or into "%20"; in turn this requires that the usual escape quoting rules are done. The certificates must be specified unambiguously otherwise an error is returned. @node GPGSM GETAUDITLOG @subsection Retrieve an audit log @anchor{gpgsm-cmd getauditlog} This command is used to retrieve an audit log. @example GETAUDITLOG [--data] [--html] @end example If @option{--data} is used, the audit log is send using D-lines instead of being sent to the file descriptor given by an @code{OUTPUT} command. If @option{--html} is used, the output is formatted as an XHTML block. This is designed to be incorporated into a HTML document. @node GPGSM GETINFO @subsection Return information about the process This is a multipurpose function to return a variety of information. @example GETINFO @var{what} @end example The value of @var{what} specifies the kind of information returned: @table @code @item version Return the version of the program. @item pid Return the process id of the process. @item agent-check Return OK if the agent is running. @item cmd_has_option @var{cmd} @var{opt} Return OK if the command @var{cmd} implements the option @var{opt}. The leading two dashes usually used with @var{opt} shall not be given. @item offline Return OK if the connection is in offline mode. This may be either due to a @code{OPTION offline=1} or due to @command{gpgsm} being started with option @option{--disable-dirmngr}. @end table @node GPGSM OPTION @subsection Session options The standard Assuan option handler supports these options. @example OPTION @var{name}[=@var{value}] @end example These @var{name}s are recognized: @table @code @item putenv Change the session's environment to be passed via gpg-agent to Pinentry. @var{value} is a string of the form @code{<KEY>[=[<STRING>]]}. If only @code{<KEY>} is given the environment variable @code{<KEY>} is removed from the session environment, if @code{<KEY>=} is given that environment variable is set to the empty string, and if @code{<STRING>} is given it is set to that string. @item display @efindex DISPLAY Set the session environment variable @code{DISPLAY} is set to @var{value}. @item ttyname @efindex GPG_TTY Set the session environment variable @code{GPG_TTY} is set to @var{value}. @item ttytype @efindex TERM Set the session environment variable @code{TERM} is set to @var{value}. @item lc-ctype @efindex LC_CTYPE Set the session environment variable @code{LC_CTYPE} is set to @var{value}. @item lc-messages @efindex LC_MESSAGES Set the session environment variable @code{LC_MESSAGES} is set to @var{value}. @item xauthority @efindex XAUTHORITY Set the session environment variable @code{XAUTHORITY} is set to @var{value}. @item pinentry-user-data @efindex PINENTRY_USER_DATA Set the session environment variable @code{PINENTRY_USER_DATA} is set to @var{value}. @item include-certs This option overrides the command line option @option{--include-certs}. A @var{value} of -2 includes all certificates except for the root certificate, -1 includes all certificates, 0 does not include any certificates, 1 includes only the signers certificate and all other positive values include up to @var{value} certificates starting with the signer cert. @item list-mode @xref{gpgsm-cmd listkeys}. @item list-to-output If @var{value} is true the output of the list commands (@pxref{gpgsm-cmd listkeys}) is written to the file descriptor set with the last @code{OUTPUT} command. If @var{value} is false the output is written via data lines; this is the default. @item with-validation If @var{value} is true for each listed certificate the validation status is printed. This may result in the download of a CRL or the user being asked about the trustworthiness of a root certificate. The default is given by a command line option (@pxref{gpgsm-option --with-validation}). @item with-secret If @var{value} is true certificates with a corresponding private key are marked by the list commands. @item validation-model This option overrides the command line option @option{validation-model} for the session. (@xref{gpgsm-option --validation-model}.) @item with-key-data This option globally enables the command line option @option{--with-key-data}. (@xref{gpgsm-option --with-key-data}.) @item enable-audit-log If @var{value} is true data to write an audit log is gathered. (@xref{gpgsm-cmd getauditlog}.) @item allow-pinentry-notify If this option is used notifications about the launch of a Pinentry are passed back to the client. @item with-ephemeral-keys If @var{value} is true ephemeral certificates are included in the output of the list commands. @item no-encrypt-to If this option is used all keys set by the command line option @option{--encrypt-to} are ignored. @item offline If @var{value} is true or @var{value} is not given all network access is disabled for this session. This is the same as the command line option @option{--disable-dirmngr}. @end table @mansect see also @ifset isman @command{gpg2}(1), @command{gpg-agent}(1) @end ifset @include see-also-note.texi diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index bff7dd652..f3fe1d663 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -1,1048 +1,1049 @@ /* call-dirmngr.c - Communication with the dirmngr * Copyright (C) 2002, 2003, 2005, 2007, 2008, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <assert.h> #include <ctype.h> #include "gpgsm.h" #include <gcrypt.h> #include <assuan.h> #include "../common/i18n.h" #include "keydb.h" #include "../common/asshelp.h" struct membuf { size_t len; size_t size; char *buf; int out_of_core; }; /* fixme: We need a context for each thread or serialize the access to the dirmngr. */ static assuan_context_t dirmngr_ctx = NULL; static assuan_context_t dirmngr2_ctx = NULL; static int dirmngr_ctx_locked; static int dirmngr2_ctx_locked; struct inq_certificate_parm_s { ctrl_t ctrl; assuan_context_t ctx; ksba_cert_t cert; ksba_cert_t issuer_cert; }; struct isvalid_status_parm_s { ctrl_t ctrl; int seen; unsigned char fpr[20]; }; struct lookup_parm_s { ctrl_t ctrl; assuan_context_t ctx; void (*cb)(void *, ksba_cert_t); void *cb_value; struct membuf data; int error; }; struct run_command_parm_s { ctrl_t ctrl; assuan_context_t ctx; }; static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert); /* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter the code with out of core checks. */ static void init_membuf (struct membuf *mb, int initiallen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->buf = xtrymalloc (initiallen); if (!mb->buf) mb->out_of_core = 1; } static void put_membuf (struct membuf *mb, const void *buf, size_t len) { if (mb->out_of_core) return; if (mb->len + len >= mb->size) { char *p; mb->size += len + 1024; p = xtryrealloc (mb->buf, mb->size); if (!p) { mb->out_of_core = 1; return; } mb->buf = p; } memcpy (mb->buf + mb->len, buf, len); mb->len += len; } static void * get_membuf (struct membuf *mb, size_t *len) { char *p; if (mb->out_of_core) { xfree (mb->buf); mb->buf = NULL; return NULL; } p = mb->buf; *len = mb->len; mb->buf = NULL; mb->out_of_core = 1; /* don't allow a reuse */ return p; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername, int mode) { gpg_error_t err; char *serverversion; const char *myversion = strusage (13); err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_error (_("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) { char *warn; warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), servername, serverversion, myversion); if (!warn) err = gpg_error_from_syserror (); else { log_info (_("WARNING: %s\n"), warn); if (!opt.quiet) { log_info (_("Note: Outdated servers may lack important" " security fixes.\n")); log_info (_("Note: Use the command \"%s\" to restart them.\n"), "gpgconf --kill all"); } gpgsm_status2 (ctrl, STATUS_WARNING, "server_version_mismatch 0", warn, NULL); xfree (warn); } } xfree (serverversion); return err; } /* This function prepares the dirmngr for a new session. The audit-events option is used so that other dirmngr clients won't get disturbed by such events. */ static void prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) { struct keyserver_spec *server; if (!err) err = warn_version_mismatch (ctrl, ctx, DIRMNGR_NAME, 0); if (!err) { err = assuan_transact (ctx, "OPTION audit-events=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* Allow the use of old dirmngr versions. */ } audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); if (!ctx || err) return; server = opt.keyserver; while (server) { char line[ASSUAN_LINELENGTH]; char *user = server->user ? server->user : ""; char *pass = server->pass ? server->pass : ""; char *base = server->base ? server->base : ""; - snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s", - server->host, server->port, user, pass, base); + snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s:%s", + server->host, server->port, user, pass, base, + server->use_ldaps? "ldaps":""); assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); /* The code below is not required because we don't return an error. */ /* err = [above call] */ /* if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD) */ /* err = 0; /\* Allow the use of old dirmngr versions. *\/ */ server = server->next; } } /* Return a new assuan context for a Dirmngr connection. */ static gpg_error_t start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r) { gpg_error_t err; assuan_context_t ctx; if (opt.disable_dirmngr || ctrl->offline) return gpg_error (GPG_ERR_NO_DIRMNGR); if (*ctx_r) return 0; /* Note: if you change this to multiple connections, you also need to take care of the implicit option sending caching. */ err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, opt.autostart, opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { static int shown; if (!shown) { shown = 1; log_info (_("no dirmngr running in this session\n")); } } prepare_dirmngr (ctrl, ctx, err); if (err) return err; *ctx_r = ctx; return 0; } static int start_dirmngr (ctrl_t ctrl) { gpg_error_t err; assert (! dirmngr_ctx_locked); dirmngr_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr_ctx); /* We do not check ERR but the existence of a context because the error might come from a failed command send to the dirmngr. Fixme: Why don't we close the drimngr context if we encountered an error in prepare_dirmngr? */ if (!dirmngr_ctx) dirmngr_ctx_locked = 0; return err; } static void release_dirmngr (ctrl_t ctrl) { (void)ctrl; if (!dirmngr_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr ctx\n"); dirmngr_ctx_locked = 0; } static int start_dirmngr2 (ctrl_t ctrl) { gpg_error_t err; assert (! dirmngr2_ctx_locked); dirmngr2_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr2_ctx); if (!dirmngr2_ctx) dirmngr2_ctx_locked = 0; return err; } static void release_dirmngr2 (ctrl_t ctrl) { (void)ctrl; if (!dirmngr2_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr2 ctx\n"); dirmngr2_ctx_locked = 0; } /* Handle a SENDCERT inquiry. */ static gpg_error_t inq_certificate (void *opaque, const char *line) { struct inq_certificate_parm_s *parm = opaque; const char *s; int rc; size_t n; const unsigned char *der; size_t derlen; int issuer_mode = 0; ksba_sexp_t ski = NULL; if ((s = has_leading_keyword (line, "SENDCERT"))) { line = s; } else if ((s = has_leading_keyword (line, "SENDCERT_SKI"))) { /* Send a certificate where a sourceKeyIdentifier is included. */ line = s; ski = make_simple_sexp_from_hexstr (line, &n); line += n; while (*line == ' ') line++; } else if ((s = has_leading_keyword (line, "SENDISSUERCERT"))) { line = s; issuer_mode = 1; } else if ((s = has_leading_keyword (line, "ISTRUSTED"))) { /* The server is asking us whether the certificate is a trusted root certificate. */ char fpr[41]; struct rootca_flags_s rootca_flags; line = s; for (s=line,n=0; hexdigitp (s); s++, n++) ; if (*s || n != 40) return gpg_error (GPG_ERR_ASS_PARAMETER); for (s=line, n=0; n < 40; s++, n++) fpr[n] = (*s >= 'a')? (*s & 0xdf): *s; fpr[n] = 0; if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags)) rc = assuan_send_data (parm->ctx, "1", 1); else rc = 0; return rc; } else { log_error ("unsupported inquiry '%s'\n", line); return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } if (!*line) { /* Send the current certificate. */ der = ksba_cert_get_image (issuer_mode? parm->issuer_cert : parm->cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); } else if (issuer_mode) { log_error ("sending specific issuer certificate back " "is not yet implemented\n"); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } else { /* Send the given certificate. */ int err; ksba_cert_t cert; err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, 1); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } xfree (ski); return rc; } /* Take a 20 byte hexencoded string and put it into the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ } static gpg_error_t isvalid_status_cb (void *opaque, const char *line) { struct isvalid_status_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "ONLY_VALID_IF_CERT_VALID"))) { parm->seen++; if (!*s || !unhexify_fpr (s, parm->fpr)) parm->seen++; /* Bump it to indicate an error. */ } return 0; } /* Call the directory manager to check whether the certificate is valid Returns 0 for valid or usually one of the errors: GPG_ERR_CERTIFICATE_REVOKED GPG_ERR_NO_CRL_KNOWN GPG_ERR_CRL_TOO_OLD Values for USE_OCSP: 0 = Do CRL check. 1 = Do an OCSP check but fallback to CRL unless CRLS are disabled. 2 = Do only an OCSP check using only the default responder. */ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp) { static int did_options; int rc; char *certid, *certfpr; char line[ASSUAN_LINELENGTH]; struct inq_certificate_parm_s parm; struct isvalid_status_parm_s stparm; rc = start_dirmngr (ctrl); if (rc) return rc; certfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); certid = gpgsm_get_certid (cert); if (!certid) { log_error ("error getting the certificate ID\n"); release_dirmngr (ctrl); return gpg_error (GPG_ERR_GENERAL); } if (opt.verbose > 1) { char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); log_info ("asking dirmngr about %s%s\n", fpr, use_ocsp? " (using OCSP)":""); xfree (fpr); } parm.ctx = dirmngr_ctx; parm.ctrl = ctrl; parm.cert = cert; parm.issuer_cert = issuer_cert; stparm.ctrl = ctrl; stparm.seen = 0; memset (stparm.fpr, 0, 20); /* It is sufficient to send the options only once because we have * one connection per process only. */ if (!did_options) { if (opt.force_crl_refresh) assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1", NULL, NULL, NULL, NULL, NULL, NULL); did_options = 1; } snprintf (line, DIM(line), "ISVALID%s%s %s%s%s", use_ocsp == 2 || opt.no_crl_check ? " --only-ocsp":"", use_ocsp == 2? " --force-default-responder":"", certid, use_ocsp? " ":"", use_ocsp? certfpr:""); xfree (certid); xfree (certfpr); rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm, isvalid_status_cb, &stparm); if (opt.verbose > 1) log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); if (!rc && stparm.seen) { /* Need to also check the certificate validity. */ if (stparm.seen != 1) { log_error ("communication problem with dirmngr detected\n"); rc = gpg_error (GPG_ERR_INV_CRL); } else { ksba_cert_t rspcert = NULL; if (get_cached_cert (dirmngr_ctx, stparm.fpr, &rspcert)) { /* Ooops: Something went wrong getting the certificate from the dirmngr. Try our own cert store now. */ KEYDB_HANDLE kh; kh = keydb_new (); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); if (!rc) rc = keydb_search_fpr (ctrl, kh, stparm.fpr); if (!rc) rc = keydb_get_cert (kh, &rspcert); if (rc) { log_error ("unable to find the certificate used " "by the dirmngr: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } keydb_release (kh); } if (!rc) { rc = gpgsm_cert_use_ocsp_p (rspcert); if (rc) rc = gpg_error (GPG_ERR_INV_CRL); else { /* Note the no_dirmngr flag: This avoids checking this certificate over and over again. */ rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL, VALIDATE_FLAG_NO_DIRMNGR, NULL); if (rc) { log_error ("invalid certificate used for CRL/OCSP: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } } } ksba_cert_release (rspcert); } } release_dirmngr (ctrl); return rc; } /* Lookup helpers*/ static gpg_error_t lookup_cb (void *opaque, const void *buffer, size_t length) { struct lookup_parm_s *parm = opaque; size_t len; char *buf; ksba_cert_t cert; int rc; if (parm->error) return 0; if (buffer) { put_membuf (&parm->data, buffer, length); return 0; } /* END encountered - process what we have */ buf = get_membuf (&parm->data, &len); if (!buf) { parm->error = gpg_error (GPG_ERR_ENOMEM); return 0; } rc = ksba_cert_new (&cert); if (rc) { parm->error = rc; return 0; } rc = ksba_cert_init_from_mem (cert, buf, len); if (rc) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc)); } else { parm->cb (parm->cb_value, cert); } ksba_cert_release (cert); init_membuf (&parm->data, 4096); return 0; } /* Return a properly escaped pattern from NAMES. The only error return is NULL to indicate a malloc failure. */ static char * pattern_from_strlist (strlist_t names) { strlist_t sl; int n; const char *s; char *pattern, *p; for (n=0, sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++, n++) { if (*s == '%' || *s == ' ' || *s == '+') n += 2; } n++; } p = pattern = xtrymalloc (n+1); if (!pattern) return NULL; for (sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++) { switch (*s) { case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break; case ' ': *p++ = '%'; *p++ = '2'; *p++ = '0'; break; case '+': *p++ = '%'; *p++ = '2'; *p++ = 'B'; break; default: *p++ = *s; break; } } *p++ = ' '; } if (p == pattern) *pattern = 0; /* is empty */ else p[-1] = '\0'; /* remove trailing blank */ return pattern; } static gpg_error_t lookup_status_cb (void *opaque, const char *line) { struct lookup_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "TRUNCATED"))) { if (parm->ctrl) { line = s; gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line); } } return 0; } /* Run the Directory Manager's lookup command using the pattern compiled from the strings given in NAMES. The caller must provide the callback CB which will be passed cert by cert. Note that CTRL is optional. With CACHE_ONLY the dirmngr will search only its own key cache. */ int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value) { int rc; char *pattern; char line[ASSUAN_LINELENGTH]; struct lookup_parm_s parm; size_t len; assuan_context_t ctx; /* The lookup function can be invoked from the callback of a lookup function, for example to walk the chain. */ if (!dirmngr_ctx_locked) { rc = start_dirmngr (ctrl); if (rc) return rc; ctx = dirmngr_ctx; } else if (!dirmngr2_ctx_locked) { rc = start_dirmngr2 (ctrl); if (rc) return rc; ctx = dirmngr2_ctx; } else { log_fatal ("both dirmngr contexts are in use\n"); } pattern = pattern_from_strlist (names); if (!pattern) { if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); return out_of_core (); } snprintf (line, DIM(line), "LOOKUP%s %s", cache_only? " --cache-only":"", pattern); xfree (pattern); parm.ctrl = ctrl; parm.ctx = ctx; parm.cb = cb; parm.cb_value = cb_value; parm.error = 0; init_membuf (&parm.data, 4096); rc = assuan_transact (ctx, line, lookup_cb, &parm, NULL, NULL, lookup_status_cb, &parm); xfree (get_membuf (&parm.data, &len)); if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); if (rc) return rc; return parm.error; } static gpg_error_t get_cached_cert_data_cb (void *opaque, const void *buffer, size_t length) { struct membuf *mb = opaque; if (buffer) put_membuf (mb, buffer, length); return 0; } /* Return a certificate from the Directory Manager's cache. This function only returns one certificate which must be specified using the fingerprint FPR and will be stored at R_CERT. On error NULL is stored at R_CERT and an error code returned. Note that the caller must provide the locked dirmngr context CTX. */ static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char hexfpr[2*20+1]; struct membuf mb; char *buf; size_t buflen = 0; ksba_cert_t cert; *r_cert = NULL; bin2hex (fpr, 20, hexfpr); snprintf (line, DIM(line), "LOOKUP --single --cache-only 0x%s", hexfpr); init_membuf (&mb, 4096); err = assuan_transact (ctx, line, get_cached_cert_data_cb, &mb, NULL, NULL, NULL, NULL); buf = get_membuf (&mb, &buflen); if (err) { xfree (buf); return err; } if (!buf) return gpg_error (GPG_ERR_ENOMEM); err = ksba_cert_new (&cert); if (err) { xfree (buf); return err; } err = ksba_cert_init_from_mem (cert, buf, buflen); xfree (buf); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); ksba_cert_release (cert); return err; } *r_cert = cert; return 0; } /* Run Command helpers*/ /* Fairly simple callback to write all output of dirmngr to stdout. */ static gpg_error_t run_command_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; if (buffer) { if ( fwrite (buffer, length, 1, stdout) != 1 ) log_error ("error writing to stdout: %s\n", strerror (errno)); } return 0; } /* Handle inquiries from the dirmngr COMMAND. */ static gpg_error_t run_command_inq_cb (void *opaque, const char *line) { struct run_command_parm_s *parm = opaque; const char *s; int rc = 0; if ((s = has_leading_keyword (line, "SENDCERT"))) { /* send the given certificate */ int err; ksba_cert_t cert; const unsigned char *der; size_t derlen; line = s; if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, 1); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } else if ((s = has_leading_keyword (line, "PRINTINFO"))) { /* Simply show the message given in the argument. */ line = s; log_info ("dirmngr: %s\n", line); } else { log_error ("unsupported inquiry '%s'\n", line); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } return rc; } static gpg_error_t run_command_status_cb (void *opaque, const char *line) { ctrl_t ctrl = opaque; const char *s; if (opt.verbose) { log_info ("dirmngr status: %s\n", line); } if ((s = has_leading_keyword (line, "PROGRESS"))) { if (ctrl) { line = s; if (gpgsm_status (ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } return 0; } /* Pass COMMAND to dirmngr and print all output generated by Dirmngr to stdout. A couple of inquiries are defined (see above). ARGC arguments in ARGV are given to the Dirmngr. Spaces, plus and percent characters within the argument strings are percent escaped so that blanks can act as delimiters. */ int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv) { int rc; int i; const char *s; char *line, *p; size_t len; struct run_command_parm_s parm; rc = start_dirmngr (ctrl); if (rc) return rc; parm.ctrl = ctrl; parm.ctx = dirmngr_ctx; len = strlen (command) + 1; for (i=0; i < argc; i++) len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */ line = xtrymalloc (len); if (!line) { release_dirmngr (ctrl); return out_of_core (); } p = stpcpy (line, command); for (i=0; i < argc; i++) { *p++ = ' '; for (s=argv[i]; *s; s++) { if (!isascii (*s)) *p++ = *s; else if (*s == ' ') *p++ = '+'; else if (!isprint (*s) || *s == '+') { sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } else *p++ = *s; } } *p = 0; rc = assuan_transact (dirmngr_ctx, line, run_command_cb, NULL, run_command_inq_cb, &parm, run_command_status_cb, ctrl); xfree (line); log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); release_dirmngr (ctrl); return rc; } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f5837079d..2cd3b0c4f 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1,2329 +1,2363 @@ /* gpgsm.c - GnuPG for S/MIME * Copyright (C) 2001-2008, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2008, 2010 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. */ #include <config.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <fcntl.h> /*#include <mcheck.h>*/ #include "gpgsm.h" #include <gcrypt.h> #include <assuan.h> /* malloc hooks */ #include "passphrase.h" #include "../common/shareddefs.h" #include "../kbx/keybox.h" /* malloc hooks */ #include "../common/i18n.h" #include "keydb.h" #include "../common/sysutils.h" #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "../common/init.h" #include "../common/compliance.h" #ifndef O_BINARY #define O_BINARY 0 #endif enum cmd_and_opt_values { aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', aListKeys = 'k', aListSecretKeys = 'K', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oRecipient = 'r', aSign = 's', oUser = 'u', oVerbose = 'v', oBatch = 500, aClearsign, aKeygen, aSignEncr, aDeleteKey, aImport, aVerify, aListExternalKeys, aListChain, aSendKeys, aRecvKeys, aExport, aExportSecretKeyP12, aExportSecretKeyP8, aExportSecretKeyRaw, aServer, aLearnCard, aCallDirmngr, aCallProtectTool, aPasswd, aGPGConfList, aGPGConfTest, aDumpKeys, aDumpChain, aDumpSecretKeys, aDumpExternalKeys, aKeydbClearSomeCertFlags, aFingerprint, oOptions, oDebug, oDebugLevel, oDebugAll, oDebugNone, oDebugWait, oDebugAllowCoreDump, oDebugNoChainValidation, oDebugIgnoreExpiration, oLogFile, oNoLogFile, oAuditLog, oHtmlAuditLog, oEnableSpecialFilenames, oAgentProgram, oDisplay, oTTYname, oTTYtype, oLCctype, oLCmessages, oXauthority, oPreferSystemDirmngr, oDirmngrProgram, oDisableDirmngr, oProtectToolProgram, oFakedSystemTime, oPassphraseFD, oPinentryMode, oRequestOrigin, oAssumeArmor, oAssumeBase64, oAssumeBinary, oBase64, oNoArmor, oP12Charset, oCompliance, oDisableCRLChecks, oEnableCRLChecks, oDisableTrustedCertCRLCheck, oEnableTrustedCertCRLCheck, oForceCRLRefresh, oDisableOCSP, oEnableOCSP, oIncludeCerts, oPolicyFile, oDisablePolicyChecks, oEnablePolicyChecks, oAutoIssuerKeyRetrieve, oWithFingerprint, oWithMD5Fingerprint, oWithKeygrip, oWithSecret, oWithKeyScreening, oAnswerYes, oAnswerNo, oKeyring, oDefaultKey, oDefRecipient, oDefRecipientSelf, oNoDefRecipient, oStatusFD, oCipherAlgo, oDigestAlgo, oExtraDigestAlgo, oNoVerbose, oNoSecmemWarn, oNoDefKeyring, oNoGreeting, oNoTTY, oNoOptions, oNoBatch, oHomedir, oWithColons, oWithKeyData, oWithValidation, oWithEphemeralKeys, oSkipVerify, oValidationModel, oKeyServer, oEncryptTo, oNoEncryptTo, oLoggerFD, oDisableCipherAlgo, oDisablePubkeyAlgo, oIgnoreTimeConflict, oNoRandomSeedFile, oNoCommonCertsImport, oIgnoreCertExtension, oAuthenticode, oAttribute, oNoAutostart }; static ARGPARSE_OPTS opts[] = { ARGPARSE_group (300, N_("@Commands:\n ")), ARGPARSE_c (aSign, "sign", N_("make a signature")), /*ARGPARSE_c (aClearsign, "clearsign", N_("make a clear text signature") ),*/ ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")), ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")), /*ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),*/ ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")), ARGPARSE_c (aVerify, "verify", N_("verify a signature")), ARGPARSE_c (aListKeys, "list-keys", N_("list keys")), ARGPARSE_c (aListExternalKeys, "list-external-keys", N_("list external keys")), ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")), ARGPARSE_c (aListChain, "list-chain", N_("list certificate chain")), ARGPARSE_c (aFingerprint, "fingerprint", N_("list keys and fingerprints")), ARGPARSE_c (aKeygen, "generate-key", N_("generate a new key pair")), ARGPARSE_c (aKeygen, "gen-key", "@"), ARGPARSE_c (aDeleteKey, "delete-keys", N_("remove keys from the public keyring")), /*ARGPARSE_c (aSendKeys, "send-keys", N_("export keys to a keyserver")),*/ /*ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a keyserver")),*/ ARGPARSE_c (aImport, "import", N_("import certificates")), ARGPARSE_c (aExport, "export", N_("export certificates")), /* We use -raw and not -p1 for pkcs#1 secret key export so that it won't accidentally be used in case -p12 was intended. */ ARGPARSE_c (aExportSecretKeyP12, "export-secret-key-p12", "@"), ARGPARSE_c (aExportSecretKeyP8, "export-secret-key-p8", "@"), ARGPARSE_c (aExportSecretKeyRaw, "export-secret-key-raw", "@"), ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")), ARGPARSE_c (aServer, "server", N_("run in server mode")), ARGPARSE_c (aCallDirmngr, "call-dirmngr", N_("pass a command to the dirmngr")), ARGPARSE_c (aCallProtectTool, "call-protect-tool", N_("invoke gpg-protect-tool")), ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")), ARGPARSE_c (aPasswd, "passwd", "@"), ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), ARGPARSE_c (aDumpKeys, "dump-cert", "@"), ARGPARSE_c (aDumpKeys, "dump-keys", "@"), ARGPARSE_c (aDumpChain, "dump-chain", "@"), ARGPARSE_c (aDumpExternalKeys, "dump-external-keys", "@"), ARGPARSE_c (aDumpSecretKeys, "dump-secret-keys", "@"), ARGPARSE_c (aKeydbClearSomeCertFlags, "keydb-clear-some-cert-flags", "@"), ARGPARSE_group (301, N_("@\nOptions:\n ")), ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")), ARGPARSE_s_n (oArmor, "armour", "@"), ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")), ARGPARSE_s_s (oP12Charset, "p12-charset", "@"), ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"), ARGPARSE_s_n (oAssumeArmor, "assume-armor", N_("assume input is in PEM format")), ARGPARSE_s_n (oAssumeBase64, "assume-base64", N_("assume input is in base-64 format")), ARGPARSE_s_n (oAssumeBinary, "assume-binary", N_("assume input is in binary format")), ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), ARGPARSE_s_n (oPreferSystemDirmngr,"prefer-system-dirmngr", "@"), ARGPARSE_s_n (oDisableCRLChecks, "disable-crl-checks", N_("never consult a CRL")), ARGPARSE_s_n (oEnableCRLChecks, "enable-crl-checks", "@"), ARGPARSE_s_n (oDisableTrustedCertCRLCheck, "disable-trusted-cert-crl-check", "@"), ARGPARSE_s_n (oEnableTrustedCertCRLCheck, "enable-trusted-cert-crl-check", "@"), ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"), ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"), ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")), ARGPARSE_s_s (oValidationModel, "validation-model", "@"), ARGPARSE_s_i (oIncludeCerts, "include-certs", N_("|N|number of certificates to include") ), ARGPARSE_s_s (oPolicyFile, "policy-file", N_("|FILE|take policy information from FILE")), ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks", N_("do not check certificate policies")), ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"), ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", N_("fetch missing issuer certificates")), ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"), ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"), ARGPARSE_s_s (oUser, "local-user", N_("|USER-ID|use USER-ID to sign or decrypt")), ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a server mode log to FILE")), ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), ARGPARSE_s_s (oAuditLog, "audit-log", N_("|FILE|write an audit log to FILE")), ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"), ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")), ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")), ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")), ARGPARSE_s_s (oKeyring, "keyring", N_("|FILE|add keyring to the list of keyrings")), ARGPARSE_s_s (oDefaultKey, "default-key", N_("|USER-ID|use USER-ID as default secret key")), /* Not yet used: */ /* ARGPARSE_s_s (oDefRecipient, "default-recipient", */ /* N_("|NAME|use NAME as default recipient")), */ /* ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", */ /* N_("use the default key as default recipient")), */ /* ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"), */ ARGPARSE_s_s (oKeyServer, "keyserver", N_("|SPEC|use this keyserver to lookup keys")), ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")), ARGPARSE_s_s (oDebug, "debug", "@"), ARGPARSE_s_s (oDebugLevel, "debug-level", N_("|LEVEL|set the debugging level to LEVEL")), ARGPARSE_s_n (oDebugAll, "debug-all", "@"), ARGPARSE_s_n (oDebugNone, "debug-none", "@"), ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), ARGPARSE_s_s (oCipherAlgo, "cipher-algo", N_("|NAME|use cipher algorithm NAME")), ARGPARSE_s_s (oDigestAlgo, "digest-algo", N_("|NAME|use message digest algorithm NAME")), ARGPARSE_s_s (oExtraDigestAlgo, "extra-digest-algo", "@"), ARGPARSE_group (302, N_( "@\n(See the man page for a complete listing of all commands and options)\n" )), /* Hidden options. */ ARGPARSE_s_s (oCompliance, "compliance", "@"), ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), ARGPARSE_s_n (oNoArmor, "no-armor", "@"), ARGPARSE_s_n (oNoArmor, "no-armour", "@"), ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"), ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), ARGPARSE_s_n (oNoOptions, "no-options", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), ARGPARSE_s_s (oDisplay, "display", "@"), ARGPARSE_s_s (oTTYname, "ttyname", "@"), ARGPARSE_s_s (oTTYtype, "ttytype", "@"), ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), ARGPARSE_s_s (oXauthority, "xauthority", "@"), ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"), ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", "@"), ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), ARGPARSE_s_n (oNoBatch, "no-batch", "@"), ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"), ARGPARSE_s_n (oWithValidation, "with-validation", "@"), ARGPARSE_s_n (oWithMD5Fingerprint, "with-md5-fingerprint", "@"), ARGPARSE_s_n (oWithEphemeralKeys, "with-ephemeral-keys", "@"), ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"), ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"), ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"), ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"), ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), ARGPARSE_s_n (oAuthenticode, "authenticode", "@"), ARGPARSE_s_s (oAttribute, "attribute", "@"), /* Command aliases. */ ARGPARSE_c (aListKeys, "list-key", "@"), ARGPARSE_c (aListChain, "list-signatures", "@"), ARGPARSE_c (aListChain, "list-sigs", "@"), ARGPARSE_c (aListChain, "check-signatures", "@"), ARGPARSE_c (aListChain, "check-sigs", "@"), ARGPARSE_c (aDeleteKey, "delete-key", "@"), ARGPARSE_end () }; /* The list of supported debug flags. */ static struct debug_flags_s debug_flags [] = { { DBG_X509_VALUE , "x509" }, { DBG_MPI_VALUE , "mpi" }, { DBG_CRYPTO_VALUE , "crypto" }, { DBG_MEMORY_VALUE , "memory" }, { DBG_CACHE_VALUE , "cache" }, { DBG_MEMSTAT_VALUE, "memstat" }, { DBG_HASHING_VALUE, "hashing" }, { DBG_IPC_VALUE , "ipc" }, { DBG_CLOCK_VALUE , "clock" }, { DBG_LOOKUP_VALUE , "lookup" }, { 0, NULL } }; /* Global variable to keep an error count. */ int gpgsm_errors_seen = 0; /* It is possible that we are currentlu running under setuid permissions */ static int maybe_setuid = 1; /* Helper to implement --debug-level and --debug*/ static const char *debug_level; static unsigned int debug_value; /* Default value for include-certs. We need an extra macro for gpgconf-list because the variable will be changed by the command line option. It is often cumbersome to locate intermediate certificates, thus by default we include all certificates in the chain. However we leave out the root certificate because that would make it too easy for the recipient to import that root certificate. A root certificate should be installed only after due checks and thus it won't help to send it along with each message. */ #define DEFAULT_INCLUDE_CERTS -2 /* Include all certs but root. */ static int default_include_certs = DEFAULT_INCLUDE_CERTS; /* Whether the chain mode shall be used for validation. */ static int default_validation_model; /* The default cipher algo. */ #define DEFAULT_CIPHER_ALGO "AES" static char *build_list (const char *text, const char *(*mapf)(int), int (*chkf)(int)); static void set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void emergency_cleanup (void); static int open_read (const char *filename); static estream_t open_es_fread (const char *filename, const char *mode); static estream_t open_es_fwrite (const char *filename); static void run_protect_tool (int argc, char **argv); static int our_pk_test_algo (int algo) { switch (algo) { case GCRY_PK_RSA: case GCRY_PK_ECDSA: return gcry_pk_test_algo (algo); default: return 1; } } static int our_cipher_test_algo (int algo) { switch (algo) { case GCRY_CIPHER_3DES: case GCRY_CIPHER_AES128: case GCRY_CIPHER_AES192: case GCRY_CIPHER_AES256: case GCRY_CIPHER_SERPENT128: case GCRY_CIPHER_SERPENT192: case GCRY_CIPHER_SERPENT256: case GCRY_CIPHER_SEED: case GCRY_CIPHER_CAMELLIA128: case GCRY_CIPHER_CAMELLIA192: case GCRY_CIPHER_CAMELLIA256: return gcry_cipher_test_algo (algo); default: return 1; } } static int our_md_test_algo (int algo) { switch (algo) { case GCRY_MD_MD5: case GCRY_MD_SHA1: case GCRY_MD_RMD160: case GCRY_MD_SHA224: case GCRY_MD_SHA256: case GCRY_MD_SHA384: case GCRY_MD_SHA512: case GCRY_MD_WHIRLPOOL: return gcry_md_test_algo (algo); default: return 1; } } static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { const char *s; char *result; if (maybe_setuid) { gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ maybe_setuid = 0; } s = getfnc (NULL); result = xmalloc (strlen (libname) + 1 + strlen (s) + 1); strcpy (stpcpy (stpcpy (result, libname), " "), s); return result; } static const char * my_strusage( int level ) { static char *digests, *pubkeys, *ciphers; static char *ver_gcry, *ver_ksba; const char *p; switch (level) { case 11: p = "@GPGSM@ (@GNUPG@)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: @GPGSM@ [options] [files] (-h for help)"); break; case 41: p = _("Syntax: @GPGSM@ [options] [files]\n" "Sign, check, encrypt or decrypt using the S/MIME protocol\n" "Default operation depends on the input data\n"); break; case 20: if (!ver_gcry) ver_gcry = make_libversion ("libgcrypt", gcry_check_version); p = ver_gcry; break; case 21: if (!ver_ksba) ver_ksba = make_libversion ("libksba", ksba_check_version); p = ver_ksba; break; case 31: p = "\nHome: "; break; case 32: p = gnupg_homedir (); break; case 33: p = _("\nSupported algorithms:\n"); break; case 34: if (!ciphers) ciphers = build_list ("Cipher: ", gnupg_cipher_algo_name, our_cipher_test_algo ); p = ciphers; break; case 35: if (!pubkeys) pubkeys = build_list ("Pubkey: ", gcry_pk_algo_name, our_pk_test_algo ); p = pubkeys; break; case 36: if (!digests) digests = build_list("Hash: ", gcry_md_algo_name, our_md_test_algo ); p = digests; break; default: p = NULL; break; } return p; } static char * build_list (const char *text, const char * (*mapf)(int), int (*chkf)(int)) { int i; size_t n=strlen(text)+2; char *list, *p; if (maybe_setuid) { gcry_control (GCRYCTL_DROP_PRIVS); /* drop setuid */ } for (i=1; i < 400; i++ ) if (!chkf(i)) n += strlen(mapf(i)) + 2; list = xmalloc (21 + n); *list = 0; for (p=NULL, i=1; i < 400; i++) { if (!chkf(i)) { if( !p ) p = stpcpy (list, text ); else p = stpcpy (p, ", "); p = stpcpy (p, mapf(i) ); } } if (p) strcpy (p, "\n" ); return list; } /* Set the file pointer into binary mode if required. */ static void set_binary (FILE *fp) { #ifdef HAVE_DOSISH_SYSTEM setmode (fileno (fp), O_BINARY); #else (void)fp; #endif } static void wrong_args (const char *text) { fprintf (stderr, _("usage: %s [options] %s\n"), GPGSM_NAME, text); gpgsm_exit (2); } static void set_opt_session_env (const char *name, const char *value) { gpg_error_t err; err = session_env_setenv (opt.session_env, name, value); if (err) log_fatal ("error setting session environment: %s\n", gpg_strerror (err)); } /* Setup the debugging. With a DEBUG_LEVEL of NULL only the active debug flags are propagated to the subsystems. With DEBUG_LEVEL set, a specific set of debug flags is set; and individual debugging flags will be added on top. */ static void set_debug (void) { int numok = (debug_level && digitp (debug_level)); int numlvl = numok? atoi (debug_level) : 0; if (!debug_level) ; else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) opt.debug = 0; else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) opt.debug = DBG_IPC_VALUE; else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) opt.debug = DBG_IPC_VALUE|DBG_X509_VALUE; else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE |DBG_CACHE_VALUE|DBG_CRYPTO_VALUE); else if (!strcmp (debug_level, "guru") || numok) { opt.debug = ~0; /* Unless the "guru" string has been used we don't want to allow hashing debugging. The rationale is that people tend to 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); } else { log_error (_("invalid debug-level '%s' given\n"), debug_level); gpgsm_exit (2); } opt.debug |= debug_value; if (opt.debug && !opt.verbose) opt.verbose = 1; if (opt.debug) opt.quiet = 0; if (opt.debug & DBG_MPI_VALUE) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); if (opt.debug & DBG_CRYPTO_VALUE ) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); if (opt.debug) parse_debug_flag (NULL, &opt.debug, debug_flags); } static void set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd) { enum cmd_and_opt_values cmd = *ret_cmd; if (!cmd || cmd == new_cmd) cmd = new_cmd; else if ( cmd == aSign && new_cmd == aEncr ) cmd = aSignEncr; else if ( cmd == aEncr && new_cmd == aSign ) cmd = aSignEncr; else if ( (cmd == aSign && new_cmd == aClearsign) || (cmd == aClearsign && new_cmd == aSign) ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); gpgsm_exit(2); } *ret_cmd = cmd; } /* Helper to add recipients to a list. */ static void do_add_recipient (ctrl_t ctrl, const char *name, certlist_t *recplist, int is_encrypt_to, int recp_required) { int rc = gpgsm_add_to_certlist (ctrl, name, 0, recplist, is_encrypt_to); if (rc) { if (recp_required) { log_error ("can't encrypt to '%s': %s\n", name, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_INV_RECP, get_inv_recpsgnr_code (rc), name, NULL); } else log_info (_("Note: won't be able to encrypt to '%s': %s\n"), name, gpg_strerror (rc)); } } static void parse_validation_model (const char *model) { int i = gpgsm_parse_validation_model (model); if (i == -1) log_error (_("unknown validation model '%s'\n"), model); else default_validation_model = i; } /* Release the list of SERVERS. As usual it is okay to call this function with SERVERS passed as NULL. */ void keyserver_list_free (struct keyserver_spec *servers) { while (servers) { struct keyserver_spec *tmp = servers->next; xfree (servers->host); xfree (servers->user); if (servers->pass) memset (servers->pass, 0, strlen (servers->pass)); xfree (servers->pass); xfree (servers->base); xfree (servers); servers = tmp; } } /* See also dirmngr ldapserver_parse_one(). */ struct keyserver_spec * parse_keyserver_line (char *line, const char *filename, unsigned int lineno) { char *p; char *endp; + const char *s; struct keyserver_spec *server; int fieldno; int fail = 0; + int i; + + if (!filename) + { + filename = "[cmd]"; + lineno = 0; + } /* Parse the colon separated fields. */ server = xcalloc (1, sizeof *server); for (fieldno = 1, p = line; p; p = endp, fieldno++ ) { endp = strchr (p, ':'); if (endp) *endp++ = '\0'; trim_spaces (p); switch (fieldno) { case 1: if (*p) - server->host = xstrdup (p); + server->host = xstrdup (p); else { log_error (_("%s:%u: no hostname given\n"), filename, lineno); fail = 1; } break; case 2: if (*p) server->port = atoi (p); break; case 3: if (*p) server->user = xstrdup (p); break; case 4: if (*p && !server->user) { log_error (_("%s:%u: password given without user\n"), filename, lineno); fail = 1; } else if (*p) server->pass = xstrdup (p); break; case 5: if (*p) server->base = xstrdup (p); break; + case 6: + { + char **flags = NULL; + + flags = strtokenize (p, ","); + if (!flags) + log_fatal ("strtokenize failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + + for (i=0; (s = flags[i]); i++) + { + if (!*s) + ; + else if (!ascii_strcasecmp (s, "ldaps")) + server->use_ldaps = 1; + else if (!ascii_strcasecmp (s, "ldap")) + server->use_ldaps = 0; + else + log_info (_("%s:%u: ignoring unknown flag '%s'\n"), + filename, lineno, s); + } + + xfree (flags); + } + break; + default: /* (We silently ignore extra fields.) */ break; } } if (fail) { log_info (_("%s:%u: skipping this line\n"), filename, lineno); keyserver_list_free (server); server = NULL; } return server; } int main ( int argc, char **argv) { ARGPARSE_ARGS pargs; int orig_argc; char **orig_argv; /* char *username;*/ int may_coredump; strlist_t sl, remusr= NULL, locusr=NULL; strlist_t nrings=NULL; int detached_sig = 0; FILE *configfp = NULL; char *configname = NULL; unsigned configlineno; int parse_debug = 0; int no_more_options = 0; int default_config =1; int default_keyring = 1; char *logfile = NULL; char *auditlog = NULL; char *htmlauditlog = NULL; int greeting = 0; int nogreeting = 0; int debug_wait = 0; int use_random_seed = 1; int no_common_certs_import = 0; int with_fpr = 0; const char *forced_digest_algo = NULL; const char *extra_digest_algo = NULL; enum cmd_and_opt_values cmd = 0; struct server_control_s ctrl; certlist_t recplist = NULL; certlist_t signerlist = NULL; int do_not_setup_keys = 0; int recp_required = 0; estream_t auditfp = NULL; estream_t htmlauditfp = NULL; struct assuan_malloc_hooks malloc_hooks; int pwfd = -1; /*mtrace();*/ early_system_init (); gnupg_reopen_std (GPGSM_NAME); /* trap_unaligned ();*/ gnupg_rl_initialize (); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); /* Please note that we may running SUID(ROOT), so be very CAREFUL when adding any stuff between here and the call to secmem_init() somewhere after the option parsing */ log_set_prefix (GPGSM_NAME, GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init (); init_common_subsystems (&argc, &argv); /* Check that the libraries are suitable. Do it here because the option parse may need services of the library */ if (!ksba_check_version (NEED_KSBA_VERSION) ) log_fatal (_("%s is too old (need %s, have %s)\n"), "libksba", NEED_KSBA_VERSION, ksba_check_version (NULL) ); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps (); gnupg_init_signals (0, emergency_cleanup); dotlock_create (NULL, 0); /* Register lockfile cleanup. */ /* Tell the compliance module who we are. */ gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPGSM); opt.autostart = 1; opt.session_env = session_env_new (); if (!opt.session_env) log_fatal ("error allocating session environment block: %s\n", strerror (errno)); /* Note: If you change this default cipher algorithm , please remember to update the Gpgconflist entry as well. */ opt.def_cipher_algoid = DEFAULT_CIPHER_ALGO; /* First check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ while (arg_parse( &pargs, opts)) { if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll) parse_debug++; else if (pargs.r_opt == oOptions) { /* yes there is one, so we do not try the default one but read the config file when it is encountered at the commandline */ default_config = 0; } else if (pargs.r_opt == oNoOptions) { default_config = 0; /* --no-options */ opt.no_homedir_creation = 1; } else if (pargs.r_opt == oHomedir) gnupg_set_homedir (pargs.r.ret_str); else if (pargs.r_opt == aCallProtectTool) break; /* This break makes sure that --version and --help are passed to the protect-tool. */ } /* Initialize the secure memory. */ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); maybe_setuid = 0; /* Now we are now working under our real uid */ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); malloc_hooks.malloc = gcry_malloc; malloc_hooks.realloc = gcry_realloc; malloc_hooks.free = gcry_free; assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); setup_libassuan_logging (&opt.debug, NULL); /* Setup a default control structure for command line mode */ memset (&ctrl, 0, sizeof ctrl); gpgsm_init_default_ctrl (&ctrl); ctrl.no_server = 1; ctrl.status_fd = -1; /* No status output. */ ctrl.autodetect_encoding = 1; /* Set the default option file */ if (default_config ) configname = make_filename (gnupg_homedir (), GPGSM_NAME EXTSEP_S "conf", NULL); /* Set the default policy file */ opt.policy_file = make_filename (gnupg_homedir (), "policies.txt", NULL); argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags = 1; /* do not remove the args */ next_pass: if (configname) { configlineno = 0; configfp = fopen (configname, "r"); if (!configfp) { if (default_config) { if (parse_debug) log_info (_("Note: no default option file '%s'\n"), configname); } else { log_error (_("option file '%s': %s\n"), configname, strerror(errno)); gpgsm_exit(2); } xfree(configname); configname = NULL; } if (parse_debug && configname) log_info (_("reading options from '%s'\n"), configname); default_config = 0; } while (!no_more_options && optfile_parse (configfp, configname, &configlineno, &pargs, opts)) { switch (pargs.r_opt) { case aGPGConfList: case aGPGConfTest: set_cmd (&cmd, pargs.r_opt); do_not_setup_keys = 1; nogreeting = 1; break; case aServer: opt.batch = 1; set_cmd (&cmd, aServer); break; case aCallDirmngr: opt.batch = 1; set_cmd (&cmd, aCallDirmngr); do_not_setup_keys = 1; break; case aCallProtectTool: opt.batch = 1; set_cmd (&cmd, aCallProtectTool); no_more_options = 1; /* Stop parsing. */ do_not_setup_keys = 1; break; case aDeleteKey: set_cmd (&cmd, aDeleteKey); /*greeting=1;*/ do_not_setup_keys = 1; break; case aDetachedSign: detached_sig = 1; set_cmd (&cmd, aSign ); break; case aKeygen: set_cmd (&cmd, aKeygen); greeting=1; do_not_setup_keys = 1; break; case aImport: case aSendKeys: case aRecvKeys: case aExport: case aExportSecretKeyP12: case aExportSecretKeyP8: case aExportSecretKeyRaw: case aDumpKeys: case aDumpChain: case aDumpExternalKeys: case aDumpSecretKeys: case aListKeys: case aListExternalKeys: case aListSecretKeys: case aListChain: case aLearnCard: case aPasswd: case aKeydbClearSomeCertFlags: do_not_setup_keys = 1; set_cmd (&cmd, pargs.r_opt); break; case aEncr: recp_required = 1; set_cmd (&cmd, pargs.r_opt); break; case aSym: case aDecrypt: case aSign: case aClearsign: case aVerify: set_cmd (&cmd, pargs.r_opt); break; /* Output encoding selection. */ case oArmor: ctrl.create_pem = 1; break; case oBase64: ctrl.create_pem = 0; ctrl.create_base64 = 1; break; case oNoArmor: ctrl.create_pem = 0; ctrl.create_base64 = 0; break; case oP12Charset: opt.p12_charset = pargs.r.ret_str; break; case oPassphraseFD: pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0); break; case oPinentryMode: opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str); if (opt.pinentry_mode == -1) log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str); break; case oRequestOrigin: opt.request_origin = parse_request_origin (pargs.r.ret_str); if (opt.request_origin == -1) log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str); break; /* Input encoding selection. */ case oAssumeArmor: ctrl.autodetect_encoding = 0; ctrl.is_pem = 1; ctrl.is_base64 = 0; break; case oAssumeBase64: ctrl.autodetect_encoding = 0; ctrl.is_pem = 0; ctrl.is_base64 = 1; break; case oAssumeBinary: ctrl.autodetect_encoding = 0; ctrl.is_pem = 0; ctrl.is_base64 = 0; break; case oDisableCRLChecks: opt.no_crl_check = 1; break; case oEnableCRLChecks: opt.no_crl_check = 0; break; case oDisableTrustedCertCRLCheck: opt.no_trusted_cert_crl_check = 1; break; case oEnableTrustedCertCRLCheck: opt.no_trusted_cert_crl_check = 0; break; case oForceCRLRefresh: opt.force_crl_refresh = 1; break; case oDisableOCSP: ctrl.use_ocsp = opt.enable_ocsp = 0; break; case oEnableOCSP: ctrl.use_ocsp = opt.enable_ocsp = 1; break; case oIncludeCerts: ctrl.include_certs = default_include_certs = pargs.r.ret_int; break; case oPolicyFile: xfree (opt.policy_file); if (*pargs.r.ret_str) opt.policy_file = xstrdup (pargs.r.ret_str); else opt.policy_file = NULL; break; case oDisablePolicyChecks: opt.no_policy_check = 1; break; case oEnablePolicyChecks: opt.no_policy_check = 0; break; case oAutoIssuerKeyRetrieve: opt.auto_issuer_key_retrieve = 1; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: /* fixme:tty_no_terminal(1);*/ break; case oDryRun: opt.dry_run = 1; break; case oVerbose: opt.verbose++; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); break; case oNoVerbose: opt.verbose = 0; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); break; case oLogFile: logfile = pargs.r.ret_str; break; case oNoLogFile: logfile = NULL; break; case oAuditLog: auditlog = pargs.r.ret_str; break; case oHtmlAuditLog: htmlauditlog = pargs.r.ret_str; break; case oBatch: opt.batch = 1; greeting = 0; break; case oNoBatch: opt.batch = 0; break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; case oKeyring: append_to_strlist (&nrings, pargs.r.ret_str); break; case oDebug: if (parse_debug_flag (pargs.r.ret_str, &debug_value, debug_flags)) { pargs.r_opt = ARGPARSE_INVALID_ARG; pargs.err = ARGPARSE_PRINT_ERROR; } break; case oDebugAll: debug_value = ~0; break; case oDebugNone: debug_value = 0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugAllowCoreDump: may_coredump = enable_core_dumps (); break; case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; case oStatusFD: ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); break; case oLoggerFD: log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oWithMD5Fingerprint: opt.with_md5_fingerprint=1; /*fall through*/ case oWithFingerprint: with_fpr=1; /*fall through*/ case aFingerprint: opt.fingerprint++; break; case oWithKeygrip: opt.with_keygrip = 1; break; case oWithKeyScreening: opt.with_key_screening = 1; break; case oOptions: /* config files may not be nested (silently ignore them) */ if (!configfp) { xfree(configname); configname = xstrdup (pargs.r.ret_str); goto next_pass; } break; case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */ case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; case oDisplay: set_opt_session_env ("DISPLAY", pargs.r.ret_str); break; case oTTYname: set_opt_session_env ("GPG_TTY", pargs.r.ret_str); break; case oTTYtype: set_opt_session_env ("TERM", pargs.r.ret_str); break; case oXauthority: set_opt_session_env ("XAUTHORITY", pargs.r.ret_str); break; case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break; case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break; case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break; case oDisableDirmngr: opt.disable_dirmngr = 1; break; case oPreferSystemDirmngr: /* Obsolete */; break; case oProtectToolProgram: opt.protect_tool_program = pargs.r.ret_str; break; case oFakedSystemTime: { time_t faked_time = isotime2epoch (pargs.r.ret_str); if (faked_time == (time_t)(-1)) faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); gnupg_set_time (faked_time, 0); } break; case oNoDefKeyring: default_keyring = 0; break; case oNoGreeting: nogreeting = 1; break; case oDefaultKey: if (*pargs.r.ret_str) { xfree (opt.local_user); opt.local_user = xstrdup (pargs.r.ret_str); } break; case oDefRecipient: if (*pargs.r.ret_str) opt.def_recipient = xstrdup (pargs.r.ret_str); break; case oDefRecipientSelf: xfree (opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 1; break; case oNoDefRecipient: xfree (opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 0; break; case oWithKeyData: opt.with_key_data=1; /* fall through */ case oWithColons: ctrl.with_colons = 1; break; case oWithSecret: ctrl.with_secret = 1; break; case oWithValidation: ctrl.with_validation=1; break; case oWithEphemeralKeys: ctrl.with_ephemeral_keys=1; break; case oSkipVerify: opt.skip_verify=1; break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* Store the recipient in the second list */ sl = add_to_strlist (&remusr, pargs.r.ret_str); sl->flags = 1; break; case oRecipient: /* store the recipient */ add_to_strlist ( &remusr, pargs.r.ret_str); break; case oUser: /* Store the local users, the first one is the default */ if (!opt.local_user) opt.local_user = xstrdup (pargs.r.ret_str); add_to_strlist (&locusr, pargs.r.ret_str); break; case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break; case oCipherAlgo: opt.def_cipher_algoid = pargs.r.ret_str; break; case oDisableCipherAlgo: { int algo = gcry_cipher_map_name (pargs.r.ret_str); gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oDisablePubkeyAlgo: { int algo = gcry_pk_map_name (pargs.r.ret_str); gcry_pk_ctl (GCRYCTL_DISABLE_ALGO,&algo, sizeof algo ); } break; case oDigestAlgo: forced_digest_algo = pargs.r.ret_str; break; case oExtraDigestAlgo: extra_digest_algo = pargs.r.ret_str; break; case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; case oNoRandomSeedFile: use_random_seed = 0; break; case oNoCommonCertsImport: no_common_certs_import = 1; break; case oEnableSpecialFilenames: enable_special_filenames (); break; case oValidationModel: parse_validation_model (pargs.r.ret_str); break; case oKeyServer: { struct keyserver_spec *keyserver; keyserver = parse_keyserver_line (pargs.r.ret_str, configname, configlineno); if (! keyserver) log_error (_("could not parse keyserver\n")); else { /* FIXME: Keep last next pointer. */ struct keyserver_spec **next_p = &opt.keyserver; while (*next_p) next_p = &(*next_p)->next; *next_p = keyserver; } } break; case oIgnoreCertExtension: add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str); break; case oAuthenticode: opt.authenticode = 1; break; case oAttribute: add_to_strlist (&opt.attributes, pargs.r.ret_str); break; case oNoAutostart: opt.autostart = 0; break; case oCompliance: { struct gnupg_compliance_option compliance_options[] = { { "gnupg", CO_GNUPG }, { "de-vs", CO_DE_VS } }; int compliance = gnupg_parse_compliance_option (pargs.r.ret_str, compliance_options, DIM (compliance_options), opt.quiet); if (compliance < 0) log_inc_errorcount (); /* Force later termination. */ opt.compliance = compliance; } break; default: pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR; break; } } if (configfp) { fclose (configfp); configfp = NULL; /* Keep a copy of the config filename. */ opt.config_filename = configname; configname = NULL; goto next_pass; } xfree (configname); configname = NULL; if (!opt.config_filename) opt.config_filename = make_filename (gnupg_homedir (), GPGSM_NAME EXTSEP_S "conf", NULL); if (log_get_errorcount(0)) { gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", gpg_error (GPG_ERR_GENERAL)); gpgsm_exit(2); } if (pwfd != -1) /* Read the passphrase now. */ read_passphrase_from_fd (pwfd); /* Now that we have the options parsed we need to update the default control structure. */ gpgsm_init_default_ctrl (&ctrl); if (nogreeting) greeting = 0; if (greeting) { es_fprintf (es_stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); es_fprintf (es_stderr, "%s\n", strusage(15) ); } # ifdef IS_DEVELOPMENT_VERSION if (!opt.batch) { log_info ("NOTE: THIS IS A DEVELOPMENT VERSION!\n"); log_info ("It is only intended for test purposes and should NOT be\n"); log_info ("used in a production environment or with production keys!\n"); } # endif if (may_coredump && !opt.quiet) log_info (_("WARNING: program may create a core file!\n")); /* if (opt.qualsig_approval && !opt.quiet) */ /* log_info (_("This software has officially been approved to " */ /* "create and verify\n" */ /* "qualified signatures according to German law.\n")); */ if (logfile && cmd == aServer) { log_set_file (logfile); log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); } if (gnupg_faked_time_p ()) { gnupg_isotime_t tbuf; log_info (_("WARNING: running with faked system time: ")); gnupg_get_isotime (tbuf); dump_isotime (tbuf); log_printf ("\n"); } /* Print a warning if an argument looks like an option. */ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) { int i; for (i=0; i < argc; i++) if (argv[i][0] == '-' && argv[i][1] == '-') log_info (_("Note: '%s' is not considered an option\n"), argv[i]); } /*FIXME if (opt.batch) */ /* tty_batchmode (1); */ gcry_control (GCRYCTL_RESUME_SECMEM_WARN); set_debug (); /* Although we always use gpgsm_exit, we better install a regular exit handler so that at least the secure memory gets wiped out. */ if (atexit (emergency_cleanup)) { log_error ("atexit failed\n"); gpgsm_exit (2); } /* Must do this after dropping setuid, because the mapping functions may try to load an module and we may have disabled an algorithm. We remap the commonly used algorithms to the OIDs for convenience. We need to work with the OIDs because they are used to check whether the encryption mode is actually available. */ if (!strcmp (opt.def_cipher_algoid, "3DES") ) opt.def_cipher_algoid = "1.2.840.113549.3.7"; else if (!strcmp (opt.def_cipher_algoid, "AES") || !strcmp (opt.def_cipher_algoid, "AES128")) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.2"; else if (!strcmp (opt.def_cipher_algoid, "AES192") ) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.22"; else if (!strcmp (opt.def_cipher_algoid, "AES256") ) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.42"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT") || !strcmp (opt.def_cipher_algoid, "SERPENT128") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.2"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT192") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.22"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT256") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.42"; else if (!strcmp (opt.def_cipher_algoid, "SEED") ) opt.def_cipher_algoid = "1.2.410.200004.1.4"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA") || !strcmp (opt.def_cipher_algoid, "CAMELLIA128") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.2"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA192") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.3"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA256") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.4"; if (cmd != aGPGConfList) { if ( !gcry_cipher_map_name (opt.def_cipher_algoid) || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid)) log_error (_("selected cipher algorithm is invalid\n")); if (forced_digest_algo) { opt.forced_digest_algo = gcry_md_map_name (forced_digest_algo); if (our_md_test_algo(opt.forced_digest_algo) ) log_error (_("selected digest algorithm is invalid\n")); } if (extra_digest_algo) { opt.extra_digest_algo = gcry_md_map_name (extra_digest_algo); if (our_md_test_algo (opt.extra_digest_algo) ) log_error (_("selected digest algorithm is invalid\n")); } } /* Check our chosen algorithms against the list of allowed * algorithms in the current compliance mode, and fail hard if it is * not. This is us being nice to the user informing her early that * the chosen algorithms are not available. We also check and * enforce this right before the actual operation. */ if (! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr || cmd == aSignEncr, gcry_cipher_map_name (opt.def_cipher_algoid), GCRY_CIPHER_MODE_NONE) && ! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr || cmd == aSignEncr, gcry_cipher_mode_from_oid (opt.def_cipher_algoid), GCRY_CIPHER_MODE_NONE)) log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); if (forced_digest_algo && ! gnupg_digest_is_allowed (opt.compliance, cmd == aSign || cmd == aSignEncr || cmd == aClearsign, opt.forced_digest_algo)) log_error (_("digest algorithm '%s' may not be used in %s mode\n"), forced_digest_algo, gnupg_compliance_option_string (opt.compliance)); if (extra_digest_algo && ! gnupg_digest_is_allowed (opt.compliance, cmd == aSign || cmd == aSignEncr || cmd == aClearsign, opt.extra_digest_algo)) log_error (_("digest algorithm '%s' may not be used in %s mode\n"), extra_digest_algo, gnupg_compliance_option_string (opt.compliance)); if (log_get_errorcount(0)) { gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-postprocessing", gpg_error (GPG_ERR_GENERAL)); gpgsm_exit (2); } /* Set the random seed file. */ if (use_random_seed) { char *p = make_filename (gnupg_homedir (), "random_seed", NULL); gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); xfree(p); } if (!cmd && opt.fingerprint && !with_fpr) set_cmd (&cmd, aListKeys); /* Add default keybox. */ if (!nrings && default_keyring) { int created; keydb_add_resource (&ctrl, "pubring.kbx", 0, &created); if (created && !no_common_certs_import) { /* Import the standard certificates for a new default keybox. */ char *filelist[2]; filelist[0] = make_filename (gnupg_datadir (),"com-certs.pem", NULL); filelist[1] = NULL; if (!access (filelist[0], F_OK)) { log_info (_("importing common certificates '%s'\n"), filelist[0]); gpgsm_import_files (&ctrl, 1, filelist, open_read); } xfree (filelist[0]); } } for (sl = nrings; sl; sl = sl->next) keydb_add_resource (&ctrl, sl->d, 0, NULL); FREE_STRLIST(nrings); /* Prepare the audit log feature for certain commands. */ if (auditlog || htmlauditlog) { switch (cmd) { case aEncr: case aSign: case aDecrypt: case aVerify: audit_release (ctrl.audit); ctrl.audit = audit_new (); if (auditlog) auditfp = open_es_fwrite (auditlog); if (htmlauditlog) htmlauditfp = open_es_fwrite (htmlauditlog); break; default: break; } } if (!do_not_setup_keys) { int errcount = log_get_errorcount (0); for (sl = locusr; sl ; sl = sl->next) { int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist, 0); if (rc) { log_error (_("can't sign using '%s': %s\n"), sl->d, gpg_strerror (rc)); gpgsm_status2 (&ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (rc), sl->d, NULL); gpgsm_status2 (&ctrl, STATUS_INV_RECP, get_inv_recpsgnr_code (rc), sl->d, NULL); } } /* Build the recipient list. We first add the regular ones and then the encrypt-to ones because the underlying function will silently ignore duplicates and we can't allow keeping a duplicate which is flagged as encrypt-to as the actually encrypt function would then complain about no (regular) recipients. */ for (sl = remusr; sl; sl = sl->next) if (!(sl->flags & 1)) do_add_recipient (&ctrl, sl->d, &recplist, 0, recp_required); if (!opt.no_encrypt_to) { for (sl = remusr; sl; sl = sl->next) if ((sl->flags & 1)) do_add_recipient (&ctrl, sl->d, &recplist, 1, recp_required); } /* We do not require a recipient for decryption but because * recipients and signers are always checked and log_error is * sometimes used (for failed signing keys or due to a failed * CRL checking) that would have bumbed up the error counter. * We clear the counter in the decryption case because there is * no reason to force decryption to fail. */ if (cmd == aDecrypt && !errcount) log_get_errorcount (1); /* clear counter */ } if (log_get_errorcount(0)) gpgsm_exit(1); /* Must stop for invalid recipients. */ /* Dispatch command. */ switch (cmd) { case aGPGConfList: { /* List options and default values in the GPG Conf format. */ char *config_filename_esc = percent_escape (opt.config_filename, NULL); es_printf ("%s-%s.conf:%lu:\"%s\n", GPGCONF_NAME, GPGSM_NAME, GC_OPT_FLAG_DEFAULT, config_filename_esc); xfree (config_filename_esc); es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("enable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_INCLUDE_CERTS); es_printf ("disable-policy-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("auto-issuer-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("cipher-algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_CIPHER_ALGO); es_printf ("p12-charset:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("default-key:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); /* The next one is an info only item and should match what proc_parameters actually implements. */ es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "RSA-3072"); } break; case aGPGConfTest: /* This is merely a dummy command to test whether the configuration file is valid. */ break; case aServer: if (debug_wait) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); gnupg_sleep (debug_wait); log_debug ("... okay\n"); } gpgsm_server (recplist); break; case aCallDirmngr: if (!argc) wrong_args ("--call-dirmngr <command> {args}"); else if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1)) gpgsm_exit (1); break; case aCallProtectTool: run_protect_tool (argc, argv); break; case aEncr: /* Encrypt the given file. */ { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); set_binary (stdin); if (!argc) /* Source is stdin. */ gpgsm_encrypt (&ctrl, recplist, 0, fp); else if (argc == 1) /* Source is the given file. */ gpgsm_encrypt (&ctrl, recplist, open_read (*argv), fp); else wrong_args ("--encrypt [datafile]"); es_fclose (fp); } break; case aSign: /* Sign the given file. */ { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); /* Fixme: We should also allow concatenation of multiple files for signing because that is what gpg does.*/ set_binary (stdin); if (!argc) /* Create from stdin. */ gpgsm_sign (&ctrl, signerlist, 0, detached_sig, fp); else if (argc == 1) /* From file. */ gpgsm_sign (&ctrl, signerlist, open_read (*argv), detached_sig, fp); else wrong_args ("--sign [datafile]"); es_fclose (fp); } break; case aSignEncr: /* sign and encrypt the given file */ log_error ("this command has not yet been implemented\n"); break; case aClearsign: /* make a clearsig */ log_error ("this command has not yet been implemented\n"); break; case aVerify: { estream_t fp = NULL; set_binary (stdin); if (argc == 2 && opt.outfile) log_info ("option --output ignored for a detached signature\n"); else if (opt.outfile) fp = open_es_fwrite (opt.outfile); if (!argc) gpgsm_verify (&ctrl, 0, -1, fp); /* normal signature from stdin */ else if (argc == 1) gpgsm_verify (&ctrl, open_read (*argv), -1, fp); /* std signature */ else if (argc == 2) /* detached signature (sig, detached) */ gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL); else wrong_args ("--verify [signature [detached_data]]"); es_fclose (fp); } break; case aDecrypt: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); set_binary (stdin); if (!argc) gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ else if (argc == 1) gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ else wrong_args ("--decrypt [filename]"); es_fclose (fp); } break; case aDeleteKey: for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_delete (&ctrl, sl); free_strlist(sl); break; case aListChain: case aDumpChain: ctrl.with_chain = 1; /* fall through */ case aListKeys: case aDumpKeys: case aListExternalKeys: case aDumpExternalKeys: case aListSecretKeys: case aDumpSecretKeys: { unsigned int mode; estream_t fp; switch (cmd) { case aListChain: case aListKeys: mode = (0 | 0 | (1<<6)); break; case aDumpChain: case aDumpKeys: mode = (256 | 0 | (1<<6)); break; case aListExternalKeys: mode = (0 | 0 | (1<<7)); break; case aDumpExternalKeys: mode = (256 | 0 | (1<<7)); break; case aListSecretKeys: mode = (0 | 2 | (1<<6)); break; case aDumpSecretKeys: mode = (256 | 2 | (1<<6)); break; default: BUG(); } fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_list_keys (&ctrl, sl, fp, mode); free_strlist(sl); es_fclose (fp); } break; case aKeygen: /* Generate a key; well kind of. */ { estream_t fpin = NULL; estream_t fpout; if (opt.batch) { if (!argc) /* Create from stdin. */ fpin = open_es_fread ("-", "r"); else if (argc == 1) /* From file. */ fpin = open_es_fread (*argv, "r"); else wrong_args ("--generate-key --batch [parmfile]"); } fpout = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (fpin) gpgsm_genkey (&ctrl, fpin, fpout); else gpgsm_gencertreq_tty (&ctrl, fpout); es_fclose (fpout); } break; case aImport: gpgsm_import_files (&ctrl, argc, argv, open_read); break; case aExport: { estream_t fp; fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_export (&ctrl, sl, fp); free_strlist(sl); es_fclose (fp); } break; case aExportSecretKeyP12: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 0); else wrong_args ("--export-secret-key-p12 KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aExportSecretKeyP8: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 1); else wrong_args ("--export-secret-key-p8 KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aExportSecretKeyRaw: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 2); else wrong_args ("--export-secret-key-raw KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aSendKeys: case aRecvKeys: log_error ("this command has not yet been implemented\n"); break; case aLearnCard: if (argc) wrong_args ("--learn-card"); else { int rc = gpgsm_agent_learn (&ctrl); if (rc) log_error ("error learning card: %s\n", gpg_strerror (rc)); } break; case aPasswd: if (argc != 1) wrong_args ("--change-passphrase <key-Id>"); else { int rc; ksba_cert_t cert = NULL; char *grip = NULL; rc = gpgsm_find_cert (&ctrl, *argv, NULL, &cert, 0); if (rc) ; else if (!(grip = gpgsm_get_keygrip_hexstring (cert))) rc = gpg_error (GPG_ERR_BUG); else { char *desc = gpgsm_format_keydesc (cert); rc = gpgsm_agent_passwd (&ctrl, grip, desc); xfree (desc); } if (rc) log_error ("error changing passphrase: %s\n", gpg_strerror (rc)); xfree (grip); ksba_cert_release (cert); } break; case aKeydbClearSomeCertFlags: for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); keydb_clear_some_cert_flags (&ctrl, sl); free_strlist(sl); break; default: log_error (_("invalid command (there is no implicit command)\n")); break; } /* Print the audit result if needed. */ if ((auditlog && auditfp) || (htmlauditlog && htmlauditfp)) { if (auditlog && auditfp) audit_print_result (ctrl.audit, auditfp, 0); if (htmlauditlog && htmlauditfp) audit_print_result (ctrl.audit, htmlauditfp, 1); audit_release (ctrl.audit); ctrl.audit = NULL; es_fclose (auditfp); es_fclose (htmlauditfp); } /* cleanup */ keyserver_list_free (opt.keyserver); opt.keyserver = NULL; gpgsm_release_certlist (recplist); gpgsm_release_certlist (signerlist); FREE_STRLIST (remusr); FREE_STRLIST (locusr); gpgsm_exit(0); return 8; /*NOTREACHED*/ } /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) { gcry_control (GCRYCTL_TERM_SECMEM ); } void gpgsm_exit (int rc) { gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); if (opt.debug & DBG_MEMSTAT_VALUE) { gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); } if (opt.debug) gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0; exit (rc); } void gpgsm_init_default_ctrl (struct server_control_s *ctrl) { ctrl->include_certs = default_include_certs; ctrl->use_ocsp = opt.enable_ocsp; ctrl->validation_model = default_validation_model; ctrl->offline = opt.disable_dirmngr; } int gpgsm_parse_validation_model (const char *model) { if (!ascii_strcasecmp (model, "shell") ) return 0; else if ( !ascii_strcasecmp (model, "chain") ) return 1; else if ( !ascii_strcasecmp (model, "steed") ) return 2; else return -1; } /* Open the FILENAME for read and return the file descriptor. Stop with an error message in case of problems. "-" denotes stdin and if special filenames are allowed the given fd is opened instead. */ static int open_read (const char *filename) { int fd; if (filename[0] == '-' && !filename[1]) { set_binary (stdin); return 0; /* stdin */ } fd = check_special_filename (filename, 0, 0); if (fd != -1) return fd; fd = open (filename, O_RDONLY | O_BINARY); if (fd == -1) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fd; } /* Same as open_read but return an estream_t. */ static estream_t open_es_fread (const char *filename, const char *mode) { int fd; estream_t fp; if (filename[0] == '-' && !filename[1]) fd = fileno (stdin); else fd = check_special_filename (filename, 0, 0); if (fd != -1) { fp = es_fdopen_nc (fd, mode); if (!fp) { log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); gpgsm_exit (2); } return fp; } fp = es_fopen (filename, mode); if (!fp) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fp; } /* Open FILENAME for fwrite and return an extended stream. Stop with an error message in case of problems. "-" denotes stdout and if special filenames are allowed the given fd is opened instead. Caller must close the returned stream. */ static estream_t open_es_fwrite (const char *filename) { int fd; estream_t fp; if (filename[0] == '-' && !filename[1]) { fflush (stdout); fp = es_fdopen_nc (fileno(stdout), "wb"); return fp; } fd = check_special_filename (filename, 1, 0); if (fd != -1) { fp = es_fdopen_nc (fd, "wb"); if (!fp) { log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); gpgsm_exit (2); } return fp; } fp = es_fopen (filename, "wb"); if (!fp) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fp; } static void run_protect_tool (int argc, char **argv) { #ifdef HAVE_W32_SYSTEM (void)argc; (void)argv; #else const char *pgm; char **av; int i; if (!opt.protect_tool_program || !*opt.protect_tool_program) pgm = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL); else pgm = opt.protect_tool_program; av = xcalloc (argc+2, sizeof *av); av[0] = strrchr (pgm, '/'); if (!av[0]) av[0] = xstrdup (pgm); for (i=1; argc; i++, argc--, argv++) av[i] = *argv; av[i] = NULL; execv (pgm, av); log_error ("error executing '%s': %s\n", pgm, strerror (errno)); #endif /*!HAVE_W32_SYSTEM*/ gpgsm_exit (2); } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 65fff853a..43793dcdf 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -1,459 +1,460 @@ /* gpgsm.h - Global definitions for GpgSM * Copyright (C) 2001, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * 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/>. */ #ifndef GPGSM_H #define GPGSM_H #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM #include <gpg-error.h> #include <ksba.h> #include "../common/util.h" #include "../common/status.h" #include "../common/audit.h" #include "../common/session-env.h" #include "../common/ksba-io-support.h" #include "../common/compliance.h" #define MAX_DIGEST_LEN 64 struct keyserver_spec { struct keyserver_spec *next; char *host; int port; char *user; char *pass; char *base; + unsigned int use_ldaps:1; }; /* A large struct named "opt" to keep global flags. */ struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int batch; /* run in batch mode, i.e w/o any user interaction */ int answer_yes; /* assume yes on most questions */ int answer_no; /* assume no on most questions */ int dry_run; /* don't change any persistent data */ int no_homedir_creation; const char *config_filename; /* Name of the used config file. */ const char *agent_program; session_env_t session_env; char *lc_ctype; char *lc_messages; int autostart; const char *dirmngr_program; int disable_dirmngr; /* Do not do any dirmngr calls. */ const char *protect_tool_program; char *outfile; /* name of output file */ int with_key_data;/* include raw key in the column delimited output */ int fingerprint; /* list fingerprints in all key listings */ int with_md5_fingerprint; /* Also print an MD5 fingerprint for standard key listings. */ int with_keygrip; /* Option --with-keygrip active. */ int with_key_screening; /* Option --with-key-screening active. */ int pinentry_mode; int request_origin; int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ const char *p12_charset; /* Use this charset for encoding the pkcs#12 passphrase. */ const char *def_cipher_algoid; /* cipher algorithm to use if nothing else is specified */ int def_compress_algo; /* Ditto for compress algorithm */ int forced_digest_algo; /* User forced hash algorithm. */ char *def_recipient; /* userID of the default recipient */ int def_recipient_self; /* The default recipient is the default key */ int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */ char *local_user; /* NULL or argument to -u */ int extra_digest_algo; /* A digest algorithm also used for verification of signatures. */ int always_trust; /* Trust the given keys even if there is no valid certification chain */ int skip_verify; /* do not check signatures on data */ int lock_once; /* Keep lock once they are set */ int ignore_time_conflict; /* Ignore certain time conflicts */ int no_crl_check; /* Don't do a CRL check */ int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */ int force_crl_refresh; /* Force refreshing the CRL. */ int enable_ocsp; /* Default to use OCSP checks. */ char *policy_file; /* full pathname of policy file */ int no_policy_check; /* ignore certificate policies */ int no_chain_validation; /* Bypass all cert chain validity tests */ int ignore_expiration; /* Ignore the notAfter validity checks. */ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ int qualsig_approval; /* Set to true if this software has officially been approved to create an verify qualified signatures. This is a runtime option in case we want to check the integrity of the software at runtime. */ struct keyserver_spec *keyserver; /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One OID per string. */ strlist_t ignored_cert_extensions; enum gnupg_compliance_mode compliance; /* Enable creation of authenticode signatures. */ int authenticode; /* A list of extra attributes put into a signed data object. For a * signed each attribute each string has the format: * <oid>:s:<hex_or_filename> * and for an unsigned attribute * <oid>:u:<hex_or_filename> * The OID is in the usual dotted decimal for. The HEX_OR_FILENAME * is either a list of hex digits or a filename with the DER encoded * value. A filename is detected by the presence of a slash in the * HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF * attribute values. */ strlist_t attributes; } opt; /* Debug values and macros. */ #define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) /* Forward declaration for an object defined in server.c */ struct server_local_s; /* Session control object. This object is passed down to most functions. Note that the default values for it are set by gpgsm_init_default_ctrl(). */ struct server_control_s { int no_server; /* We are not running under server control */ int status_fd; /* Only for non-server mode */ struct server_local_s *server_local; audit_ctx_t audit; /* NULL or a context for the audit subsystem. */ int agent_seen; /* Flag indicating that the gpg-agent has been accessed. */ int with_colons; /* Use column delimited output format */ int with_secret; /* Mark secret keys in a public key listing. */ int with_chain; /* Include the certifying certs in a listing */ int with_validation;/* Validate each key while listing. */ int with_ephemeral_keys; /* Include ephemeral flagged keys in the keylisting. */ int autodetect_encoding; /* Try to detect the input encoding */ int is_pem; /* Is in PEM format */ int is_base64; /* is in plain base-64 format */ int create_base64; /* Create base64 encoded output */ int create_pem; /* create PEM output */ const char *pem_name; /* PEM name to use */ int include_certs; /* -1 to send all certificates in the chain along with a signature or the number of certificates up the chain (0 = none, 1 = only signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ int validation_model; /* 0 := standard model (shell), 1 := chain model, 2 := STEED model. */ int offline; /* If true gpgsm won't do any network access. */ }; /* An object to keep a list of certificates. */ struct certlist_s { struct certlist_s *next; ksba_cert_t cert; int is_encrypt_to; /* True if the certificate has been set through the --encrypto-to option. */ int hash_algo; /* Used to track the hash algorithm to use. */ const char *hash_algo_oid; /* And the corresponding OID. */ }; typedef struct certlist_s *certlist_t; /* A structure carrying information about trusted root certificates. */ struct rootca_flags_s { unsigned int valid:1; /* The rest of the structure has valid information. */ unsigned int relax:1; /* Relax checking of root certificates. */ unsigned int chain_model:1; /* Root requires the use of the chain model. */ }; /*-- gpgsm.c --*/ void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); int gpgsm_parse_validation_model (const char *model); /*-- server.c --*/ void gpgsm_server (certlist_t default_recplist); gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text); gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0); gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); /*-- fingerprint --*/ unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo, unsigned char *array, int *r_len); char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo); char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo); unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high); unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert); char *gpgsm_get_certid (ksba_cert_t cert); /*-- certdump.c --*/ void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p); void gpgsm_print_time (estream_t fp, ksba_isotime_t t); void gpgsm_print_name2 (FILE *fp, const char *string, int translate); void gpgsm_print_name (FILE *fp, const char *string); void gpgsm_es_print_name (estream_t fp, const char *string); void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate); void gpgsm_cert_log_name (const char *text, ksba_cert_t cert); void gpgsm_dump_cert (const char *text, ksba_cert_t cert); void gpgsm_dump_serial (ksba_const_sexp_t p); void gpgsm_dump_time (ksba_isotime_t t); void gpgsm_dump_string (const char *string); char *gpgsm_format_serial (ksba_const_sexp_t p); char *gpgsm_format_name2 (const char *name, int translate); char *gpgsm_format_name (const char *name); char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer); char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert); char *gpgsm_format_keydesc (ksba_cert_t cert); /*-- certcheck.c --*/ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, gcry_md_hd_t md, int hash_algo, int *r_pkalgo); /* fixme: move create functions to another file */ int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, unsigned char **r_sigval); /*-- certchain.c --*/ /* Flags used with gpgsm_validate_chain. */ #define VALIDATE_FLAG_NO_DIRMNGR 1 #define VALIDATE_FLAG_CHAIN_MODEL 2 #define VALIDATE_FLAG_STEED 4 int gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next); int gpgsm_is_root_cert (ksba_cert_t cert); int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, ksba_isotime_t r_exptime, int listmode, estream_t listfp, unsigned int flags, unsigned int *retflags); int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert); /*-- certlist.c --*/ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent); int gpgsm_cert_use_encrypt_p (ksba_cert_t cert); int gpgsm_cert_use_verify_p (ksba_cert_t cert); int gpgsm_cert_use_decrypt_p (ksba_cert_t cert); int gpgsm_cert_use_cert_p (ksba_cert_t cert); int gpgsm_cert_use_ocsp_p (ksba_cert_t cert); int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert); int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b); int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to); int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode); /*-- import.c --*/ int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode); int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)); /*-- export.c --*/ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode); /*-- delete.c --*/ int gpgsm_delete (ctrl_t ctrl, strlist_t names); /*-- verify.c --*/ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp); /*-- sign.c --*/ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert); int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp); /*-- encrypt.c --*/ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream); /*-- certreqgen-ui.c --*/ void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream); /*-- qualified.c --*/ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country); gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert); gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert); /*-- call-agent.c --*/ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey); int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey); int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno); int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list); int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags); int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip); int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert); int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno); gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase); gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen); gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen); gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp); int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value); int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv); /*-- misc.c --*/ void setup_pinentry_env (void); gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen); #endif /*GPGSM_H*/