diff --git a/NEWS b/NEWS index 48f4fdb3b..232d8beef 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,36 @@ Noteworthy changes in version 2.3.0 (unreleased) ------------------------------------------------ + Changes also found in 2.2.8: + + * gpg: Decryption of messages not using the MDC mode will now lead + to a hard failure even if a legacy cipher algorithm was used. The + option --ignore-mdc-error can be used to turn this failure into a + warning. Take care: Never use that option unconditionally or + without a prior warning. + + * gpg: The MDC encryption mode is now always used regardless of the + cipher algorithm or any preferences. For testing --rfc2440 can be + used to create a message without an MDC. + + * gpg: Sanitize the diagnostic output of the original file name in + verbose mode. [#4012,CVE-2018-12020] + + * gpg: Detect suspicious multiple plaintext packets in a more + reliable way. [#4000] + + * gpg: Fix the duplicate key signature detection code. [#3994] + + * gpg: The options --no-mdc-warn, --force-mdc, --no-force-mdc, + --disable-mdc and --no-disable-mdc have no more effect. + + * gpg: New command --show-keys. + + * agent: Add DBUS_SESSION_BUS_ADDRESS and a few other envvars to the + list of startup environment variables. [#3947] + + See-also: gnupg-announce/2018q2/000425.html + Changes also found in 2.2.7: * gpg: New option --no-symkey-cache to disable the passphrase cache @@ -36,6 +66,8 @@ Noteworthy changes in version 2.3.0 (unreleased) * agent,dirmngr: New sub-command "getenv" for "getinfo" to ease debugging. + See-also: gnupg-announce/2018q2/000424.html + Changes also found in 2.2.6: * gpg,gpgsm: New option --request-origin to pretend requests coming @@ -81,6 +113,8 @@ Noteworthy changes in version 2.3.0 (unreleased) * Allow the use of UNC directory names as homedir. [#3818] + See-also: gnupg-announce/2018q2/000421.html + Changes also found in 2.2.5: * gpg: Allow the use of the "cv25519" and "ed25519" short names in @@ -124,6 +158,8 @@ Noteworthy changes in version 2.3.0 (unreleased) with statically linked versions of the core GnuPG libraries. Also use --enable-wks-tools by default by Speedo builds for Unix. + See-also: gnupg-announce/2018q1/000420.html + Changes also found in 2.2.4: * gpg: Change default preferences to prefer SHA512. @@ -153,6 +189,8 @@ Noteworthy changes in version 2.3.0 (unreleased) * New configure option --enable-run-gnupg-user-socket to first try a socket directory which is not removed by systemd at session end. + See-also: gnupg-announce/2017q4/000419.html + Changes also found in 2.2.3: * gpgsm: Fix initial keybox creation on Windows. [#3507] @@ -172,7 +210,6 @@ Noteworthy changes in version 2.3.0 (unreleased) See-also: gnupg-announce/2017q4/000417.html - Changes also found in 2.2.2: * gpg: Avoid duplicate key imports by concurrently running gpg @@ -236,6 +273,8 @@ Noteworthy changes in version 2.3.0 (unreleased) certificates are configured. If build with GNUTLS, this was already the case. + See-also: gnupg-announce/2017q3/000415.html + Release dates of 2.2.x versions: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Version 2.2.1 (2017-09-19) @@ -245,6 +284,7 @@ Noteworthy changes in version 2.3.0 (unreleased) Version 2.2.5 (2018-02-22) Version 2.2.6 (2018-04-09) Version 2.2.7 (2018-05-02) + Version 2.2.8 (2018-06-08) Noteworthy changes in version 2.2.0 (2017-08-28) diff --git a/agent/agent.h b/agent/agent.h index 9fdbc76d3..9baf59601 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -304,11 +304,12 @@ enum typedef enum { CACHE_MODE_IGNORE = 0, /* Special mode to bypass the cache. */ - CACHE_MODE_ANY, /* Any mode except ignore matches. */ + CACHE_MODE_ANY, /* Any mode except ignore and data matches. */ CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */ CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */ CACHE_MODE_SSH, /* SSH related cache. */ - CACHE_MODE_NONCE /* This is a non-predictable nonce. */ + CACHE_MODE_NONCE, /* This is a non-predictable nonce. */ + CACHE_MODE_DATA /* Arbitrary data. */ } cache_mode_t; diff --git a/agent/cache.c b/agent/cache.c index 238b6e214..799d595ab 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -28,6 +28,10 @@ #include "agent.h" +/* The default TTL for DATA items. This has no configure + * option because it is expected that clients provide a TTL. */ +#define DEF_CACHE_TTL_DATA (10 * 60) /* 10 minutes. */ + /* The size of the encryption key in bytes. */ #define ENCRYPTION_KEYSIZE (128/8) @@ -50,11 +54,12 @@ struct secret_data_s { char data[1]; /* A string. */ }; +/* The cache object. */ typedef struct cache_item_s *ITEM; struct cache_item_s { ITEM next; time_t created; - time_t accessed; + time_t accessed; /* Not updated for CACHE_MODE_DATA */ int ttl; /* max. lifetime given in seconds, -1 one means infinite */ struct secret_data_s *pw; cache_mode_t cache_mode; @@ -211,14 +216,18 @@ housekeeping (void) } } - /* Second, make sure that we also remove them based on the created stamp so - that the user has to enter it from time to time. */ + /* Second, make sure that we also remove them based on the created + * stamp so that the user has to enter it from time to time. We + * don't do this for data items which are used to storage secrets in + * meory and are not user entered passphrases etc. */ for (r=thecache; r; r = r->next) { unsigned long maxttl; switch (r->cache_mode) { + case CACHE_MODE_DATA: + continue; /* No MAX TTL here. */ case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break; default: maxttl = opt.max_cache_ttl; break; } @@ -315,8 +324,11 @@ static int cache_mode_equal (cache_mode_t a, cache_mode_t b) { /* CACHE_MODE_ANY matches any mode other than CACHE_MODE_IGNORE. */ - return ((a == CACHE_MODE_ANY && b != CACHE_MODE_IGNORE) - || (b == CACHE_MODE_ANY && a != CACHE_MODE_IGNORE) || a == b); + return ((a == CACHE_MODE_ANY + && !(b == CACHE_MODE_IGNORE || b == CACHE_MODE_DATA)) + || (b == CACHE_MODE_ANY + && !(a == CACHE_MODE_IGNORE || a == CACHE_MODE_DATA)) + || a == b); } @@ -349,6 +361,7 @@ agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, switch(cache_mode) { case CACHE_MODE_SSH: ttl = opt.def_cache_ttl_ssh; break; + case CACHE_MODE_DATA: ttl = DEF_CACHE_TTL_DATA; break; default: ttl = opt.def_cache_ttl; break; } } @@ -415,9 +428,7 @@ agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, } -/* Try to find an item in the cache. Note that we currently don't - make use of CACHE_MODE except for CACHE_MODE_NONCE and - CACHE_MODE_USER. */ +/* Try to find an item in the cache. */ char * agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode) { @@ -458,8 +469,11 @@ agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode) && r->restricted == restricted && !strcmp (r->key, key)) { - /* Note: To avoid races KEY may not be accessed anymore below. */ - r->accessed = gnupg_get_time (); + /* Note: To avoid races KEY may not be accessed anymore + * below. Note also that we don't update the accessed time + * for data items. */ + if (r->cache_mode != CACHE_MODE_DATA) + r->accessed = gnupg_get_time (); if (DBG_CACHE) log_debug ("... hit\n"); if (r->pw->totallen < 32) diff --git a/agent/command.c b/agent/command.c index 1a08cfcc0..925d1f780 100644 --- a/agent/command.c +++ b/agent/command.c @@ -50,6 +50,8 @@ #define MAXLEN_KEYPARAM 1024 /* Maximum allowed size of key data as used in inquiries (bytes). */ #define MAXLEN_KEYDATA 8192 +/* Maximum length of a secret to store under one key. */ +#define MAXLEN_PUT_SECRET 4096 /* The size of the import/export KEK key (in bytes). */ #define KEYWRAP_KEYSIZE (128/8) @@ -292,6 +294,31 @@ parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf) } +/* Parse the TTL from STRING. Leading and trailing spaces are + * skipped. The value is constrained to -1 .. MAXINT. On error 0 is + * returned, else the number of bytes scanned. */ +static size_t +parse_ttl (const char *string, int *r_ttl) +{ + const char *string_orig = string; + long ttl; + char *pend; + + ttl = strtol (string, &pend, 10); + string = pend; + if (string == string_orig || !(spacep (string) || !*string) + || ttl < -1L || (int)ttl != (long)ttl) + { + *r_ttl = 0; + return 0; + } + while (spacep (string) || *string== '\n') + string++; + *r_ttl = (int)ttl; + return string - string_orig; +} + + /* Write an Assuan status line. KEYWORD is the first item on the * status line. The following arguments are all separated by a space * in the output. The last argument must be a NULL. Linefeeds and @@ -2567,6 +2594,187 @@ cmd_keytocard (assuan_context_t ctx, char *line) } + +static const char hlp_get_secret[] = + "GET_SECRET \n" + "\n" + "Return the secret value stored under KEY\n"; +static gpg_error_t +cmd_get_secret (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + char *p, *key; + char *value = NULL; + size_t valuelen; + + /* For now we allow this only for local connections. */ + if (ctrl->restricted) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + + line = skip_options (line); + + for (p=line; *p == ' '; p++) + ; + key = p; + p = strchr (key, ' '); + if (p) + { + *p++ = 0; + for (; *p == ' '; p++) + ; + if (*p) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "too many arguments"); + goto leave; + } + } + if (!*key) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "no key given"); + goto leave; + } + + + value = agent_get_cache (ctrl, key, CACHE_MODE_DATA); + if (!value) + { + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + + valuelen = percent_unescape_inplace (value, 0); + err = assuan_send_data (ctx, value, valuelen); + wipememory (value, valuelen); + + leave: + xfree (value); + return leave_cmd (ctx, err); +} + + +static const char hlp_put_secret[] = + "PUT_SECRET [--clear] []\n" + "\n" + "This commands stores a secret under KEY in gpg-agent's in-memory\n" + "cache. The TTL must be explicitly given by TTL and the options\n" + "from the configuration file are not used. The value is either given\n" + "percent-escaped as 3rd argument or if not given inquired by gpg-agent\n" + "using the keyword \"SECRET\".\n" + "The option --clear removes the secret from the cache." + ""; +static gpg_error_t +cmd_put_secret (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err = 0; + int opt_clear; + unsigned char *value = NULL; + size_t valuelen = 0; + size_t n; + char *p, *key, *ttlstr; + unsigned char *valstr; + int ttl; + char *string = NULL; + + /* For now we allow this only for local connections. */ + if (ctrl->restricted) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + + opt_clear = has_option (line, "--clear"); + line = skip_options (line); + + for (p=line; *p == ' '; p++) + ; + key = p; + ttlstr = NULL; + valstr = NULL; + p = strchr (key, ' '); + if (p) + { + *p++ = 0; + for (; *p == ' '; p++) + ; + if (*p) + { + ttlstr = p; + p = strchr (ttlstr, ' '); + if (p) + { + *p++ = 0; + for (; *p == ' '; p++) + ; + if (*p) + valstr = p; + } + } + } + if (!*key) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "no key given"); + goto leave; + } + if (!ttlstr || !*ttlstr || !(n = parse_ttl (ttlstr, &ttl))) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "no or invalid TTL given"); + goto leave; + } + if (valstr && opt_clear) + { + err = set_error (GPG_ERR_ASS_PARAMETER, + "value not expected with --clear"); + goto leave; + } + + if (valstr) + { + valuelen = percent_unescape_inplace (valstr, 0); + value = NULL; + } + else /* Inquire the value to store */ + { + err = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u",MAXLEN_PUT_SECRET); + if (!err) + err = assuan_inquire (ctx, "SECRET", + &value, &valuelen, MAXLEN_PUT_SECRET); + if (err) + goto leave; + } + + /* Our cache expects strings and thus we need to turn the buffer + * into a string. Instead of resorting to base64 encoding we use a + * special percent escaping which only quoted the Nul and the + * percent character. */ + string = percent_data_escape (value? value : valstr, valuelen); + if (!string) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = agent_put_cache (ctrl, key, CACHE_MODE_DATA, string, ttl); + + + leave: + if (string) + { + wipememory (string, strlen (string)); + xfree (string); + } + if (value) + { + wipememory (value, valuelen); + xfree (value); + } + return leave_cmd (ctx, err); +} + + static const char hlp_getval[] = "GETVAL \n" @@ -3259,6 +3467,8 @@ register_commands (assuan_context_t ctx) { "IMPORT_KEY", cmd_import_key, hlp_import_key }, { "EXPORT_KEY", cmd_export_key, hlp_export_key }, { "DELETE_KEY", cmd_delete_key, hlp_delete_key }, + { "GET_SECRET", cmd_get_secret, hlp_get_secret }, + { "PUT_SECRET", cmd_put_secret, hlp_put_secret }, { "GETVAL", cmd_getval, hlp_getval }, { "PUTVAL", cmd_putval, hlp_putval }, { "UPDATESTARTUPTTY", cmd_updatestartuptty, hlp_updatestartuptty }, @@ -3351,7 +3561,8 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd) for (;;) { - assuan_peercred_t client_creds; + assuan_peercred_t client_creds; /* Note: Points into CTX. */ + pid_t pid; rc = assuan_accept (ctx); if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1) @@ -3367,17 +3578,21 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd) rc = assuan_get_peercred (ctx, &client_creds); if (rc) { - log_info ("Assuan get_peercred failed: %s\n", gpg_strerror (rc)); - client_creds->pid = assuan_get_pid (ctx); + + if (listen_fd == GNUPG_INVALID_FD && fd == GNUPG_INVALID_FD) + ; + else + log_info ("Assuan get_peercred failed: %s\n", gpg_strerror (rc)); + pid = assuan_get_pid (ctx); ctrl->client_uid = -1; } - ctrl->server_local->connect_from_self = - (client_creds->pid == getpid ()); - if (client_creds->pid != ASSUAN_INVALID_PID) - ctrl->client_pid = (unsigned long)client_creds->pid; else - ctrl->client_pid = 0; - ctrl->client_uid = client_creds->uid; + { + pid = client_creds->pid; + ctrl->client_uid = client_creds->uid; + } + ctrl->client_pid = (pid == ASSUAN_INVALID_PID)? 0 : (unsigned long)pid; + ctrl->server_local->connect_from_self = (pid == getpid ()); rc = assuan_process (ctx); if (rc) diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index bd9a471e8..1fdc94d0f 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1979,15 +1979,15 @@ agent_deinit_default_ctrl (ctrl_t ctrl) gpg_error_t agent_copy_startup_env (ctrl_t ctrl) { - static const char *names[] = - {"GPG_TTY", "DISPLAY", "TERM", "XAUTHORITY", "PINENTRY_USER_DATA", NULL}; gpg_error_t err = 0; - int idx; - const char *value; + int iterator = 0; + const char *name, *value; - for (idx=0; !err && names[idx]; idx++) - if ((value = session_env_getenv (opt.startup_env, names[idx]))) - err = session_env_setenv (ctrl->session_env, names[idx], value); + while (!err && (name = session_env_list_stdenvnames (&iterator, NULL))) + { + if ((value = session_env_getenv (opt.startup_env, name))) + err = session_env_setenv (ctrl->session_env, name, value); + } if (!err && !ctrl->lc_ctype && opt.startup_lc_ctype) if (!(ctrl->lc_ctype = xtrystrdup (opt.startup_lc_ctype))) diff --git a/common/Makefile.am b/common/Makefile.am index 94318dae4..d288fa36b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -49,7 +49,7 @@ common_sources = \ strlist.c strlist.h \ utf8conv.c utf8conv.h \ argparse.c argparse.h \ - logging.c logging.h \ + logging.h \ dotlock.c dotlock.h \ mischelp.c mischelp.h \ status.c status.h\ diff --git a/common/init.c b/common/init.c index 86b71e5ee..f62c5cd58 100644 --- a/common/init.c +++ b/common/init.c @@ -210,7 +210,11 @@ _init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp) } /* --version et al shall use estream as well. */ - argparse_register_outfnc (writestring_via_estream); + argparse_register_outfnc (writestring_via_estream); /* legacy. */ + gpgrt_set_usage_outfnc (writestring_via_estream); + + /* Register our string mapper with gpgrt. */ + gpgrt_set_fixed_string_mapper (map_static_macro_string); /* Logging shall use the standard socket directory as fallback. */ log_set_socket_dir_cb (gnupg_socketdir); diff --git a/common/logging.c b/common/logging.c deleted file mode 100644 index 88860e715..000000000 --- a/common/logging.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* logging.c - Useful logging functions - * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, - * 2009, 2010 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute and/or modify this - * part of GnuPG under the terms of either - * - * - the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 3 of the License, or (at - * your option) any later version. - * - * or - * - * - 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. - * - * or both in parallel, as here. - * - * 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 copies of the GNU General Public License - * and the GNU Lesser General Public License along with this program; - * if not, see . - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_W32_SYSTEM -# ifdef HAVE_WINSOCK2_H -# include -# endif -# include -#else /*!HAVE_W32_SYSTEM*/ -# include -# include -# include -# include -#endif /*!HAVE_W32_SYSTEM*/ -#include -#include -#include -/* #include */ - -#define GNUPG_COMMON_NEED_AFLOCAL 1 -#include "util.h" -#include "i18n.h" -#include "common-defs.h" -#include "logging.h" -#include "sysutils.h" - -#if defined(GPGRT_ENABLE_LOG_MACROS) && defined(log_debug_string) - /* Nothing to do; the libgpgrt functions are used. */ -#else /* Use our own logging functions. */ - -#ifdef HAVE_W32_SYSTEM -# ifndef S_IRWXG -# define S_IRGRP S_IRUSR -# define S_IWGRP S_IWUSR -# endif -# ifndef S_IRWXO -# define S_IROTH S_IRUSR -# define S_IWOTH S_IWUSR -# endif -#endif - - -#ifdef HAVE_W32CE_SYSTEM -# define isatty(a) (0) -#endif - -#undef WITH_IPV6 -#if defined (AF_INET6) && defined(PF_INET) \ - && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) -# define WITH_IPV6 1 -#endif - -#ifndef EAFNOSUPPORT -# define EAFNOSUPPORT EINVAL -#endif -#ifndef INADDR_NONE /* Slowaris is missing that. */ -#define INADDR_NONE ((unsigned long)(-1)) -#endif /*INADDR_NONE*/ - -#ifdef HAVE_W32_SYSTEM -#define sock_close(a) closesocket(a) -#else -#define sock_close(a) close(a) -#endif - - -static estream_t logstream; -static int log_socket = -1; -static char prefix_buffer[80]; -static int with_time; -static int with_prefix; -static int with_pid; -#ifdef HAVE_W32_SYSTEM -static int no_registry; -#endif -static int (*get_pid_suffix_cb)(unsigned long *r_value); -static const char * (*socket_dir_cb)(void); -static int running_detached; -static int force_prefixes; - -static int missing_lf; -static int errorcount; - - -int -log_get_errorcount (int clear) -{ - int n = errorcount; - if( clear ) - errorcount = 0; - return n; -} - -void -log_inc_errorcount (void) -{ - errorcount++; -} - - -/* The following 3 functions are used by es_fopencookie to write logs - to a socket. */ -struct fun_cookie_s -{ - int fd; - int quiet; - int want_socket; - int is_socket; -#ifdef HAVE_W32CE_SYSTEM - int use_writefile; -#endif - char name[1]; -}; - - -/* Write NBYTES of BUFFER to file descriptor FD. */ -static int -writen (int fd, const void *buffer, size_t nbytes, int is_socket) -{ - const char *buf = buffer; - size_t nleft = nbytes; - int nwritten; -#ifndef HAVE_W32_SYSTEM - (void)is_socket; /* Not required. */ -#endif - - while (nleft > 0) - { -#ifdef HAVE_W32_SYSTEM - if (is_socket) - nwritten = send (fd, buf, nleft, 0); - else -#endif - nwritten = write (fd, buf, nleft); - - if (nwritten < 0 && errno == EINTR) - continue; - if (nwritten < 0) - return -1; - nleft -= nwritten; - buf = buf + nwritten; - } - - return 0; -} - - -/* Returns true if STR represents a valid port number in decimal - notation and no garbage is following. */ -static int -parse_portno (const char *str, unsigned short *r_port) -{ - unsigned int value; - - for (value=0; *str && (*str >= '0' && *str <= '9'); str++) - { - value = value * 10 + (*str - '0'); - if (value > 65535) - return 0; - } - if (*str || !value) - return 0; - - *r_port = value; - return 1; -} - - -static gpgrt_ssize_t -fun_writer (void *cookie_arg, const void *buffer, size_t size) -{ - struct fun_cookie_s *cookie = cookie_arg; - - /* FIXME: Use only estream with a callback for socket writing. This - avoids the ugly mix of fd and estream code. */ - - /* Note that we always try to reconnect to the socket but print - error messages only the first time an error occurred. If - RUNNING_DETACHED is set we don't fall back to stderr and even do - not print any error messages. This is needed because detached - processes often close stderr and by writing to file descriptor 2 - we might send the log message to a file not intended for logging - (e.g. a pipe or network connection). */ - if (cookie->want_socket && cookie->fd == -1) - { -#ifdef WITH_IPV6 - struct sockaddr_in6 srvr_addr_in6; -#endif - struct sockaddr_in srvr_addr_in; -#ifndef HAVE_W32_SYSTEM - struct sockaddr_un srvr_addr_un; -#endif - const char *name_for_err = ""; - size_t addrlen; - struct sockaddr *srvr_addr = NULL; - unsigned short port = 0; - int af = AF_LOCAL; - int pf = PF_LOCAL; - const char *name = cookie->name; - - /* Not yet open or meanwhile closed due to an error. */ - cookie->is_socket = 0; - - /* Check whether this is a TCP socket or a local socket. */ - if (!strncmp (name, "tcp://", 6) && name[6]) - { - name += 6; - af = AF_INET; - pf = PF_INET; - } -#ifndef HAVE_W32_SYSTEM - else if (!strncmp (name, "socket://", 9)) - name += 9; -#endif - - if (af == AF_LOCAL) - { - addrlen = 0; -#ifndef HAVE_W32_SYSTEM - memset (&srvr_addr, 0, sizeof srvr_addr); - srvr_addr_un.sun_family = af; - if (!*name && (name = socket_dir_cb ()) && *name) - { - if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1) - { - strncpy (srvr_addr_un.sun_path, - name, sizeof (srvr_addr_un.sun_path)-1); - strcat (srvr_addr_un.sun_path, "/S.log"); - srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; - srvr_addr = (struct sockaddr *)&srvr_addr_un; - addrlen = SUN_LEN (&srvr_addr_un); - name_for_err = srvr_addr_un.sun_path; - } - } - else - { - if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1) - { - strncpy (srvr_addr_un.sun_path, - name, sizeof (srvr_addr_un.sun_path)-1); - srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; - srvr_addr = (struct sockaddr *)&srvr_addr_un; - addrlen = SUN_LEN (&srvr_addr_un); - } - } -#endif /*!HAVE_W32SYSTEM*/ - } - else - { - char *addrstr, *p; -#ifdef HAVE_INET_PTON - void *addrbuf = NULL; -#endif /*HAVE_INET_PTON*/ - - addrstr = xtrymalloc (strlen (name) + 1); - if (!addrstr) - addrlen = 0; /* This indicates an error. */ - else if (*name == '[') - { - /* Check for IPv6 literal address. */ - strcpy (addrstr, name+1); - p = strchr (addrstr, ']'); - if (!p || p[1] != ':' || !parse_portno (p+2, &port)) - { - gpg_err_set_errno (EINVAL); - addrlen = 0; - } - else - { - *p = 0; -#ifdef WITH_IPV6 - af = AF_INET6; - pf = PF_INET6; - memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); - srvr_addr_in6.sin6_family = af; - srvr_addr_in6.sin6_port = htons (port); -#ifdef HAVE_INET_PTON - addrbuf = &srvr_addr_in6.sin6_addr; -#endif /*HAVE_INET_PTON*/ - srvr_addr = (struct sockaddr *)&srvr_addr_in6; - addrlen = sizeof srvr_addr_in6; -#else - gpg_err_set_errno (EAFNOSUPPORT); - addrlen = 0; -#endif - } - } - else - { - /* Check for IPv4 literal address. */ - strcpy (addrstr, name); - p = strchr (addrstr, ':'); - if (!p || !parse_portno (p+1, &port)) - { - gpg_err_set_errno (EINVAL); - addrlen = 0; - } - else - { - *p = 0; - memset (&srvr_addr_in, 0, sizeof srvr_addr_in); - srvr_addr_in.sin_family = af; - srvr_addr_in.sin_port = htons (port); -#ifdef HAVE_INET_PTON - addrbuf = &srvr_addr_in.sin_addr; -#endif /*HAVE_INET_PTON*/ - srvr_addr = (struct sockaddr *)&srvr_addr_in; - addrlen = sizeof srvr_addr_in; - } - } - - if (addrlen) - { -#ifdef HAVE_INET_PTON - if (inet_pton (af, addrstr, addrbuf) != 1) - addrlen = 0; -#else /*!HAVE_INET_PTON*/ - /* We need to use the old function. If we are here v6 - support isn't enabled anyway and thus we can do fine - without. Note that Windows has a compatible inet_pton - function named inetPton, but only since Vista. */ - srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); - if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) - addrlen = 0; -#endif /*!HAVE_INET_PTON*/ - } - - xfree (addrstr); - } - - cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1; - if (cookie->fd == -1) - { - if (!cookie->quiet && !running_detached - && isatty (es_fileno (es_stderr))) - es_fprintf (es_stderr, "failed to create socket for logging: %s\n", - strerror(errno)); - } - else - { - if (connect (cookie->fd, srvr_addr, addrlen) == -1) - { - if (!cookie->quiet && !running_detached - && isatty (es_fileno (es_stderr))) - es_fprintf (es_stderr, "can't connect to '%s%s': %s\n", - cookie->name, name_for_err, strerror(errno)); - sock_close (cookie->fd); - cookie->fd = -1; - } - } - - if (cookie->fd == -1) - { - if (!running_detached) - { - /* Due to all the problems with apps not running - detached but being called with stderr closed or used - for a different purposes, it does not make sense to - switch to stderr. We therefore disable it. */ - if (!cookie->quiet) - { - /* fputs ("switching logging to stderr\n", stderr);*/ - cookie->quiet = 1; - } - cookie->fd = -1; /*fileno (stderr);*/ - } - } - else /* Connection has been established. */ - { - cookie->quiet = 0; - cookie->is_socket = 1; - } - } - - log_socket = cookie->fd; - if (cookie->fd != -1) - { -#ifdef HAVE_W32CE_SYSTEM - if (cookie->use_writefile) - { - DWORD nwritten; - - WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL); - return (gpgrt_ssize_t)size; /* Okay. */ - } -#endif - if (!writen (cookie->fd, buffer, size, cookie->is_socket)) - return (gpgrt_ssize_t)size; /* Okay. */ - } - - if (!running_detached && cookie->fd != -1 - && isatty (es_fileno (es_stderr))) - { - if (*cookie->name) - es_fprintf (es_stderr, "error writing to '%s': %s\n", - cookie->name, strerror(errno)); - else - es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n", - cookie->fd, strerror(errno)); - } - if (cookie->is_socket && cookie->fd != -1) - { - sock_close (cookie->fd); - cookie->fd = -1; - log_socket = -1; - } - - return (gpgrt_ssize_t)size; -} - - -static int -fun_closer (void *cookie_arg) -{ - struct fun_cookie_s *cookie = cookie_arg; - - if (cookie->fd != -1 && cookie->fd != 2) - sock_close (cookie->fd); - xfree (cookie); - log_socket = -1; - return 0; -} - - -/* Common function to either set the logging to a file or a file - descriptor. */ -static void -set_file_fd (const char *name, int fd) -{ - estream_t fp; - int want_socket; -#ifdef HAVE_W32CE_SYSTEM - int use_writefile = 0; -#endif - struct fun_cookie_s *cookie; - - /* Close an open log stream. */ - if (logstream) - { - if (logstream != es_stderr) - es_fclose (logstream); - logstream = NULL; - } - - /* Figure out what kind of logging we want. */ - if (name && !strcmp (name, "-")) - { - name = NULL; - fd = es_fileno (es_stderr); - } - - want_socket = 0; - if (name && !strncmp (name, "tcp://", 6) && name[6]) - want_socket = 1; -#ifndef HAVE_W32_SYSTEM - else if (name && !strncmp (name, "socket://", 9)) - want_socket = 2; -#endif /*HAVE_W32_SYSTEM*/ -#ifdef HAVE_W32CE_SYSTEM - else if (name && !strcmp (name, "GPG2:")) - { - HANDLE hd; - - ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0); - /* Ignore a filename and write the debug output to the GPG2: - device. */ - hd = CreateFile (L"GPG2:", GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd; - name = NULL; - force_prefixes = 1; - use_writefile = 1; - } -#endif /*HAVE_W32CE_SYSTEM*/ - - /* Setup a new stream. */ - - /* The xmalloc below is justified because we can expect that this - function is called only during initialization and there is no - easy way out of this error condition. */ - cookie = xmalloc (sizeof *cookie + (name? strlen (name):0)); - strcpy (cookie->name, name? name:""); - cookie->quiet = 0; - cookie->is_socket = 0; - cookie->want_socket = want_socket; -#ifdef HAVE_W32CE_SYSTEM - cookie->use_writefile = use_writefile; -#endif - if (!name) - cookie->fd = fd; - else if (want_socket) - cookie->fd = -1; - else - { - do - cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT, - (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)); - while (cookie->fd == -1 && errno == EINTR); - } - log_socket = cookie->fd; - - { - es_cookie_io_functions_t io = { NULL }; - io.func_write = fun_writer; - io.func_close = fun_closer; - - fp = es_fopencookie (cookie, "w", io); - } - - /* On error default to a stderr based estream. */ - if (!fp) - fp = es_stderr; - - es_setvbuf (fp, NULL, _IOLBF, 0); - - logstream = fp; - - /* We always need to print the prefix and the pid for socket mode, - so that the server reading the socket can do something - meaningful. */ - force_prefixes = want_socket; - - missing_lf = 0; -} - - -/* Set the file to write log to. The special names NULL and "-" may - be used to select stderr and names formatted like - "socket:///home/foo/mylogs" may be used to write the logging to the - socket "/home/foo/mylogs". If the connection to the socket fails - or a write error is detected, the function writes to stderr and - tries the next time again to connect the socket. - */ -void -log_set_file (const char *name) -{ - set_file_fd (name? name: "-", -1); -} - -void -log_set_fd (int fd) -{ - if (! gnupg_fd_valid (fd)) - log_fatal ("logger-fd is invalid: %s\n", strerror (errno)); - - set_file_fd (NULL, fd); -} - - -/* Set a function to retrieve the directory name of a socket if - * only "socket://" has been given to log_set_file. */ -void -log_set_socket_dir_cb (const char *(*fnc)(void)) -{ - socket_dir_cb = fnc; -} - - -void -log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)) -{ - get_pid_suffix_cb = cb; -} - - -void -log_set_prefix (const char *text, unsigned int flags) -{ - if (text) - { - strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); - prefix_buffer[sizeof (prefix_buffer)-1] = 0; - } - - with_prefix = (flags & GPGRT_LOG_WITH_PREFIX); - with_time = (flags & GPGRT_LOG_WITH_TIME); - with_pid = (flags & GPGRT_LOG_WITH_PID); - running_detached = (flags & GPGRT_LOG_RUN_DETACHED); -#ifdef HAVE_W32_SYSTEM - no_registry = (flags & GPGRT_LOG_NO_REGISTRY); -#endif -} - - -const char * -log_get_prefix (unsigned int *flags) -{ - if (flags) - { - *flags = 0; - if (with_prefix) - *flags |= GPGRT_LOG_WITH_PREFIX; - if (with_time) - *flags |= GPGRT_LOG_WITH_TIME; - if (with_pid) - *flags |= GPGRT_LOG_WITH_PID; - if (running_detached) - *flags |= GPGRT_LOG_RUN_DETACHED; -#ifdef HAVE_W32_SYSTEM - if (no_registry) - *flags |= GPGRT_LOG_NO_REGISTRY; -#endif - } - return prefix_buffer; -} - -/* This function returns true if the file descriptor FD is in use for - logging. This is preferable over a test using log_get_fd in that - it allows the logging code to use more then one file descriptor. */ -int -log_test_fd (int fd) -{ - if (logstream) - { - int tmp = es_fileno (logstream); - if ( tmp != -1 && tmp == fd) - return 1; - } - if (log_socket != -1 && log_socket == fd) - return 1; - return 0; -} - -int -log_get_fd () -{ - return logstream? es_fileno(logstream) : -1; -} - -estream_t -log_get_stream () -{ - if (!logstream) - { - log_set_file (NULL); /* Make sure a log stream has been set. */ - assert (logstream); - } - return logstream; -} - - -static void -print_prefix (int level, int leading_backspace) -{ - if (level != GPGRT_LOG_CONT) - { /* Note this does not work for multiple line logging as we would - * need to print to a buffer first */ - if (with_time && !force_prefixes) - { - struct tm *tp; - time_t atime = time (NULL); - - tp = localtime (&atime); - es_fprintf_unlocked (logstream, "%04d-%02d-%02d %02d:%02d:%02d ", - 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, - tp->tm_hour, tp->tm_min, tp->tm_sec ); - } - if (with_prefix || force_prefixes) - es_fputs_unlocked (prefix_buffer, logstream); - if (with_pid || force_prefixes) - { - unsigned long pidsuf; - int pidfmt; - - if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf))) - es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]", - (unsigned int)getpid (), pidsuf); - else - es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ()); - } - if ((!with_time && (with_prefix || with_pid)) || force_prefixes) - es_putc_unlocked (':', logstream); - /* A leading backspace suppresses the extra space so that we can - correctly output, programname, filename and linenumber. */ - if (!leading_backspace - && (with_time || with_prefix || with_pid || force_prefixes)) - es_putc_unlocked (' ', logstream); - } - - switch (level) - { - case GPGRT_LOG_BEGIN: break; - case GPGRT_LOG_CONT: break; - case GPGRT_LOG_INFO: break; - case GPGRT_LOG_WARN: break; - case GPGRT_LOG_ERROR: break; - case GPGRT_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break; - case GPGRT_LOG_BUG: es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break; - case GPGRT_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break; - default: - es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level); - break; - } -} - - -static void -do_logv (int level, int ignore_arg_ptr, const char *extrastring, - const char *prefmt, const char *fmt, va_list arg_ptr) -{ - int leading_backspace = (fmt && *fmt == '\b'); - - if (!logstream) - { -#ifdef HAVE_W32_SYSTEM - char *tmp; - - tmp = (no_registry - ? NULL - : read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, - "DefaultLogFile")); - log_set_file (tmp && *tmp? tmp : NULL); - xfree (tmp); -#else - log_set_file (NULL); /* Make sure a log stream has been set. */ -#endif - assert (logstream); - } - - es_flockfile (logstream); - if (missing_lf && level != GPGRT_LOG_CONT) - es_putc_unlocked ('\n', logstream ); - missing_lf = 0; - - print_prefix (level, leading_backspace); - if (leading_backspace) - fmt++; - - if (fmt) - { - if (prefmt) - es_fputs_unlocked (prefmt, logstream); - - if (ignore_arg_ptr) - { /* This is used by log_string and comes with the extra - * feature that after a LF the next line is indent at the - * length of the prefix. Note that we do not yet include - * the length of the timestamp and pid in the indent - * computation. */ - const char *p, *pend; - - for (p = fmt; (pend = strchr (p, '\n')); p = pend+1) - es_fprintf_unlocked (logstream, "%*s%.*s", - (int)((p != fmt - && (with_prefix || force_prefixes)) - ?strlen (prefix_buffer)+2:0), "", - (int)(pend - p)+1, p); - es_fputs_unlocked (p, logstream); - } - else - es_vfprintf_unlocked (logstream, fmt, arg_ptr); - if (*fmt && fmt[strlen(fmt)-1] != '\n') - missing_lf = 1; - } - - /* If we have an EXTRASTRING print it now while we still hold the - * lock on the logstream. */ - if (extrastring) - { - int c; - - if (missing_lf) - { - es_putc_unlocked ('\n', logstream); - missing_lf = 0; - } - print_prefix (level, leading_backspace); - es_fputs_unlocked (">> ", logstream); - missing_lf = 1; - while ((c = *extrastring++)) - { - missing_lf = 1; - if (c == '\\') - es_fputs_unlocked ("\\\\", logstream); - else if (c == '\r') - es_fputs_unlocked ("\\r", logstream); - else if (c == '\n') - { - es_fputs_unlocked ("\\n\n", logstream); - if (*extrastring) - { - print_prefix (level, leading_backspace); - es_fputs_unlocked (">> ", logstream); - } - else - missing_lf = 0; - } - else - es_putc_unlocked (c, logstream); - } - if (missing_lf) - { - es_putc_unlocked ('\n', logstream); - missing_lf = 0; - } - } - - if (level == GPGRT_LOG_FATAL) - { - if (missing_lf) - es_putc_unlocked ('\n', logstream); - es_funlockfile (logstream); - exit (2); - } - else if (level == GPGRT_LOG_BUG) - { - if (missing_lf) - es_putc_unlocked ('\n', logstream ); - es_funlockfile (logstream); - /* Using backtrace requires a configure test and to pass - * -rdynamic to gcc. Thus we do not enable it now. */ - /* { */ - /* void *btbuf[20]; */ - /* int btidx, btlen; */ - /* char **btstr; */ - - /* btlen = backtrace (btbuf, DIM (btbuf)); */ - /* btstr = backtrace_symbols (btbuf, btlen); */ - /* if (btstr) */ - /* for (btidx=0; btidx < btlen; btidx++) */ - /* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */ - /* } */ - abort (); - } - else - es_funlockfile (logstream); -} - - -void -log_log (int level, const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt) ; - do_logv (level, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); -} - - -void -log_logv (int level, const char *fmt, va_list arg_ptr) -{ - do_logv (level, 0, NULL, NULL, fmt, arg_ptr); -} - - -/* Same as log_logv but PREFIX is printed immediately before FMT. - * Note that PREFIX is an additional string and independent of the - * prefix set by log_set_prefix. */ -void -log_logv_prefix (int level, const char *prefix, - const char *fmt, va_list arg_ptr) -{ - do_logv (level, 0, NULL, prefix, fmt, arg_ptr); -} - - -static void -do_log_ignore_arg (int level, const char *str, ...) -{ - va_list arg_ptr; - va_start (arg_ptr, str); - do_logv (level, 1, NULL, NULL, str, arg_ptr); - va_end (arg_ptr); -} - - -/* Log STRING at LEVEL but indent from the second line on by the - * length of the prefix. */ -void -log_string (int level, const char *string) -{ - /* We need a dummy arg_ptr, but there is no portable way to create - * one. So we call the do_logv function through a variadic wrapper. */ - do_log_ignore_arg (level, string); -} - - -void -log_info (const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_INFO, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); -} - - -void -log_error (const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_ERROR, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); - /* Protect against counter overflow. */ - if (errorcount < 30000) - errorcount++; -} - - -void -log_fatal (const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_FATAL, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); - abort (); /* Never called; just to make the compiler happy. */ -} - - -void -log_bug (const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_BUG, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); - abort (); /* Never called; just to make the compiler happy. */ -} - - -void -log_debug (const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_DEBUG, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); -} - - -/* The same as log_debug but at the end of the output STRING is - * printed with LFs expanded to include the prefix and a final --end-- - * marker. */ -void -log_debug_string (const char *string, const char *fmt, ...) -{ - va_list arg_ptr ; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_DEBUG, 0, string, NULL, fmt, arg_ptr); - va_end (arg_ptr); -} - - -void -log_printf (const char *fmt, ...) -{ - va_list arg_ptr; - - va_start (arg_ptr, fmt); - do_logv (fmt ? GPGRT_LOG_CONT : GPGRT_LOG_BEGIN, 0, NULL, NULL, fmt, arg_ptr); - va_end (arg_ptr); -} - - -/* Flush the log - this is useful to make sure that the trailing - linefeed has been printed. */ -void -log_flush (void) -{ - do_log_ignore_arg (GPGRT_LOG_CONT, NULL); -} - - -/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw - dump, with TEXT just an empty string, print a trailing linefeed, - otherwise print an entire debug line. */ -void -log_printhex (const void *buffer, size_t length, const char *text) -{ - if (text && *text) - log_debug ("%s ", text); - if (length) - { - const unsigned char *p = buffer; - log_printf ("%02X", *p); - for (length--, p++; length--; p++) - log_printf (" %02X", *p); - } - if (text) - log_printf ("\n"); -} - - -/* -void -log_printcanon () {} -is found in sexputils.c -*/ - -/* -void -log_printsexp () {} -is found in sexputils.c -*/ - -/* Print a microsecond timestamp followed by a FORMAT. */ -void -log_clock (const char *fmt, ...) -{ -#if ENABLE_LOG_CLOCK - static unsigned long long initial; - struct timespec tv; - unsigned long long now; - char clockbuf[50]; - va_list arg_ptr; - - if (clock_gettime (CLOCK_REALTIME, &tv)) - { - log_debug ("error getting the realtime clock value\n"); - return; - } - now = tv.tv_sec * 1000000000ull; - now += tv.tv_nsec; - - if (!initial) - initial = now; - - snprintf (clockbuf, sizeof clockbuf, "[%6llu] ", (now - initial)/1000); - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_DEBUG, 0, NULL, clockbuf, fmt, arg_ptr); - va_end (arg_ptr); - -#else /*!ENABLE_LOG_CLOCK*/ - - /* You may need to link with -ltr to use the above code. */ - va_list arg_ptr; - - va_start (arg_ptr, fmt); - do_logv (GPGRT_LOG_DEBUG, 0, NULL, "[no clock] ", fmt, arg_ptr); - va_end (arg_ptr); - -#endif /*!ENABLE_LOG_CLOCK*/ -} - - -#ifdef GPGRT_HAVE_MACRO_FUNCTION -void -bug_at( const char *file, int line, const char *func ) -{ - log_log (GPGRT_LOG_BUG, "... this is a bug (%s:%d:%s)\n", file, line, func); - abort (); /* Never called; just to make the compiler happy. */ -} -#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ -void -bug_at( const char *file, int line ) -{ - log_log (GPGRT_LOG_BUG, "you found a bug ... (%s:%d)\n", file, line); - abort (); /* Never called; just to make the compiler happy. */ -} -#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ - - -#ifdef GPGRT_HAVE_MACRO_FUNCTION -void -_log_assert (const char *expr, const char *file, int line, const char *func) -{ - log_log (GPGRT_LOG_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n", - expr, func, file, line); - abort (); /* Never called; just to make the compiler happy. */ -} -#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ -void -_log_assert (const char *expr, const char *file, int line) -{ - log_log (GPGRT_LOG_BUG, "Assertion \"%s\" failed (%s:%d)\n", - expr, file, line); - abort (); /* Never called; just to make the compiler happy. */ -} -#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ - -#endif /* Use our own logging functions. */ diff --git a/common/logging.h b/common/logging.h index a20b8f895..a5800cb4c 100644 --- a/common/logging.h +++ b/common/logging.h @@ -38,10 +38,9 @@ #include "mischelp.h" #include "w32help.h" -#if defined(GPGRT_ENABLE_LOG_MACROS) && defined(log_debug_string) - /* We use the libgpg-error provided log functions. but we need one - * more function: */ -# ifdef GPGRT_HAVE_MACRO_FUNCTION +/* We use the libgpg-error provided log functions. but we need one + * more function: */ +#ifdef GPGRT_HAVE_MACRO_FUNCTION # define BUG() bug_at ( __FILE__, __LINE__, __FUNCTION__) static inline void bug_at (const char *file, int line, const char *func) GPGRT_ATTR_NORETURN; @@ -52,7 +51,7 @@ bug_at (const char *file, int line, const char *func) file, line, func); abort (); } -# else +#else # define BUG() bug_at ( __FILE__, __LINE__) static inline void bug_at (const char *file, int line) GPGRT_ATTR_NORETURN; @@ -62,94 +61,9 @@ bug_at (const char *file, int line) gpgrt_log (GPGRT_LOGLVL_BUG, "there is a bug at %s:%d\n", file, line); abort (); } -# endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ - - -#else /* Use gnupg internal logging functions. */ - -int log_get_errorcount (int clear); -void log_inc_errorcount (void); -void log_set_file( const char *name ); -void log_set_fd (int fd); -void log_set_socket_dir_cb (const char *(*fnc)(void)); -void log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)); -void log_set_prefix (const char *text, unsigned int flags); -const char *log_get_prefix (unsigned int *flags); -int log_test_fd (int fd); -int log_get_fd(void); -estream_t log_get_stream (void); - -#ifdef GPGRT_HAVE_MACRO_FUNCTION - void bug_at (const char *file, int line, const char *func) - GPGRT_ATTR_NORETURN; - void _log_assert (const char *expr, const char *file, int line, - const char *func) GPGRT_ATTR_NORETURN; -# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__) -# define log_assert(expr) \ - ((expr) \ - ? (void) 0 \ - : _log_assert (#expr, __FILE__, __LINE__, __FUNCTION__)) -#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ - void bug_at (const char *file, int line); - void _log_assert (const char *expr, const char *file, int line); -# define BUG() bug_at( __FILE__ , __LINE__ ) -# define log_assert(expr) \ - ((expr) \ - ? (void) 0 \ - : _log_assert (#expr, __FILE__, __LINE__)) #endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ -/* Flag values for log_set_prefix. */ -#define GPGRT_LOG_WITH_PREFIX 1 -#define GPGRT_LOG_WITH_TIME 2 -#define GPGRT_LOG_WITH_PID 4 -#define GPGRT_LOG_RUN_DETACHED 256 -#define GPGRT_LOG_NO_REGISTRY 512 -/* Log levels as used by log_log. */ -enum jnlib_log_levels { - GPGRT_LOG_BEGIN, - GPGRT_LOG_CONT, - GPGRT_LOG_INFO, - GPGRT_LOG_WARN, - GPGRT_LOG_ERROR, - GPGRT_LOG_FATAL, - GPGRT_LOG_BUG, - GPGRT_LOG_DEBUG -}; -#define GPGRT_LOGLVL_BEGIN GPGRT_LOG_BEGIN -#define GPGRT_LOGLVL_CONT GPGRT_LOG_CONT -#define GPGRT_LOGLVL_INFO GPGRT_LOG_INFO -#define GPGRT_LOGLVL_WARN GPGRT_LOG_WARN -#define GPGRT_LOGLVL_ERROR GPGRT_LOG_ERROR -#define GPGRT_LOGLVL_FATAL GPGRT_LOG_FATAL -#define GPGRT_LOGLVL_BUG GPGRT_LOG_BUG -#define GPGRT_LOGLVL_DEBUG GPGRT_LOG_DEBUG - -void log_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3); -void log_logv (int level, const char *fmt, va_list arg_ptr); -void log_logv_prefix (int level, const char *prefix, - const char *fmt, va_list arg_ptr); -void log_string (int level, const char *string); -void log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); -void log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); -void log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); -void log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); -void log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); -void log_debug_string (const char *string, const char *fmt, - ...) GPGRT_ATTR_PRINTF(2,3); -void log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); -void log_flush (void); - -/* Print a hexdump of BUFFER. With TEXT passes as NULL print just the - raw dump, with TEXT being an empty string, print a trailing - linefeed, otherwise print an entire debug line with TEXT followed - by the hexdump and a final LF. */ -void log_printhex (const void *buffer, size_t length, const char *text); - -void log_clock (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); - -#endif /* Use gnupg internal logging functions. */ /* Some handy assertion macros which don't abort. */ diff --git a/common/name-value.h b/common/name-value.h index db9270a6c..5c24b8db1 100644 --- a/common/name-value.h +++ b/common/name-value.h @@ -102,7 +102,7 @@ gpg_error_t nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp); /* Parsing and serialization. */ -/* Parse STREAM and return a newly allocated private key container +/* Parse STREAM and return a newly allocated name-value container structure in RESULT. If ERRLINEP is given, the line number the parser was last considering is stored there. */ gpg_error_t nvc_parse (nvc_t *result, int *errlinep, estream_t stream); diff --git a/common/percent.c b/common/percent.c index 569c5fd99..eeb026fbe 100644 --- a/common/percent.c +++ b/common/percent.c @@ -87,6 +87,50 @@ percent_plus_escape (const char *string) } +/* Create a newly alloced string from (DATA,DATALEN) with embedded + * Nuls quoted as %00. The standard percent unescaping can be + * used to reverse this encoding. */ +char * +percent_data_escape (const void *data, size_t datalen) +{ + char *buffer, *p; + const char *s; + size_t n, length; + + for (length=1, s=data, n=datalen; n; s++, n--) + { + if (!*s || *s == '%') + length += 3; + else + length++; + } + + buffer = p = xtrymalloc (length); + if (!buffer) + return NULL; + + for (s=data, n=datalen; n; s++, n--) + { + if (!*s) + { + memcpy (p, "%00", 3); + p += 3; + } + else if (*s == '%') + { + memcpy (p, "%25", 3); + p += 3; + } + else + *p++ = *s; + } + *p = 0; + + return buffer; + +} + + /* Do the percent and plus/space unescaping from STRING to BUFFER and return the length of the valid buffer. Plus unescaping is only done if WITHPLUS is true. An escaped Nul character will be diff --git a/common/t-percent.c b/common/t-percent.c index 145a89bf3..94ece9249 100644 --- a/common/t-percent.c +++ b/common/t-percent.c @@ -99,6 +99,55 @@ test_percent_plus_escape (void) } +static void +test_percent_data_escape (void) +{ + static struct { + const char *data; + size_t datalen; + const char *expect; + } tbl[] = { + { + "", 0, + "" + }, { + "a", 1, + "a", + }, { + "%22", 3, + "%2522" + }, { + "%%", 3, + "%25%25%00" + }, { + "\n \0BC\t", 6, + "\n %00BC\t" + }, { NULL, 0, NULL } + }; + char *buf; + int i; + size_t len; + + for (i=0; tbl[i].data; i++) + { + buf = percent_data_escape (tbl[i].data, tbl[i].datalen); + if (!buf) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf, tbl[i].expect)) + fail (i); + len = percent_plus_unescape_inplace (buf, 0); + if (len != tbl[i].datalen) + fail (i); + else if (memcmp (buf, tbl[i].data, tbl[i].datalen)) + fail (i); + xfree (buf); + } +} + + int main (int argc, char **argv) @@ -109,6 +158,6 @@ main (int argc, char **argv) /* FIXME: We escape_unescape is not tested - only percent_plus_unescape. */ test_percent_plus_escape (); - + test_percent_data_escape (); return 0; } diff --git a/common/util.h b/common/util.h index f3722812d..682415d92 100644 --- a/common/util.h +++ b/common/util.h @@ -39,21 +39,6 @@ * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ -#if GPG_ERROR_VERSION_NUMBER < 0x011a00 /* 1.26 */ -# define GPG_ERR_UNKNOWN_FLAG 309 -# define GPG_ERR_INV_ORDER 310 -# define GPG_ERR_ALREADY_FETCHED 311 -# define GPG_ERR_TRY_LATER 312 -# define GPG_ERR_SYSTEM_BUG 666 -# define GPG_ERR_DNS_UNKNOWN 711 -# define GPG_ERR_DNS_SECTION 712 -# define GPG_ERR_DNS_ADDRESS 713 -# define GPG_ERR_DNS_NO_QUERY 714 -# define GPG_ERR_DNS_NO_ANSWER 715 -# define GPG_ERR_DNS_CLOSED 716 -# define GPG_ERR_DNS_VERIFY 717 -# define GPG_ERR_DNS_TIMEOUT 718 -#endif /* Hash function used with libksba. */ @@ -216,6 +201,7 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); +char *percent_data_escape (const void *data, size_t datalen); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); diff --git a/configure.ac b/configure.ac index 0665115dc..78a03c420 100644 --- a/configure.ac +++ b/configure.ac @@ -53,7 +53,7 @@ AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org]) # build-aux/speedo.mk and Makefile.am AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg24", [swdb tag for this branch]) -NEED_GPG_ERROR_VERSION=1.24 +NEED_GPG_ERROR_VERSION=1.29 NEED_LIBGCRYPT_API=1 NEED_LIBGCRYPT_VERSION=1.7.0 @@ -507,6 +507,9 @@ AH_BOTTOM([ #define GNUPG_PRIVATE_KEYS_DIR "private-keys-v1.d" #define GNUPG_OPENPGP_REVOC_DIR "openpgp-revocs.d" +#define GNUPG_DEF_COPYRIGHT_LINE \ + "Copyright (C) 2018 Free Software Foundation, Inc." + /* For some systems (DOS currently), we hardcode the path here. For POSIX systems the values are constructed by the Makefiles, so that the values may be overridden by the make invocations; this is to @@ -1216,6 +1219,8 @@ elif test x"$with_mailprog" != xno ; then AC_SUBST(SENDMAIL,$with_mailprog) AC_MSG_RESULT($with_mailprog) fi +AC_DEFINE_UNQUOTED(NAME_OF_SENDMAIL,"$SENDMAIL", + [Tool with sendmail -t interface]) # @@ -1387,18 +1392,17 @@ AC_CHECK_DECLS(getpagesize) AC_FUNC_FSEEKO AC_FUNC_VPRINTF AC_FUNC_FORK -AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap canonicalize_file_name]) -AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r strtoull]) -AC_CHECK_FUNCS([setenv unsetenv fcntl ftruncate inet_ntop]) -AC_CHECK_FUNCS([canonicalize_file_name]) -AC_CHECK_FUNCS([gettimeofday getrusage getrlimit setrlimit clock_gettime]) -AC_CHECK_FUNCS([atexit raise getpagesize strftime nl_langinfo setlocale]) -AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe getaddrinfo]) -AC_CHECK_FUNCS([ttyname rand ftello fsync stat lstat]) -AC_CHECK_FUNCS([memicmp stpcpy strsep strlwr strtoul memmove stricmp strtol \ - memrchr isascii timegm getrusage setrlimit stat setlocale \ - flockfile funlockfile getpwnam getpwuid \ - getenv inet_pton strpbrk]) +AC_CHECK_FUNCS([atexit canonicalize_file_name clock_gettime ctermid \ + fcntl flockfile fsync ftello ftruncate funlockfile \ + getaddrinfo getenv getpagesize getpwnam getpwuid \ + getrlimit getrusage gettimeofday gmtime_r \ + inet_ntop inet_pton isascii lstat \ + memicmp memmove memrchr mmap nl_langinfo pipe \ + raise rand setenv setlocale setrlimit sigaction \ + sigprocmask stat stpcpy strcasecmp strerror strftime \ + stricmp strlwr strncasecmp strpbrk strsep \ + strtol strtoul strtoull tcgetattr timegm times \ + ttyname unsetenv wait4 waitpid ]) # On some systems (e.g. Solaris) nanosleep requires linking to librl. # Given that we use nanosleep only as an optimization over a select diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index 7324aae46..ffac816f9 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -612,6 +612,8 @@ libdns_init (void) } } + ld.resolv_conf->options.recurse = recursive_resolver_p (); + /* dns_hints_local for stub mode, dns_hints_root for recursive. */ ld.hints = (recursive_resolver ? dns_hints_root (ld.resolv_conf, &derr) diff --git a/dirmngr/dns.c b/dirmngr/dns.c index 8e8b6db0b..77f83f437 100644 --- a/dirmngr/dns.c +++ b/dirmngr/dns.c @@ -5371,13 +5371,16 @@ struct dns_resolv_conf *dns_resconf_open(int *error) { if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) goto syerr; - dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); - dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); - /* - * XXX: If gethostname() returned a string without any label - * separator, then search[0][0] should be NUL. + * If gethostname() returned a string without any label + * separator, then search[0][0] should be NUL. */ + if (strchr (resconf->search[0], '.')) { + dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); + } else { + memset (resconf->search[0], 0, sizeof resconf->search[0]); + } dns_resconf_acquire(resconf); @@ -5549,6 +5552,7 @@ int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { unsigned short port = 0; int ch, af = AF_INET, error; + memset(ss, 0, sizeof *ss); while ((ch = *src++)) { switch (ch) { case ' ': @@ -6096,17 +6100,9 @@ int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { dns_anyconf_skip(" \t", fp); if ('[' == dns_anyconf_peek(fp)) { - dns_anyconf_skip("[ \t", fp); + dns_anyconf_skip("[! \t", fp); - for (;;) { - if ('!' == dns_anyconf_peek(fp)) { - dns_anyconf_skip("! \t", fp); - /* FIXME: negating statuses; currently not implemented */ - dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ - break; - } - - if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) break; + while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_skip("= \t", fp); if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_pop(&cf); /* discard status */ @@ -6319,6 +6315,7 @@ int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsi int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; int error; + memset(&resconf->iface, 0, sizeof (struct sockaddr_storage)); if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) return error; @@ -6630,6 +6627,7 @@ struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { for (i = 0; i < lengthof(root_hints); i++) { af = root_hints[i].af; + memset(&ss, 0, sizeof ss); if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) goto error; @@ -7123,6 +7121,8 @@ static int dns_socket(struct sockaddr *local, int type, int *error_) { if (type != SOCK_DGRAM) return fd; +#define LEAVE_SELECTION_OF_PORT_TO_KERNEL +#if !defined(LEAVE_SELECTION_OF_PORT_TO_KERNEL) /* * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by * default. Though the ephemeral range is quite small on OS X @@ -7148,6 +7148,7 @@ static int dns_socket(struct sockaddr *local, int type, int *error_) { /* NB: continue to next bind statement */ } +#endif if (0 == bind(fd, local, dns_sa_len(local))) return fd; @@ -7619,8 +7620,23 @@ retry: so->state++; /* FALL THROUGH */ case DNS_SO_UDP_CONN: + udp_connect_retry: error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)); dns_trace_sys_connect(so->trace, so->udp, SOCK_DGRAM, (struct sockaddr *)&so->remote, error); + + /* Linux returns EINVAL when address was bound to + localhost and it's external IP address now. */ + if (error == EINVAL) { + struct sockaddr unspec_addr; + memset (&unspec_addr, 0, sizeof unspec_addr); + unspec_addr.sa_family = AF_UNSPEC; + connect(so->udp, &unspec_addr, sizeof unspec_addr); + goto udp_connect_retry; + } else if (error == ECONNREFUSED) + /* Error for previous socket operation may + be reserverd asynchronously. */ + goto udp_connect_retry; + if (error) goto error; @@ -8829,7 +8845,10 @@ exec: if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) dgoto(R->sp, DNS_R_FOREACH_A); - if ((error = dns_so_check(&R->so))) + error = dns_so_check(&R->so); + if (R->so.state != DNS_SO_SOCKS_CONN && error == ECONNREFUSED) + dgoto(R->sp, DNS_R_FOREACH_A); + else if (error) goto error; if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) @@ -8962,7 +8981,10 @@ exec: if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) dgoto(R->sp, DNS_R_FOREACH_AAAA); - if ((error = dns_so_check(&R->so))) + error = dns_so_check(&R->so); + if (error == ECONNREFUSED) + dgoto(R->sp, DNS_R_FOREACH_AAAA); + else if (error) goto error; if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) @@ -10874,6 +10896,7 @@ static int send_query(int argc, char *argv[]) { struct dns_socket *so; int error, type; + memset(&ss, 0, sizeof ss); if (argc > 1) { ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; diff --git a/doc/DETAILS b/doc/DETAILS index e7567f7e3..eb6d7dd4b 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -59,6 +59,7 @@ described here. - uat :: User attribute (same as user id except for field 10). - sig :: Signature - rev :: Revocation signature + - rvs :: Recocation signature (standalone) [since 2.2.9] - fpr :: Fingerprint (fingerprint is in field 10) - pkd :: Public key data [*] - grp :: Keygrip @@ -179,6 +180,9 @@ described here. revocation key is also given here, 'x' and 'l' is used the same way. This field if not used for X.509. + "rev" and "rvs" may be followed by a comma and a 2 digit hexnumber + with the revocation reason. + *** Field 12 - Key capabilities The defined capabilities are: @@ -207,12 +211,13 @@ described here. For "uid" records this field lists the preferences in the same way gpg's --edit-key menu does. - For "sig" records, this is the fingerprint of the key that issued - the signature. Note that this may only be filled if the signature - verified correctly. Note also that for various technical reasons, - this fingerprint is only available if --no-sig-cache is used. - Since 2.2.7 this field will also be set if the key is missing but - the signature carries an issuer fingerprint as meta data. + For "sig", "rev" and "rvs" records, this is the fingerprint of the + key that issued the signature. Note that this may only be filled + if the signature verified correctly. Note also that for various + technical reasons, this fingerprint is only available if + --no-sig-cache is used. Since 2.2.7 this field will also be set + if the key is missing but the signature carries an issuer + fingerprint as meta data. *** Field 14 - Flag field @@ -260,6 +265,12 @@ described here. optionally followed by a space and an URL. This goes along with the previous field. The URL is quoted in C style. +*** Field 21 - Comment + + This is currently only used in "rev" and "rvs" records to carry + the the comment field of the recocation reason. The value is + quoted in C style. + ** Special fields *** PKD - Public key data diff --git a/doc/gpg.texi b/doc/gpg.texi index a12b5afee..4cfd00079 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -328,7 +328,7 @@ following the "sig" tag (and thus before the flags described below. A "!" indicates that the signature has been successfully verified, a "-" denotes a bad signature and a "%" is used if an error occurred while checking the signature (e.g. a non supported algorithm). Signatures -where the public key is not availabale are not listed; to see their +where the public key is not available are not listed; to see their keyids the command @option{--list-sigs} can be used. For each signature listed, there are several flags in between the @@ -353,6 +353,16 @@ may thus be used to see what keys @command{@gpgname} might use. In particular external methods as defined by @option{--auto-key-locate} may be used to locate a key. Only public keys are listed. +@item --show-keys +@opindex show-keys +This commands takes OpenPGP keys as input and prints information about +them in the same way the command @option{--list-keys} does for locally +stored key. In addition the list options @code{show-unusable-uids}, +@code{show-unusable-subkeys}, @code{show-notations} and +@code{show-policy-urls} are also enabled. As usual for automated +processing, this command should be combined with the option +@option{--with-colons}. + @item --fingerprint @opindex fingerprint List all keys (or the specified ones) along with their @@ -2305,7 +2315,8 @@ opposite meaning. The options are: Show a listing of the key as imported right before it is stored. This can be combined with the option @option{--dry-run} to only look at keys; the option @option{show-only} is a shortcut for this - combination. Note that suffixes like '#' for "sec" and "sbb" lines + combination. The command @option{--show-keys} is another shortcut + for this. Note that suffixes like '#' for "sec" and "sbb" lines may or may not be printed. @item import-export @@ -2417,6 +2428,11 @@ The available properties are: Boolean indicating whether a key or subkey is a secret one. (drop-subkey) + @item usage + A string indicating the usage flags for the subkey, from the + sequence ``ecsa?''. For example, a subkey capable of just signing + and authentication would be an exact match for ``sa''. (drop-subkey) + @item sig_created @itemx sig_created_d The first is the timestamp a signature packet was created. The @@ -3368,7 +3384,7 @@ absolute date in the form YYYY-MM-DD. Defaults to "0". @opindex default-new-key-algo @var{string} This option can be used to change the default algorithms for key generation. The @var{string} is similar to the arguments required for -the command @option{--quick-add-key} but slighly different. For +the command @option{--quick-add-key} but slightly different. For example the current default of @code{"rsa2048/cert,sign+rsa2048/encr"} (or @code{"rsa3072"}) can be changed to the value of what we currently call future default, which is @code{"ed25519/cert,sign+cv25519/encr"}. diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index a9e6d1e7a..81af28105 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -399,7 +399,7 @@ comes with almost all German banking cards. This application adds read-only support for keys and certificates stored on a @uref{http://www.smartcard-hsm.com, SmartCard-HSM}. -To generate keys and store certifiates you may use +To generate keys and store certificates you may use @uref{https://github.com/OpenSC/OpenSC/wiki/SmartCardHSM, OpenSC} or the tools from @uref{http://www.openscdp.org, OpenSCDP}. diff --git a/doc/tools.texi b/doc/tools.texi index 9301334c7..7becf67e2 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -290,7 +290,7 @@ Check the options for the component @var{component}. Apply the configuration settings listed in @var{file} to the configuration files. If @var{file} has no suffix and no slashes the command first tries to read a file with the suffix @code{.prf} from -the the data directory (@code{gpgconf --list-dirs datadir}) before it +the data directory (@code{gpgconf --list-dirs datadir}) before it reads the file verbatim. A profile is divided into sections using the bracketed component name. Each section then lists the option which shall go into the respective configuration file. diff --git a/g10/Makefile.am b/g10/Makefile.am index b8b92d702..3b4464364 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -152,6 +152,7 @@ gpg_sources = server.c \ trust.c $(trust_source) $(tofu_source) \ $(card_source) \ exec.c exec.h \ + key-clean.c key-clean.h \ key-check.c key-check.h gpg_SOURCES = gpg.c \ diff --git a/g10/card-util.c b/g10/card-util.c index 587f181f2..b7eedc0c8 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -851,6 +851,7 @@ fetch_url (ctrl_t ctrl) } } + agent_release_card_info (&info); return rc; } diff --git a/g10/export.c b/g10/export.c index c538dc1f1..e94e959fb 100644 --- a/g10/export.c +++ b/g10/export.c @@ -41,6 +41,8 @@ #include "../common/init.h" #include "trustdb.h" #include "call-agent.h" +#include "key-clean.h" + /* An object to keep track of subkeys. */ struct subkey_list_s @@ -2001,12 +2003,19 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } /* Always do the cleaning on the public key part if requested. - * Note that both export-clean and export-minimal only apply to - * UID sigs (0x10, 0x11, 0x12, and 0x13). A designated - * revocation is never stripped, even with export-minimal set. */ + * A designated revocation is never stripped, even with + * export-minimal set. */ if ((options & EXPORT_CLEAN)) - clean_key (ctrl, keyblock, opt.verbose, - (options&EXPORT_MINIMAL), NULL, NULL); + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, opt.verbose, + (options&EXPORT_MINIMAL), NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, + (options&EXPORT_MINIMAL)? KEY_CLEAN_ALL + /**/ : KEY_CLEAN_AUTHENCR, + NULL, NULL); + commit_kbnode (&keyblock); + } if (export_keep_uid) { diff --git a/g10/getkey.c b/g10/getkey.c index 55c862877..41afeb9e8 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -677,6 +677,24 @@ pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key) } +/* Specialized version of get_pubkey which retrieves the key based on + * information in SIG. In contrast to get_pubkey PK is required. */ +gpg_error_t +get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig) +{ + const byte *fpr; + size_t fprlen; + + /* First try the new ISSUER_FPR info. */ + fpr = issuer_fpr_raw (sig, &fprlen); + if (fpr && !get_pubkey_byfprint (ctrl, pk, NULL, fpr, fprlen)) + return 0; + + /* Fallback to use the ISSUER_KEYID. */ + return get_pubkey (ctrl, pk, sig->keyid); +} + + /* Return the public key with the key id KEYID and store it at PK. * The resources in *PK should be released using * release_public_key_parts(). This function also stores a copy of @@ -739,8 +757,9 @@ get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid) /* Do a lookup. */ { struct getkey_ctx_s ctx; - KBNODE kb = NULL; - KBNODE found_key = NULL; + kbnode_t kb = NULL; + kbnode_t found_key = NULL; + memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; @@ -863,6 +882,28 @@ get_pubkey_fast (PKT_public_key * pk, u32 * keyid) } +/* Return the entire keyblock used to create SIG. This is a + * specialized version of get_pubkeyblock. + * + * FIXME: This is a hack because get_pubkey_for_sig was already called + * and it could have used a cache to hold the key. */ +kbnode_t +get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig) +{ + const byte *fpr; + size_t fprlen; + kbnode_t keyblock; + + /* First try the new ISSUER_FPR info. */ + fpr = issuer_fpr_raw (sig, &fprlen); + if (fpr && !get_pubkey_byfprint (ctrl, NULL, &keyblock, fpr, fprlen)) + return keyblock; + + /* Fallback to use the ISSUER_KEYID. */ + return get_pubkeyblock (ctrl, sig->keyid); +} + + /* Return the key block for the key with key id KEYID or NULL, if an * error occurs. Use release_kbnode() to release the key block. * @@ -1802,6 +1843,8 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; + /* FIXME: We should get the handle from the cache like we do in + * get_pubkey. */ ctx.kr_handle = keydb_new (); if (!ctx.kr_handle) return gpg_error_from_syserror (); @@ -3142,7 +3185,7 @@ buf_to_sig (const byte * buf, size_t len) if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0) { - xfree (sig); + free_seckey_enc (sig); sig = NULL; } diff --git a/g10/gpg.c b/g10/gpg.c index 70bdddfda..600f8440d 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -150,6 +150,7 @@ enum cmd_and_opt_values aSearchKeys, aRefreshKeys, aFetchKeys, + aShowKeys, aExport, aExportSecret, aExportSecretSub, @@ -500,6 +501,7 @@ static ARGPARSE_OPTS opts[] = { N_("update all keys from a keyserver")), ARGPARSE_c (aLocateKeys, "locate-keys", "@"), ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ), + ARGPARSE_c (aShowKeys, "show-keys" , "@" ), ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ), ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ), ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ), @@ -740,6 +742,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aListKeys, "list-key", "@"), /* alias */ ARGPARSE_c (aListSigs, "list-sig", "@"), /* alias */ ARGPARSE_c (aCheckKeys, "check-sig", "@"), /* alias */ + ARGPARSE_c (aShowKeys, "show-key", "@"), /* alias */ ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"), ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"), ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"), @@ -2642,6 +2645,17 @@ main (int argc, char **argv) greeting=1; break; + case aShowKeys: + set_cmd (&cmd, pargs.r_opt); + opt.import_options |= IMPORT_SHOW; + opt.import_options |= IMPORT_DRY_RUN; + opt.import_options &= ~IMPORT_REPAIR_KEYS; + opt.list_options |= LIST_SHOW_UNUSABLE_UIDS; + opt.list_options |= LIST_SHOW_UNUSABLE_SUBKEYS; + opt.list_options |= LIST_SHOW_NOTATIONS; + opt.list_options |= LIST_SHOW_POLICY_URLS; + break; + case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aDecryptFiles: multifile=1; /* fall through */ @@ -3611,7 +3625,7 @@ main (int argc, char **argv) else { pargs.err = ARGPARSE_PRINT_ERROR; - /* The argparse fucntion calls a plain exit and thus + /* The argparse function calls a plain exit and thus * we need to print a status here. */ write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL)); @@ -4638,6 +4652,7 @@ main (int argc, char **argv) case aFastImport: opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: + case aShowKeys: import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options, opt.key_origin, opt.key_origin_url); break; diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 094bc7614..b3f7ecdce 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1835,7 +1835,7 @@ signature (const char *option, int argc, char *argv[], void *cookie) debug ("Wrote signature packet:\n"); dump_component (&pkt); - xfree (sig); + free_seckey_enc (sig); release_kbnode (si.issuer_kb); xfree (si.revocation_key); diff --git a/g10/gpgv.c b/g10/gpgv.c index 16d90440c..b33590655 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -772,3 +772,18 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) return 0; } + + +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + (void)sig; + (void)r_commentlen; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + return 0; +} diff --git a/g10/import.c b/g10/import.c index 9fc769df9..1eb3ecceb 100644 --- a/g10/import.c +++ b/g10/import.c @@ -41,6 +41,7 @@ #include "../common/init.h" #include "../common/mbox-util.h" #include "key-check.h" +#include "key-clean.h" struct import_stats_s @@ -113,8 +114,8 @@ static int import_secret_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg); -static int import_revoke_cert (ctrl_t ctrl, - kbnode_t node, struct import_stats_s *stats); +static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, + struct import_stats_s *stats); static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self); static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, @@ -494,7 +495,9 @@ import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, if (!stats_handle) { - import_print_stats (stats); + if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN)) + != (IMPORT_SHOW | IMPORT_DRY_RUN)) + import_print_stats (stats); import_release_stats_handle (stats); } @@ -588,7 +591,7 @@ import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, screener, screener_arg); else if (keyblock->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (keyblock->pkt->pkt.signature) ) - rc = import_revoke_cert (ctrl, keyblock, stats); + rc = import_revoke_cert (ctrl, keyblock, options, stats); else { log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); @@ -778,7 +781,7 @@ read_block( IOBUF a, int with_meta, struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t root = NULL; - int in_cert, in_v3key; + int in_cert, in_v3key, skip_sigs; *r_v3keys = 0; @@ -797,6 +800,7 @@ read_block( IOBUF a, int with_meta, if (!with_meta) parsectx.skip_meta = 1; in_v3key = 0; + skip_sigs = 0; while ((rc=parse_packet (&parsectx, pkt)) != -1) { if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY @@ -811,8 +815,25 @@ read_block( IOBUF a, int with_meta, } else if (rc ) /* (ignore errors) */ { + skip_sigs = 0; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) ; /* Do not show a diagnostic. */ + else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET + && (pkt->pkttype == PKT_USER_ID + || pkt->pkttype == PKT_ATTRIBUTE)) + { + /* This indicates a too large user id or attribute + * packet. We skip this packet and all following + * signatures. Sure, this won't allow to repair a + * garbled keyring in case one of the signatures belong + * to another user id. However, this better mitigates + * DoS using inserted user ids. */ + skip_sigs = 1; + } + else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET + && (pkt->pkttype == PKT_OLD_COMMENT + || pkt->pkttype == PKT_COMMENT)) + ; /* Ignore too large comment packets. */ else { log_error("read_block: read error: %s\n", gpg_strerror (rc) ); @@ -824,76 +845,88 @@ read_block( IOBUF a, int with_meta, continue; } - if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY - || pkt->pkttype == PKT_SECRET_KEY)) - { - free_packet (pkt, &parsectx); - init_packet(pkt); - continue; - } - in_v3key = 0; + if (skip_sigs) + { + if (pkt->pkttype == PKT_SIGNATURE) + { + free_packet (pkt, &parsectx); + init_packet (pkt); + continue; + } + skip_sigs = 0; + } - if (!root && pkt->pkttype == PKT_SIGNATURE - && IS_KEY_REV (pkt->pkt.signature) ) - { - /* This is a revocation certificate which is handled in a - * special way. */ - root = new_kbnode( pkt ); - pkt = NULL; - goto ready; - } + if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_SECRET_KEY)) + { + free_packet (pkt, &parsectx); + init_packet(pkt); + continue; + } + in_v3key = 0; - /* Make a linked list of all packets. */ - switch (pkt->pkttype) - { - case PKT_COMPRESSED: - if (check_compress_algo (pkt->pkt.compressed->algorithm)) - { - rc = GPG_ERR_COMPR_ALGO; - goto ready; - } - else - { - compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); - pkt->pkt.compressed->buf = NULL; - if (push_compress_filter2 (a, cfx, - pkt->pkt.compressed->algorithm, 1)) - xfree (cfx); /* e.g. in case of compression_algo NONE. */ - } - free_packet (pkt, &parsectx); - init_packet(pkt); - break; + if (!root && pkt->pkttype == PKT_SIGNATURE + && IS_KEY_REV (pkt->pkt.signature) ) + { + /* This is a revocation certificate which is handled in a + * special way. */ + root = new_kbnode( pkt ); + pkt = NULL; + goto ready; + } - case PKT_RING_TRUST: - /* Skip those packets unless we are in restore mode. */ - if ((opt.import_options & IMPORT_RESTORE)) - goto x_default; - free_packet (pkt, &parsectx); - init_packet(pkt); - break; + /* Make a linked list of all packets. */ + switch (pkt->pkttype) + { + case PKT_COMPRESSED: + if (check_compress_algo (pkt->pkt.compressed->algorithm)) + { + rc = GPG_ERR_COMPR_ALGO; + goto ready; + } + else + { + compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); + pkt->pkt.compressed->buf = NULL; + if (push_compress_filter2 (a, cfx, + pkt->pkt.compressed->algorithm, 1)) + xfree (cfx); /* e.g. in case of compression_algo NONE. */ + } + free_packet (pkt, &parsectx); + init_packet(pkt); + break; - case PKT_PUBLIC_KEY: - case PKT_SECRET_KEY: - if (in_cert ) /* Store this packet. */ - { - *pending_pkt = pkt; - pkt = NULL; - goto ready; - } - in_cert = 1; /* fall through */ - default: - x_default: - if (in_cert && valid_keyblock_packet (pkt->pkttype)) - { - if (!root ) - root = new_kbnode (pkt); - else - add_kbnode (root, new_kbnode (pkt)); - pkt = xmalloc (sizeof *pkt); - } - init_packet(pkt); - break; - } + case PKT_RING_TRUST: + /* Skip those packets unless we are in restore mode. */ + if ((opt.import_options & IMPORT_RESTORE)) + goto x_default; + free_packet (pkt, &parsectx); + init_packet(pkt); + break; + + case PKT_PUBLIC_KEY: + case PKT_SECRET_KEY: + if (in_cert ) /* Store this packet. */ + { + *pending_pkt = pkt; + pkt = NULL; + goto ready; + } + in_cert = 1; + /* fall through */ + default: + x_default: + if (in_cert && valid_keyblock_packet (pkt->pkttype)) + { + if (!root ) + root = new_kbnode (pkt); + else + add_kbnode (root, new_kbnode (pkt)); + pkt = xmalloc (sizeof *pkt); + } + init_packet(pkt); + break; + } } ready: @@ -1312,6 +1345,16 @@ impex_filter_getval (void *cookie, const char *propname) { result = pk_is_disabled (pk)? "1":"0"; } + else if (!strcmp (propname, "usage")) + { + snprintf (numbuf, sizeof numbuf, "%s%s%s%s%s", + (pk->pubkey_usage & PUBKEY_USAGE_ENC)?"e":"", + (pk->pubkey_usage & PUBKEY_USAGE_SIG)?"s":"", + (pk->pubkey_usage & PUBKEY_USAGE_CERT)?"c":"", + (pk->pubkey_usage & PUBKEY_USAGE_AUTH)?"a":"", + (pk->pubkey_usage & PUBKEY_USAGE_UNKNOWN)?"?":""); + result = numbuf; + } else result = NULL; } @@ -1653,6 +1696,10 @@ import_one (ctrl_t ctrl, int any_filter = 0; KEYDB_HANDLE hd = NULL; + /* If show-only is active we don't won't any extra output. */ + if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN))) + silent = 1; + /* Get the key and print some info about it. */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if (!node ) @@ -1712,9 +1759,14 @@ import_one (ctrl_t ctrl, that we have to clean later. This has no practical impact on the end result, but does result in less logging which might confuse the user. */ - if (options&IMPORT_CLEAN) - clean_key (ctrl, keyblock, - opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); + if ((options & IMPORT_CLEAN)) + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, + opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); + } clear_kbnode_flags( keyblock ); @@ -1855,8 +1907,13 @@ import_one (ctrl_t ctrl, log_info (_("writing to '%s'\n"), keydb_get_resource_name (hd) ); if ((options & IMPORT_CLEAN)) - clean_key (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), - &n_uids_cleaned,&n_sigs_cleaned); + { + merge_keys_and_selfsig (ctrl, keyblock); + clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), + &n_uids_cleaned,&n_sigs_cleaned); + clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); + } /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet @@ -1941,8 +1998,14 @@ import_one (ctrl_t ctrl, goto leave; if ((options & IMPORT_CLEAN)) - clean_key (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), - &n_uids_cleaned,&n_sigs_cleaned); + { + merge_keys_and_selfsig (ctrl, keyblock_orig); + clean_all_uids (ctrl, keyblock_orig, opt.verbose, + (options&IMPORT_MINIMAL), + &n_uids_cleaned,&n_sigs_cleaned); + clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, KEY_CLEAN_NONE, + NULL, NULL); + } if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { @@ -2616,11 +2679,216 @@ import_secret_one (ctrl_t ctrl, kbnode_t keyblock, } + +/* Return the recocation reason from signature SIG. If no revocation + * reason is availabale 0 is returned, in other cases the reason + * (0..255). If R_REASON is not NULL a malloced textual + * representation of the code is stored there. If R_COMMENT is not + * NULL the comment from the reason is stored there and its length at + * R_COMMENTLEN. Note that the value at R_COMMENT is not filtered but + * user supplied data in UTF8; thus it needs to be escaped for display + * purposes. Both return values are either NULL or a malloced + * string/buffer. */ +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + int reason_seq = 0; + size_t reason_n; + const byte *reason_p; + char reason_code_buf[20]; + const char *reason_text = NULL; + int reason_code = 0; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + + /* Skip over empty reason packets. */ + while ((reason_p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON, + &reason_n, &reason_seq, NULL)) + && !reason_n) + ; + if (reason_p) + { + reason_code = *reason_p; + reason_n--; reason_p++; + switch (reason_code) + { + case 0x00: reason_text = _("No reason specified"); break; + case 0x01: reason_text = _("Key is superseded"); break; + case 0x02: reason_text = _("Key has been compromised"); break; + case 0x03: reason_text = _("Key is no longer used"); break; + case 0x20: reason_text = _("User ID is no longer valid"); break; + default: + snprintf (reason_code_buf, sizeof reason_code_buf, + "code=%02x", reason_code); + reason_text = reason_code_buf; + break; + } + + if (r_reason) + *r_reason = xstrdup (reason_text); + + if (r_comment && reason_n) + { + *r_comment = xmalloc (reason_n); + memcpy (*r_comment, reason_p, reason_n); + *r_commentlen = reason_n; + } + } + + return reason_code; +} + + +/* List the recocation signature as a "rvs" record. SIGRC shows the + * character from the signature verification or 0 if no public key was + * found. */ +static void +list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) +{ + char *siguid = NULL; + size_t siguidlen = 0; + char *issuer_fpr = NULL; + int reason_code = 0; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; + + if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) + { + int nouid; + siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid); + if (nouid) + sigrc = '?'; + } + + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, &reason_commentlen); + + if (opt.with_colons) + { + es_fputs ("rvs:", es_stdout); + if (sigrc) + es_putc (sigrc, es_stdout); + es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:::", + sig->pubkey_algo, + (ulong) sig->keyid[0], (ulong) sig->keyid[1], + colon_datestr_from_sig (sig), + colon_expirestr_from_sig (sig)); + + if (siguid) + es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); + + es_fprintf (es_stdout, ":%02x%c", sig->sig_class, + sig->flags.exportable ? 'x' : 'l'); + if (reason_text) + es_fprintf (es_stdout, ",%02x", reason_code); + es_fputs ("::", es_stdout); + + if ((issuer_fpr = issuer_fpr_string (sig))) + es_fputs (issuer_fpr, es_stdout); + + es_fprintf (es_stdout, ":::%d:", sig->digest_algo); + + if (reason_comment) + { + es_fputs ("::::", es_stdout); + es_write_sanitized (es_stdout, reason_comment, reason_commentlen, + ":", NULL); + es_putc (':', es_stdout); + } + es_putc ('\n', es_stdout); + + if (opt.show_subpackets) + print_subpackets_colon (sig); + } + else /* Human readable. */ + { + es_fputs ("rvs", es_stdout); + es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", + sigrc, (sig->sig_class - 0x10 > 0 && + sig->sig_class - 0x10 < + 4) ? '0' + sig->sig_class - 0x10 : ' ', + sig->flags.exportable ? ' ' : 'L', + sig->flags.revocable ? ' ' : 'R', + sig->flags.policy_url ? 'P' : ' ', + sig->flags.notation ? 'N' : ' ', + sig->flags.expired ? 'X' : ' ', + (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > + 0) ? '0' + + sig->trust_depth : ' ', keystr (sig->keyid), + datestr_from_sig (sig)); + if (siguid) + { + es_fprintf (es_stdout, " "); + print_utf8_buffer (es_stdout, siguid, siguidlen); + } + es_putc ('\n', es_stdout); + + if (sig->flags.policy_url + && (opt.list_options & LIST_SHOW_POLICY_URLS)) + show_policy_url (sig, 3, 0); + + if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) + show_notation (sig, 3, 0, + ((opt.list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + + + ((opt.list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); + + if (sig->flags.pref_ks + && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) + show_keyserver_url (sig, 3, 0); + + if (reason_text) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; + + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do + { + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) + { + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; + } + } while (s_lf); + } + } + } + + es_fflush (es_stdout); + + xfree (reason_text); + xfree (reason_comment); + xfree (siguid); + xfree (issuer_fpr); +} + + /**************** * Import a revocation certificate; this is a single signature packet. */ static int -import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) +import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, + struct import_stats_s *stats) { PKT_public_key *pk = NULL; kbnode_t onode; @@ -2628,6 +2896,11 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; + int sigrc = 0; + int silent; + + /* No error output for --show-keys. */ + silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN)); log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); @@ -2640,15 +2913,16 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) rc = get_pubkey (ctrl, pk, keyid ); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { - log_error(_("key %s: no public key -" - " can't apply revocation certificate\n"), keystr(keyid)); + if (!silent) + log_error (_("key %s: no public key -" + " can't apply revocation certificate\n"), keystr(keyid)); rc = 0; goto leave; } else if (rc ) { - log_error(_("key %s: public key not found: %s\n"), - keystr(keyid), gpg_strerror (rc)); + log_error (_("key %s: public key not found: %s\n"), + keystr(keyid), gpg_strerror (rc)); goto leave; } @@ -2685,12 +2959,21 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 (KEY_REV) in - * this special case. */ + * this special case. SIGRC is only used for IMPORT_SHOW. */ rc = check_key_signature (ctrl, keyblock, node, NULL); + switch (gpg_err_code (rc)) + { + case 0: sigrc = '!'; break; + case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; + case GPG_ERR_NO_PUBKEY: sigrc = '?'; break; + case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; + default: sigrc = '%'; break; + } if (rc ) { - log_error( _("key %s: invalid revocation certificate" - ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); + if (!silent) + log_error (_("key %s: invalid revocation certificate" + ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } @@ -2710,33 +2993,39 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) /* insert it */ insert_kbnode( keyblock, clone_kbnode(node), 0 ); - /* and write the keyblock back */ - rc = keydb_update_keyblock (ctrl, hd, keyblock ); - if (rc) - log_error (_("error writing keyring '%s': %s\n"), - keydb_get_resource_name (hd), gpg_strerror (rc) ); - keydb_release (hd); - hd = NULL; - - /* we are ready */ - if (!opt.quiet ) + /* and write the keyblock back unless in dry run mode. */ + if (!(opt.dry_run || (options & IMPORT_DRY_RUN))) { - char *p=get_user_id_native (ctrl, keyid); - log_info( _("key %s: \"%s\" revocation certificate imported\n"), - keystr(keyid),p); - xfree(p); + rc = keydb_update_keyblock (ctrl, hd, keyblock ); + if (rc) + log_error (_("error writing keyring '%s': %s\n"), + keydb_get_resource_name (hd), gpg_strerror (rc) ); + keydb_release (hd); + hd = NULL; + + /* we are ready */ + if (!opt.quiet ) + { + char *p=get_user_id_native (ctrl, keyid); + log_info( _("key %s: \"%s\" revocation certificate imported\n"), + keystr(keyid),p); + xfree(p); + } + + /* If the key we just revoked was ultimately trusted, remove its + * ultimate trust. This doesn't stop the user from putting the + * ultimate trust back, but is a reasonable solution for now. */ + if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE) + clear_ownertrusts (ctrl, pk); + + revalidation_mark (ctrl); } stats->n_revoc++; - /* If the key we just revoked was ultimately trusted, remove its - ultimate trust. This doesn't stop the user from putting the - ultimate trust back, but is a reasonable solution for now. */ - if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE) - clear_ownertrusts (ctrl, pk); - - revalidation_mark (ctrl); - leave: + if ((options & IMPORT_SHOW)) + list_standalone_revocation (ctrl, node->pkt->pkt.signature, sigrc); + keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); @@ -2744,8 +3033,9 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) } -/* Loop over the keyblock and check all self signatures. On return - * the following bis in the node flags are set: +/* Loop over the KEYBLOCK and check all self signatures. KEYID is the + * keyid of the primary key for reporting purposes. On return the + * following bits in the node flags are set: * * - NODE_GOOD_SELFSIG :: User ID or subkey has a self-signature * - NODE_BAD_SELFSIG :: Used ID or subkey has an invalid self-signature @@ -2760,17 +3050,22 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, struct import_stats_s *stats) static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) { - kbnode_t n, knode = NULL; + kbnode_t knode = NULL; /* The node of the current subkey. */ + PKT_public_key *subpk = NULL; /* and its packet. */ + kbnode_t bsnode = NULL; /* Subkey binding signature node. */ + u32 bsdate = 0; /* Timestamp of that node. */ + kbnode_t rsnode = NULL; /* Subkey recocation signature node. */ + u32 rsdate = 0; /* Timestamp of tha node. */ PKT_signature *sig; int rc; - u32 bsdate=0, rsdate=0; - kbnode_t bsnode = NULL, rsnode = NULL; + kbnode_t n; for (n=keyblock; (n = find_next_kbnode (n, 0)); ) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) { knode = n; + subpk = knode->pkt->pkt.public_key; bsdate = 0; rsdate = 0; bsnode = NULL; @@ -2859,11 +3154,14 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) if ( rc ) { if (opt.verbose) - log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? - _("key %s: unsupported public key" - " algorithm\n"): - _("key %s: invalid subkey binding\n"), - keystr (keyid)); + { + keyid_from_pk (subpk, NULL); + log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? + _("key %s: unsupported public key" + " algorithm\n"): + _("key %s: invalid subkey binding\n"), + keystr_with_sub (keyid, subpk->keyid)); + } n->flag |= NODE_DELETION_MARK; } else @@ -2878,8 +3176,12 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) one is newer */ bsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) - log_info (_("key %s: removed multiple subkey" - " binding\n"),keystr(keyid)); + { + keyid_from_pk (subpk, NULL); + log_info (_("key %s: removed multiple subkey" + " binding\n"), + keystr_with_sub (keyid, subpk->keyid)); + } } bsnode = n; @@ -2958,6 +3260,7 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; + PKT_public_key *pk; for (node=keyblock->next; node; node = node->next ) { @@ -2995,7 +3298,12 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) - log_info( _("key %s: skipped subkey\n"),keystr(keyid)); + { + pk = node->pkt->pkt.public_key; + keyid_from_pk (pk, NULL); + log_info (_("key %s: skipped subkey\n"), + keystr_with_sub (keyid, pk->keyid)); + } delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ diff --git a/g10/key-check.c b/g10/key-check.c index 86b1e769d..c17b12c94 100644 --- a/g10/key-check.c +++ b/g10/key-check.c @@ -1,7 +1,7 @@ /* key-check.c - Detect and fix various problems with keys * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch - * Copyright (C) 2015-2017 g10 Code GmbH + * Copyright (C) 2015-2018 g10 Code GmbH * * This file is part of GnuPG. * @@ -72,6 +72,13 @@ sig_comparison (const void *av, const void *bv) a = an->pkt->pkt.signature; b = bn->pkt->pkt.signature; + /* Signatures with a different help counter are not identical for + * our purpose. */ + if (a->help_counter < b->help_counter) + return -1; + if (a->help_counter > b->help_counter) + return 1; + if (a->digest_algo < b->digest_algo) return -1; if (a->digest_algo > b->digest_algo) @@ -94,6 +101,125 @@ sig_comparison (const void *av, const void *bv) } +static gpg_error_t +remove_duplicate_sigs (kbnode_t kb, int *dups, int *modified) +{ + gpg_error_t err; + kbnode_t n; + int nsigs; + kbnode_t *sigs; /* Allocated array with the signature packet. */ + int i; + int last_i; + int block; + PKT_signature *sig; + + /* Count the sigs. */ + for (nsigs = 0, n = kb; n; n = n->next) + { + if (is_deleted_kbnode (n)) + continue; + else if (n->pkt->pkttype == PKT_SIGNATURE) + nsigs ++; + } + + if (!nsigs) + return 0; /* No signatures at all. */ + + /* Add them all to the SIGS array. */ + sigs = xtrycalloc (nsigs, sizeof *sigs); + if (!sigs) + { + err = gpg_error_from_syserror (); + log_error (_("error allocating memory: %s\n"), gpg_strerror (err)); + return err; + } + + block = 0; + i = 0; + for (n = kb; n; n = n->next) + { + if (is_deleted_kbnode (n)) + continue; + + if (n->pkt->pkttype != PKT_SIGNATURE) + { + switch (n->pkt->pkttype) + { + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_SUBKEY: + case PKT_USER_ID: + case PKT_ATTRIBUTE: + /* Bump the block number so that we only consider + * signatures below the same object as duplicates. */ + block++; + break; + default: + break; + } + continue; + } + sig = n->pkt->pkt.signature; + sig->help_counter = block; + sigs[i++] = n; + } + log_assert (i == nsigs); + + qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); + + last_i = 0; + for (i = 1; i < nsigs; i ++) + { + log_assert (sigs[last_i]); + log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE); + log_assert (sigs[i]); + log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE); + + if (sig_comparison (&sigs[last_i], &sigs[i]) == 0) + { + /* They are the same. Kill the latter. */ + if (DBG_PACKET) + { + sig = sigs[i]->pkt->pkt.signature; + + log_debug ("Signature appears multiple times, " + "deleting duplicate:\n"); + log_debug (" sig: class 0x%x, issuer: %s," + " timestamp: %s (%lld), digest: %02x %02x\n", + sig->sig_class, keystr (sig->keyid), + isotimestamp (sig->timestamp), + (long long) sig->timestamp, + sig->digest_start[0], sig->digest_start[1]); + } + + /* Remove sigs[i] from the keyblock. */ + { + kbnode_t z, *prevp; + int to_kill = last_i; + last_i = i; + + for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next) + if (z == sigs[to_kill]) + break; + + *prevp = sigs[to_kill]->next; + + sigs[to_kill]->next = NULL; + release_kbnode (sigs[to_kill]); + sigs[to_kill] = NULL; + + ++*dups; + *modified = 1; + } + } + else + last_i = i; + } + + xfree (sigs); + return 0; +} + + /* Perform a few sanity checks on a keyblock is okay and possibly * repair some damage. Concretely: * @@ -133,108 +259,17 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, int bad_signature = 0; int missing_selfsig = 0; int modified = 0; + PKT_signature *sig; log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); pk = kb->pkt->pkt.public_key; /* First we look for duplicates. */ - { - int nsigs; - kbnode_t *sigs; - int i; - int last_i; + if (remove_duplicate_sigs (kb, &dups, &modified)) + goto leave; /* Error */ - /* Count the sigs. */ - for (nsigs = 0, n = kb; n; n = n->next) - { - if (is_deleted_kbnode (n)) - continue; - else if (n->pkt->pkttype == PKT_SIGNATURE) - nsigs ++; - } - - if (!nsigs) - return 0; /* No signatures at all. */ - - /* Add them all to the SIGS array. */ - sigs = xtrycalloc (nsigs, sizeof *sigs); - if (!sigs) - { - log_error (_("error allocating memory: %s\n"), - gpg_strerror (gpg_error_from_syserror ())); - return 0; - } - - i = 0; - for (n = kb; n; n = n->next) - { - if (is_deleted_kbnode (n)) - continue; - - if (n->pkt->pkttype != PKT_SIGNATURE) - continue; - - sigs[i] = n; - i ++; - } - log_assert (i == nsigs); - - qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison); - - last_i = 0; - for (i = 1; i < nsigs; i ++) - { - log_assert (sigs[last_i]); - log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE); - log_assert (sigs[i]); - log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE); - - if (sig_comparison (&sigs[last_i], &sigs[i]) == 0) - /* They are the same. Kill the latter. */ - { - if (DBG_PACKET) - { - PKT_signature *sig = sigs[i]->pkt->pkt.signature; - - log_debug ("Signature appears multiple times, " - "deleting duplicate:\n"); - log_debug (" sig: class 0x%x, issuer: %s," - " timestamp: %s (%lld), digest: %02x %02x\n", - sig->sig_class, keystr (sig->keyid), - isotimestamp (sig->timestamp), - (long long) sig->timestamp, - sig->digest_start[0], sig->digest_start[1]); - } - - /* Remove sigs[i] from the keyblock. */ - { - KBNODE z, *prevp; - int to_kill = last_i; - last_i = i; - - for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next) - if (z == sigs[to_kill]) - break; - - *prevp = sigs[to_kill]->next; - - sigs[to_kill]->next = NULL; - release_kbnode (sigs[to_kill]); - sigs[to_kill] = NULL; - - dups ++; - modified = 1; - } - } - else - last_i = i; - } - - xfree (sigs); - } - - /* Make sure the sigs occur after the component (public key, subkey, - user id) that they sign. */ + /* Now make sure the sigs occur after the component (aka block) + * (public key, subkey, user id) that they sign. */ issuer = NULL; last_printed_component = NULL; for (n_prevp = &kb, n = kb; @@ -244,7 +279,6 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, { PACKET *p; int processed_current_component; - PKT_signature *sig; int rc; int dump_sig_params = 0; @@ -573,11 +607,18 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, free_public_key (issuer); issuer = NULL; + /* If we reordered signatures we need to de-duplicate again because + * a signature can now be a duplicate in another block. */ + if (reordered) + { + if (remove_duplicate_sigs (kb, &dups, &modified)) + goto leave; + } + /* Identify keys / uids that don't have a self-sig. */ { int has_selfsig = 0; PACKET *p; - PKT_signature *sig; current_component = NULL; for (n = kb; n; n = n->next) @@ -643,6 +684,8 @@ key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, } } + + leave: if (!opt.quiet) { char prefix[100]; diff --git a/g10/key-clean.c b/g10/key-clean.c new file mode 100644 index 000000000..f66a0dbb4 --- /dev/null +++ b/g10/key-clean.c @@ -0,0 +1,614 @@ +/* key-clean.c - Functions to clean a keyblock + * Copyright (C) 1998-2008, 2010-2011 Free Software Foundation, Inc. + * Copyright (C) 2014, 2016-2018 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-or-later + */ + +#include +#include +#include +#include + +#include "gpg.h" +#include "keydb.h" +#include "../common/util.h" +#include "../common/host2net.h" +#include "../common/i18n.h" +#include "options.h" +#include "packet.h" +#include "main.h" +#include "key-clean.h" + + +/* + * Mark the signature of the given UID which are used to certify it. + * To do this, we first revmove all signatures which are not valid and + * from the remain ones we look for the latest one. If this is not a + * certification revocation signature we mark the signature by setting + * node flag bit 8. Revocations are marked with flag 11, and sigs + * from unavailable keys are marked with flag 12. Note that flag bits + * 9 and 10 are used for internal purposes. + */ +void +mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + u32 *main_kid, struct key_item *klist, + u32 curtime, u32 *next_expire) +{ + kbnode_t node; + PKT_signature *sig; + + /* First check all signatures. */ + for (node=uidnode->next; node; node = node->next) + { + int rc; + + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + if (node->pkt->pkttype == PKT_USER_ID + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + break; /* ready */ + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + if (main_kid + && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) + continue; /* ignore self-signatures if we pass in a main_kid */ + if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) + continue; /* we only look at these signature classes */ + if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && + sig->sig_class-0x10flag |= 1<<12; + continue; + } + node->flag |= 1<<9; + } + /* Reset the remaining flags. */ + for (; node; node = node->next) + node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); + + /* kbnode flag usage: bit 9 is here set for signatures to consider, + * bit 10 will be set by the loop to keep track of keyIDs already + * processed, bit 8 will be set for the usable signatures, and bit + * 11 will be set for usable revocations. */ + + /* For each cert figure out the latest valid one. */ + for (node=uidnode->next; node; node = node->next) + { + KBNODE n, signode; + u32 kid[2]; + u32 sigdate; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + break; + if ( !(node->flag & (1<<9)) ) + continue; /* not a node to look at */ + if ( (node->flag & (1<<10)) ) + continue; /* signature with a keyID already processed */ + node->flag |= (1<<10); /* mark this node as processed */ + sig = node->pkt->pkt.signature; + signode = node; + sigdate = sig->timestamp; + kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; + + /* Now find the latest and greatest signature */ + for (n=uidnode->next; n; n = n->next) + { + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY + || n->pkt->pkttype == PKT_SECRET_SUBKEY) + break; + if ( !(n->flag & (1<<9)) ) + continue; + if ( (n->flag & (1<<10)) ) + continue; /* shortcut already processed signatures */ + sig = n->pkt->pkt.signature; + if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) + continue; + n->flag |= (1<<10); /* mark this node as processed */ + + /* If signode is nonrevocable and unexpired and n isn't, + then take signode (skip). It doesn't matter which is + older: if signode was older then we don't want to take n + as signode is nonrevocable. If n was older then we're + automatically fine. */ + + if(((IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + (!(IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + continue; + + /* If n is nonrevocable and unexpired and signode isn't, + then take n. Again, it doesn't matter which is older: if + n was older then we don't want to take signode as n is + nonrevocable. If signode was older then we're + automatically fine. */ + + if((!(IS_UID_SIG(signode->pkt->pkt.signature) && + !signode->pkt->pkt.signature->flags.revocable && + (signode->pkt->pkt.signature->expiredate==0 || + signode->pkt->pkt.signature->expiredate>curtime))) && + ((IS_UID_SIG(n->pkt->pkt.signature) && + !n->pkt->pkt.signature->flags.revocable && + (n->pkt->pkt.signature->expiredate==0 || + n->pkt->pkt.signature->expiredate>curtime)))) + { + signode = n; + sigdate = sig->timestamp; + continue; + } + + /* At this point, if it's newer, it goes in as the only + remaining possibilities are signode and n are both either + revocable or expired or both nonrevocable and unexpired. + If the timestamps are equal take the later ordered + packet, presuming that the key packets are hopefully in + their original order. */ + + if (sig->timestamp >= sigdate) + { + signode = n; + sigdate = sig->timestamp; + } + } + + sig = signode->pkt->pkt.signature; + if (IS_UID_SIG (sig)) + { /* this seems to be a usable one which is not revoked. + * Just need to check whether there is an expiration time, + * We do the expired certification after finding a suitable + * certification, the assumption is that a signator does not + * want that after the expiration of his certificate the + * system falls back to an older certification which has a + * different expiration time */ + const byte *p; + u32 expire; + + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); + expire = p? sig->timestamp + buf32_to_u32(p) : 0; + + if (expire==0 || expire > curtime ) + { + signode->flag |= (1<<8); /* yeah, found a good cert */ + if (next_expire && expire && expire < *next_expire) + *next_expire = expire; + } + } + else + signode->flag |= (1<<11); + } +} + + +static int +clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only) +{ + int deleted = 0; + kbnode_t node; + u32 keyid[2]; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + + keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); + + /* Passing in a 0 for current time here means that we'll never weed + out an expired sig. This is correct behavior since we want to + keep the most recent expired sig in a series. */ + mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL); + + /* What we want to do here is remove signatures that are not + considered as part of the trust calculations. Thus, all invalid + signatures are out, as are any signatures that aren't the last of + a series of uid sigs or revocations It breaks down like this: + coming out of mark_usable_uid_certs, if a sig is unflagged, it is + not even a candidate. If a sig has flag 9 or 10, that means it + was selected as a candidate and vetted. If a sig has flag 8 it + is a usable signature. If a sig has flag 11 it is a usable + revocation. If a sig has flag 12 it was issued by an unavailable + key. "Usable" here means the most recent valid + signature/revocation in a series from a particular signer. + + Delete everything that isn't a usable uid sig (which might be + expired), a usable revocation, or a sig from an unavailable + key. */ + + for (node=uidnode->next; + node && node->pkt->pkttype==PKT_SIGNATURE; + node=node->next) + { + int keep; + + keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] + && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; + + /* Keep usable uid sigs ... */ + if ((node->flag & (1<<8)) && keep) + continue; + + /* ... and usable revocations... */ + if ((node->flag & (1<<11)) && keep) + continue; + + /* ... and sigs from unavailable keys. */ + /* disabled for now since more people seem to want sigs from + unavailable keys removed altogether. */ + /* + if(node->flag & (1<<12)) + continue; + */ + + /* Everything else we delete */ + + /* At this point, if 12 is set, the signing key was unavailable. + If 9 or 10 is set, it's superseded. Otherwise, it's + invalid. */ + + if (noisy) + log_info ("removing signature from key %s on user ID \"%s\": %s\n", + keystr (node->pkt->pkt.signature->keyid), + uidnode->pkt->pkt.user_id->name, + node->flag&(1<<12)? "key unavailable": + node->flag&(1<<9)? "signature superseded" + /* */ :"invalid signature" ); + + delete_kbnode (node); + deleted++; + } + + return deleted; +} + + +/* This is substantially easier than clean_sigs_from_uid since we just + have to establish if the uid has a valid self-sig, is not revoked, + and is not expired. Note that this does not take into account + whether the uid has a trust path to it - just whether the keyholder + themselves has certified the uid. Returns true if the uid was + compacted. To "compact" a user ID, we simply remove ALL signatures + except the self-sig that caused the user ID to be remove-worthy. + We don't actually remove the user ID packet itself since it might + be resurrected in a later merge. Note that this function requires + that the caller has already done a merge_keys_and_selfsig(). + + TODO: change the import code to allow importing a uid with only a + revocation if the uid already exists on the keyring. */ + +static int +clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) +{ + kbnode_t node; + PKT_user_id *uid = uidnode->pkt->pkt.user_id; + int deleted = 0; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + log_assert (uidnode->pkt->pkttype==PKT_USER_ID); + + /* Skip valid user IDs, compacted user IDs, and non-self-signed user + IDs if --allow-non-selfsigned-uid is set. */ + if (uid->created + || uid->flags.compacted + || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid)) + return 0; + + for (node=uidnode->next; + node && node->pkt->pkttype == PKT_SIGNATURE; + node=node->next) + { + if (!node->pkt->pkt.signature->flags.chosen_selfsig) + { + delete_kbnode (node); + deleted = 1; + uidnode->pkt->pkt.user_id->flags.compacted = 1; + } + } + + if (noisy) + { + const char *reason; + char *user = utf8_to_native (uid->name, uid->len, 0); + + if (uid->flags.revoked) + reason = _("revoked"); + else if (uid->flags.expired) + reason = _("expired"); + else + reason = _("invalid"); + + log_info ("compacting user ID \"%s\" on key %s: %s\n", + user, keystr_from_pk (keyblock->pkt->pkt.public_key), + reason); + + xfree (user); + } + + return deleted; +} + + +/* Needs to be called after a merge_keys_and_selfsig() */ +void +clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) +{ + int dummy = 0; + + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + log_assert (uidnode->pkt->pkttype==PKT_USER_ID); + + if (!uids_cleaned) + uids_cleaned = &dummy; + + if (!sigs_cleaned) + sigs_cleaned = &dummy; + + /* Do clean_uid_from_key first since if it fires off, we don't have + to bother with the other. */ + *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); + if (!uidnode->pkt->pkt.user_id->flags.compacted) + *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode, + noisy, self_only); +} + + +/* NB: This function marks the deleted nodes only and the caller is + * responsible to skip or remove them. Needs to be called after a + * merge_keys_and_selfsig(). */ +void +clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned, int *sigs_cleaned) +{ + kbnode_t node; + + for (node = keyblock->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (node->pkt->pkttype == PKT_USER_ID) + clean_one_uid (ctrl, keyblock, node, noisy, self_only, + uids_cleaned, sigs_cleaned); + } + + /* Remove bogus subkey binding signatures: The only signatures + * allowed are of class 0x18 and 0x28. */ + log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY)); +} + + +/* Helper for clean_all_subkeys. */ +static int +clean_one_subkey (ctrl_t ctrl, kbnode_t subkeynode, int noisy, int clean_level) +{ + kbnode_t node; + PKT_public_key *pk = subkeynode->pkt->pkt.public_key; + unsigned int use = pk->pubkey_usage; + int do_clean = 0; + + (void)ctrl; + (void)noisy; + + log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY + || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY); + + if (DBG_LOOKUP) + log_debug ("\tchecking subkey %08lX [%c%c%c%c%c]\n", + (ulong) keyid_from_pk (pk, NULL), + (use & PUBKEY_USAGE_ENC)? 'e':'-', + (use & PUBKEY_USAGE_SIG)? 's':'-', + (use & PUBKEY_USAGE_CERT)? 'c':'-', + (use & PUBKEY_USAGE_AUTH)? 'a':'-', + (use & PUBKEY_USAGE_UNKNOWN)? '?':'-'); + + if (!pk->flags.valid) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey not valid\n"); + if (clean_level == KEY_CLEAN_INVALID) + do_clean = 1; + } + if (pk->has_expired) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has expired\n"); + if (clean_level == KEY_CLEAN_ALL) + do_clean = 1; + else if (clean_level == KEY_CLEAN_AUTHENCR + && (use & (PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH)) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT))) + do_clean = 1; + else if (clean_level == KEY_CLEAN_ENCR + && (use & PUBKEY_USAGE_ENC) + && !(use & (PUBKEY_USAGE_SIG | PUBKEY_USAGE_CERT + | PUBKEY_USAGE_AUTH))) + do_clean = 1; + } + if (pk->flags.revoked) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey has been revoked (keeping)\n"); + /* Avoid any cleaning because revocations are important. */ + do_clean = 0; + } + if (!do_clean) + return 0; + + if (DBG_LOOKUP) + log_debug ("\t=> removing this subkey\n"); + + delete_kbnode (subkeynode); + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + delete_kbnode (node); + + return 1; +} + + +/* Helper for clean_all_subkeys. Here duplicate signatures from a + * subkey are removed. This should in general not happen because + * import takes care of that. However, sometimes other tools are used + * to manage a keyring or key has been imported a long time ago. */ +static int +clean_one_subkey_dupsigs (ctrl_t ctrl, kbnode_t subkeynode) +{ + kbnode_t node; + PKT_public_key *pk = subkeynode->pkt->pkt.public_key; + int any_choosen = 0; + int count = 0; + + (void)ctrl; + + log_assert (subkeynode->pkt->pkttype == PKT_PUBLIC_SUBKEY + || subkeynode->pkt->pkttype == PKT_SECRET_SUBKEY); + + if (DBG_LOOKUP) + log_debug ("\tchecking subkey %08lX for dupsigs\n", + (ulong) keyid_from_pk (pk, NULL)); + + /* First check that the choosen flag has been set. Note that we + * only look at plain signatures so to keep all revocation + * signatures which may carry important information. */ + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (!is_deleted_kbnode (node) + && node->pkt->pkttype == PKT_SIGNATURE + && IS_SUBKEY_SIG (node->pkt->pkt.signature) + && node->pkt->pkt.signature->flags.chosen_selfsig) + { + any_choosen = 1; + break; + } + } + + if (!any_choosen) + return 0; /* Ooops no choosen flag set - we can't decide. */ + + for (node = subkeynode->next; + node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY); + node = node->next) + { + if (!is_deleted_kbnode (node) + && node->pkt->pkttype == PKT_SIGNATURE + && IS_SUBKEY_SIG (node->pkt->pkt.signature) + && !node->pkt->pkt.signature->flags.chosen_selfsig) + { + delete_kbnode (node); + count++; + } + } + + return count; +} + + +/* This function only marks the deleted nodes and the caller is + * responsible to skip or remove them. Needs to be called after a + * merge_keys_and_selfsig. CLEAN_LEVEL is one of the KEY_CLEAN_* + * values. */ +void +clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, int noisy, int clean_level, + int *subkeys_cleaned, int *sigs_cleaned) +{ + kbnode_t first_subkey, node; + int n; + + if (DBG_LOOKUP) + log_debug ("clean_all_subkeys: checking key %08lX\n", + (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL)); + + for (node = keyblock->next; node; node = node->next) + if (!is_deleted_kbnode (node) + && (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY)) + break; + first_subkey = node; + + /* Remove bogus subkey binding signatures: The only signatures + * allowed are of class 0x18 and 0x28. */ + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_SIGNATURE + && !(IS_SUBKEY_SIG (node->pkt->pkt.signature) + || IS_SUBKEY_REV (node->pkt->pkt.signature))) + { + delete_kbnode (node); + if (sigs_cleaned) + ++*sigs_cleaned; + } + } + + /* Do the selected cleaning. */ + if (clean_level > KEY_CLEAN_NONE) + { + /* Clean enitre subkeys. */ + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + { + if (clean_one_subkey (ctrl, node, noisy, clean_level)) + { + if (subkeys_cleaned) + ++*subkeys_cleaned; + } + } + } + + /* Clean duplicate signatures from a subkey. */ + for (node = first_subkey; node; node = node->next) + { + if (is_deleted_kbnode (node)) + continue; + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + { + n = clean_one_subkey_dupsigs (ctrl, node); + if (sigs_cleaned) + *sigs_cleaned += n; + } + } + } +} diff --git a/g10/key-clean.h b/g10/key-clean.h new file mode 100644 index 000000000..a0fb76950 --- /dev/null +++ b/g10/key-clean.h @@ -0,0 +1,52 @@ +/* key-clean.h - Functions to clean a keyblock + * Copyright (C) 2018 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-or-later + */ + +#ifndef GNUPG_G10_KEY_CLEAN_H +#define GNUPG_G10_KEY_CLEAN_H + +#include "gpg.h" + +/* No explict cleaning. */ +#define KEY_CLEAN_NONE 0 +/* Remove only invalid subkeys (ie. missing key-bindings) */ +#define KEY_CLEAN_INVALID 1 +/* Remove expired encryption keys */ +#define KEY_CLEAN_ENCR 2 +/* Remove expired authentication and encryption keys. */ +#define KEY_CLEAN_AUTHENCR 3 +/* Remove all expired subkeys. */ +#define KEY_CLEAN_ALL 4 + + +void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + u32 *main_kid, struct key_item *klist, + u32 curtime, u32 *next_expire); + +void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, + int noisy, int self_only, + int *uids_cleaned, int *sigs_cleaned); +void clean_all_uids (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, + int *uids_cleaned,int *sigs_cleaned); +void clean_all_subkeys (ctrl_t ctrl, kbnode_t keyblock, + int noisy, int clean_level, + int *subkeys_cleaned, int *sigs_cleaned); + + +#endif /*GNUPG_G10_KEY_CLEAN_H*/ diff --git a/g10/keydb.h b/g10/keydb.h index bd156a6a3..9748e571e 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -64,6 +64,20 @@ struct kbnode_struct { #define is_cloned_kbnode(a) ((a)->private_flag & 2) +/* + * A structure to store key identification as well as some stuff + * needed for key validation. + */ +struct key_item { + struct key_item *next; + unsigned int ownertrust,min_ownertrust; + byte trust_depth; + byte trust_value; + char *trust_regexp; + u32 kid[2]; +}; + + /* Bit flags used with build_pk_list. */ enum { @@ -133,6 +147,22 @@ enum }; +/* + * Check whether the signature SIG is in the klist K. + */ +static inline struct key_item * +is_in_klist (struct key_item *k, PKT_signature *sig) +{ + for (; k; k = k->next) + { + if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) + return k; + } + return NULL; +} + + + /*-- keydb.c --*/ #define KEYDB_RESOURCE_FLAG_PRIMARY 2 /* The primary resource. */ @@ -283,6 +313,10 @@ void cache_public_key( PKT_public_key *pk ); /* Disable and drop the public key cache. */ void getkey_disable_caches(void); +/* Return the public key used for signature SIG and store it at PK. */ +gpg_error_t get_pubkey_for_sig (ctrl_t ctrl, + PKT_public_key *pk, PKT_signature *sig); + /* Return the public key with the key id KEYID and store it at PK. */ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); @@ -291,6 +325,10 @@ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); also only considers primary keys. */ int get_pubkey_fast (PKT_public_key *pk, u32 *keyid); +/* Return the entire keyblock used to create SIG. This is a + * specialized version of get_pubkeyblock. */ +kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig); + /* Return the key block for the key with KEYID. */ kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 *keyid); diff --git a/g10/keyedit.c b/g10/keyedit.c index 00b4e7280..9716ed9d6 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -49,6 +49,7 @@ #include "../common/host2net.h" #include "tofu.h" #include "key-check.h" +#include "key-clean.h" #include "keyedit.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, diff --git a/g10/keygen.c b/g10/keygen.c index 9e9cead07..fbead107b 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -3293,7 +3293,7 @@ parse_key_parameter_string (const char *string, int part, * part consider this to be the subkey algo. In case a * SUGGESTED_USE has been given and the usage of the secondary * part does not match SUGGESTED_USE try again using the primary - * part. Noet thar when falling back to the primary key we need + * part. Note that when falling back to the primary key we need * to force clearing the cert usage. */ if (secondary) { diff --git a/g10/keylist.c b/g10/keylist.c index 1f501fc97..39b87e49f 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1107,6 +1107,9 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; if (listctx->check_sigs) { @@ -1143,7 +1146,11 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) - sigstr = "rev"; + { + sigstr = "rev"; + get_revocation_reason (sig, &reason_text, + &reason_comment, &reason_commentlen); + } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) @@ -1205,6 +1212,40 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); + if (reason_text) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; + + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do + { + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) + { + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; + } + } while (s_lf); + } + } + + xfree (reason_text); + xfree (reason_comment); + /* fixme: check or list other sigs here */ } } @@ -1554,10 +1595,19 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, char *siguid; size_t siguidlen; char *issuer_fpr = NULL; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; + int reason_code; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) - sigstr = "rev"; + { + sigstr = "rev"; + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, + &reason_commentlen); + } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) @@ -1651,8 +1701,11 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, else if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); - es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, + es_fprintf (es_stdout, ":%02x%c", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); + if (reason_text) + es_fprintf (es_stdout, ",%02x", reason_code); + es_fputs ("::", es_stdout); if (opt.no_sig_cache && opt.check_sigs && fprokay) { @@ -1662,12 +1715,23 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, else if ((issuer_fpr = issuer_fpr_string (sig))) es_fputs (issuer_fpr, es_stdout); - es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); + es_fprintf (es_stdout, ":::%d:", sig->digest_algo); + + if (reason_comment) + { + es_fputs ("::::", es_stdout); + es_write_sanitized (es_stdout, reason_comment, reason_commentlen, + ":", NULL); + es_putc (':', es_stdout); + } + es_putc ('\n', es_stdout); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ + xfree (reason_text); + xfree (reason_comment); xfree (siguid); xfree (issuer_fpr); } diff --git a/g10/main.h b/g10/main.h index 453d1226a..768f7cfb8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -396,6 +396,9 @@ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, int collapse_uids( KBNODE *keyblock ); +int get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen); + /*-- export.c --*/ struct export_stats_s; diff --git a/g10/mainproc.c b/g10/mainproc.c index 835ab6b72..dcf7dee01 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -533,6 +533,14 @@ static void proc_encrypted (CTX c, PACKET *pkt) { int result = 0; + int early_plaintext = literals_seen; + + if (early_plaintext) + { + log_info (_("WARNING: multiple plaintexts seen\n")); + write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA); + /* We fail only later so that we can print some more info first. */ + } if (!opt.quiet) { @@ -683,6 +691,10 @@ proc_encrypted (CTX c, PACKET *pkt) if (!result) result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek ); + /* Trigger the deferred error. */ + if (!result && early_plaintext) + result = gpg_error (GPG_ERR_BAD_DATA); + if (result == -1) ; else if (!result @@ -788,7 +800,14 @@ proc_plaintext( CTX c, PACKET *pkt ) if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) - log_info (_("original file name='%.*s'\n"), pt->namelen, pt->name); + { + /* We don't use print_utf8_buffer because that would require a + * string change which we don't want in 2.2. It is also not + * clear whether the filename is always utf-8 encoded. */ + char *tmp = make_printable_string (pt->name, pt->namelen, 0); + log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp); + xfree (tmp); + } free_md_filter_context (&c->mfx); if (gcry_md_open (&c->mfx.md, 0, 0)) @@ -1681,7 +1700,7 @@ akl_has_wkd_method (void) /* Return the ISSUER fingerprint buffer and its lenbgth at R_LEN. * Returns NULL if not available. The returned buffer is valid as * long as SIG is not modified. */ -static const byte * +const byte * issuer_fpr_raw (PKT_signature *sig, size_t *r_len) { const byte *p; @@ -1698,7 +1717,7 @@ issuer_fpr_raw (PKT_signature *sig, size_t *r_len) } -/* Return the ISSUER fingerprint string in human readbale format if +/* Return the ISSUER fingerprint string in human readable format if * available. Caller must release the string. */ /* FIXME: Move to another file. */ char * @@ -2064,7 +2083,7 @@ check_sig_and_print (CTX c, kbnode_t node) * keyblock has already been fetched. Thus we could use the * fingerprint or PK itself to lookup the entire keyblock. That * would best be done with a cache. */ - keyblock = get_pubkeyblock (c->ctrl, sig->keyid); + keyblock = get_pubkeyblock_for_sig (c->ctrl, sig); snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ", (ulong)sig->keyid[0], (ulong)sig->keyid[1]); diff --git a/g10/packet.h b/g10/packet.h index a4157c53f..3f872944b 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -244,6 +244,7 @@ typedef struct const byte *trust_regexp; struct revocation_key *revkey; int numrevkeys; + int help_counter; /* Used internally bu some fucntions. */ pka_info_t *pka_info; /* Malloced PKA data or NULL if not available. See also flags.pka_tried. */ char *signers_uid; /* Malloced value of the SIGNERS_UID @@ -630,6 +631,7 @@ int proc_signature_packets_by_fd (ctrl_t ctrl, int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a); int list_packets( iobuf_t a ); +const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len); char *issuer_fpr_string (PKT_signature *sig); /*-- parse-packet.c --*/ diff --git a/g10/pkclist.c b/g10/pkclist.c index 6aec13882..e7484432a 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -113,7 +113,7 @@ void show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode) { /* Hmmm, this is not so easy because we have to duplicate the code - * used in the trustbd to calculate the keyflags. We need to find + * used in the trustdb to calculate the keyflags. We need to find * a clean way to check revocation certificates on keys and * signatures. And there should be no duplicate code. Because we * enter this function only when the trustdb told us that we have @@ -548,7 +548,7 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig) unsigned int trustlevel = TRUST_UNKNOWN; int rc=0; - rc = get_pubkey (ctrl, pk, sig->keyid ); + rc = get_pubkey_for_sig (ctrl, pk, sig); if (rc) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); diff --git a/g10/sig-check.c b/g10/sig-check.c index fc6983993..0ec384347 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -156,7 +156,7 @@ check_signature2 (ctrl_t ctrl, log_info(_("WARNING: signature digest conflict in message\n")); rc = gpg_error (GPG_ERR_GENERAL); } - else if (get_pubkey (ctrl, pk, sig->keyid)) + else if (get_pubkey_for_sig (ctrl, pk, sig)) rc = gpg_error (GPG_ERR_NO_PUBKEY); else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pk->pubkey_algo, pk->pkey, @@ -478,8 +478,17 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, sig->sig_class, pk->pubkey_usage); return rc; } - /* Fixme: Should we also check the signing capability here for data - * signature? */ + + /* For data signatures check that the key has sign usage. */ + if (IS_SIG (sig) && !(pk->pubkey_usage & PUBKEY_USAGE_SIG)) + { + rc = gpg_error (GPG_ERR_WRONG_KEY_USAGE); + if (!opt.quiet) + log_info (_("bad data signature from key %s: %s (0x%02x, 0x%x)\n"), + keystr_from_pk (pk), gpg_strerror (rc), + sig->sig_class, pk->pubkey_usage); + return rc; + } /* Make sure the digest algo is enabled (in case of a detached * signature). */ @@ -917,7 +926,7 @@ check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, if (IS_CERT (sig)) signer->req_usage = PUBKEY_USAGE_CERT; - rc = get_pubkey (ctrl, signer, sig->keyid); + rc = get_pubkey_for_sig (ctrl, signer, sig); if (rc) { xfree (signer); diff --git a/g10/sign.c b/g10/sign.c index df71ccce1..581a08f5b 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -772,7 +772,7 @@ write_signature_packets (ctrl_t ctrl, gpg_strerror (rc)); } else - xfree (sig); + free_seckey_enc (sig); if (rc) return rc; diff --git a/g10/test-stubs.c b/g10/test-stubs.c index 088aba8c6..a2b0d2906 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -535,3 +535,17 @@ tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) return 0; } + +int +get_revocation_reason (PKT_signature *sig, char **r_reason, + char **r_comment, size_t *r_commentlen) +{ + (void)sig; + (void)r_commentlen; + + if (r_reason) + *r_reason = NULL; + if (r_comment) + *r_comment = NULL; + return 0; +} diff --git a/g10/trust.c b/g10/trust.c index 6d4f0e74b..bd1c89458 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -437,391 +437,3 @@ get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) return _("revoked"); return trust_value_to_string (trustlevel); } - - - -/* - * Mark the signature of the given UID which are used to certify it. - * To do this, we first revmove all signatures which are not valid and - * from the remain ones we look for the latest one. If this is not a - * certification revocation signature we mark the signature by setting - * node flag bit 8. Revocations are marked with flag 11, and sigs - * from unavailable keys are marked with flag 12. Note that flag bits - * 9 and 10 are used for internal purposes. - */ -void -mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - u32 *main_kid, struct key_item *klist, - u32 curtime, u32 *next_expire) -{ - kbnode_t node; - PKT_signature *sig; - - /* First check all signatures. */ - for (node=uidnode->next; node; node = node->next) - { - int rc; - - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); - if (node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY) - break; /* ready */ - if (node->pkt->pkttype != PKT_SIGNATURE) - continue; - sig = node->pkt->pkt.signature; - if (main_kid - && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1]) - continue; /* ignore self-signatures if we pass in a main_kid */ - if (!IS_UID_SIG(sig) && !IS_UID_REV(sig)) - continue; /* we only look at these signature classes */ - if(sig->sig_class>=0x11 && sig->sig_class<=0x13 && - sig->sig_class-0x10flag |= 1<<12; - continue; - } - node->flag |= 1<<9; - } - /* Reset the remaining flags. */ - for (; node; node = node->next) - node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12); - - /* kbnode flag usage: bit 9 is here set for signatures to consider, - * bit 10 will be set by the loop to keep track of keyIDs already - * processed, bit 8 will be set for the usable signatures, and bit - * 11 will be set for usable revocations. */ - - /* For each cert figure out the latest valid one. */ - for (node=uidnode->next; node; node = node->next) - { - KBNODE n, signode; - u32 kid[2]; - u32 sigdate; - - if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY) - break; - if ( !(node->flag & (1<<9)) ) - continue; /* not a node to look at */ - if ( (node->flag & (1<<10)) ) - continue; /* signature with a keyID already processed */ - node->flag |= (1<<10); /* mark this node as processed */ - sig = node->pkt->pkt.signature; - signode = node; - sigdate = sig->timestamp; - kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1]; - - /* Now find the latest and greatest signature */ - for (n=uidnode->next; n; n = n->next) - { - if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY - || n->pkt->pkttype == PKT_SECRET_SUBKEY) - break; - if ( !(n->flag & (1<<9)) ) - continue; - if ( (n->flag & (1<<10)) ) - continue; /* shortcut already processed signatures */ - sig = n->pkt->pkt.signature; - if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1]) - continue; - n->flag |= (1<<10); /* mark this node as processed */ - - /* If signode is nonrevocable and unexpired and n isn't, - then take signode (skip). It doesn't matter which is - older: if signode was older then we don't want to take n - as signode is nonrevocable. If n was older then we're - automatically fine. */ - - if(((IS_UID_SIG(signode->pkt->pkt.signature) && - !signode->pkt->pkt.signature->flags.revocable && - (signode->pkt->pkt.signature->expiredate==0 || - signode->pkt->pkt.signature->expiredate>curtime))) && - (!(IS_UID_SIG(n->pkt->pkt.signature) && - !n->pkt->pkt.signature->flags.revocable && - (n->pkt->pkt.signature->expiredate==0 || - n->pkt->pkt.signature->expiredate>curtime)))) - continue; - - /* If n is nonrevocable and unexpired and signode isn't, - then take n. Again, it doesn't matter which is older: if - n was older then we don't want to take signode as n is - nonrevocable. If signode was older then we're - automatically fine. */ - - if((!(IS_UID_SIG(signode->pkt->pkt.signature) && - !signode->pkt->pkt.signature->flags.revocable && - (signode->pkt->pkt.signature->expiredate==0 || - signode->pkt->pkt.signature->expiredate>curtime))) && - ((IS_UID_SIG(n->pkt->pkt.signature) && - !n->pkt->pkt.signature->flags.revocable && - (n->pkt->pkt.signature->expiredate==0 || - n->pkt->pkt.signature->expiredate>curtime)))) - { - signode = n; - sigdate = sig->timestamp; - continue; - } - - /* At this point, if it's newer, it goes in as the only - remaining possibilities are signode and n are both either - revocable or expired or both nonrevocable and unexpired. - If the timestamps are equal take the later ordered - packet, presuming that the key packets are hopefully in - their original order. */ - - if (sig->timestamp >= sigdate) - { - signode = n; - sigdate = sig->timestamp; - } - } - - sig = signode->pkt->pkt.signature; - if (IS_UID_SIG (sig)) - { /* this seems to be a usable one which is not revoked. - * Just need to check whether there is an expiration time, - * We do the expired certification after finding a suitable - * certification, the assumption is that a signator does not - * want that after the expiration of his certificate the - * system falls back to an older certification which has a - * different expiration time */ - const byte *p; - u32 expire; - - p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL ); - expire = p? sig->timestamp + buf32_to_u32(p) : 0; - - if (expire==0 || expire > curtime ) - { - signode->flag |= (1<<8); /* yeah, found a good cert */ - if (next_expire && expire && expire < *next_expire) - *next_expire = expire; - } - } - else - signode->flag |= (1<<11); - } -} - - -static int -clean_sigs_from_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only) -{ - int deleted = 0; - kbnode_t node; - u32 keyid[2]; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - - keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); - - /* Passing in a 0 for current time here means that we'll never weed - out an expired sig. This is correct behavior since we want to - keep the most recent expired sig in a series. */ - mark_usable_uid_certs (ctrl, keyblock, uidnode, NULL, NULL, 0, NULL); - - /* What we want to do here is remove signatures that are not - considered as part of the trust calculations. Thus, all invalid - signatures are out, as are any signatures that aren't the last of - a series of uid sigs or revocations It breaks down like this: - coming out of mark_usable_uid_certs, if a sig is unflagged, it is - not even a candidate. If a sig has flag 9 or 10, that means it - was selected as a candidate and vetted. If a sig has flag 8 it - is a usable signature. If a sig has flag 11 it is a usable - revocation. If a sig has flag 12 it was issued by an unavailable - key. "Usable" here means the most recent valid - signature/revocation in a series from a particular signer. - - Delete everything that isn't a usable uid sig (which might be - expired), a usable revocation, or a sig from an unavailable - key. */ - - for (node=uidnode->next; - node && node->pkt->pkttype==PKT_SIGNATURE; - node=node->next) - { - int keep; - - keep = self_only? (node->pkt->pkt.signature->keyid[0] == keyid[0] - && node->pkt->pkt.signature->keyid[1] == keyid[1]) : 1; - - /* Keep usable uid sigs ... */ - if ((node->flag & (1<<8)) && keep) - continue; - - /* ... and usable revocations... */ - if ((node->flag & (1<<11)) && keep) - continue; - - /* ... and sigs from unavailable keys. */ - /* disabled for now since more people seem to want sigs from - unavailable keys removed altogether. */ - /* - if(node->flag & (1<<12)) - continue; - */ - - /* Everything else we delete */ - - /* At this point, if 12 is set, the signing key was unavailable. - If 9 or 10 is set, it's superseded. Otherwise, it's - invalid. */ - - if (noisy) - log_info ("removing signature from key %s on user ID \"%s\": %s\n", - keystr (node->pkt->pkt.signature->keyid), - uidnode->pkt->pkt.user_id->name, - node->flag&(1<<12)? "key unavailable": - node->flag&(1<<9)? "signature superseded" - /* */ :"invalid signature" ); - - delete_kbnode (node); - deleted++; - } - - return deleted; -} - - -/* This is substantially easier than clean_sigs_from_uid since we just - have to establish if the uid has a valid self-sig, is not revoked, - and is not expired. Note that this does not take into account - whether the uid has a trust path to it - just whether the keyholder - themselves has certified the uid. Returns true if the uid was - compacted. To "compact" a user ID, we simply remove ALL signatures - except the self-sig that caused the user ID to be remove-worthy. - We don't actually remove the user ID packet itself since it might - be resurrected in a later merge. Note that this function requires - that the caller has already done a merge_keys_and_selfsig(). - - TODO: change the import code to allow importing a uid with only a - revocation if the uid already exists on the keyring. */ - -static int -clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy) -{ - kbnode_t node; - PKT_user_id *uid = uidnode->pkt->pkt.user_id; - int deleted = 0; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - log_assert (uidnode->pkt->pkttype==PKT_USER_ID); - - /* Skip valid user IDs, compacted user IDs, and non-self-signed user - IDs if --allow-non-selfsigned-uid is set. */ - if (uid->created - || uid->flags.compacted - || (!uid->flags.expired && !uid->flags.revoked && opt.allow_non_selfsigned_uid)) - return 0; - - for (node=uidnode->next; - node && node->pkt->pkttype == PKT_SIGNATURE; - node=node->next) - { - if (!node->pkt->pkt.signature->flags.chosen_selfsig) - { - delete_kbnode (node); - deleted = 1; - uidnode->pkt->pkt.user_id->flags.compacted = 1; - } - } - - if (noisy) - { - const char *reason; - char *user = utf8_to_native (uid->name, uid->len, 0); - - if (uid->flags.revoked) - reason = _("revoked"); - else if (uid->flags.expired) - reason = _("expired"); - else - reason = _("invalid"); - - log_info ("compacting user ID \"%s\" on key %s: %s\n", - user, keystr_from_pk (keyblock->pkt->pkt.public_key), - reason); - - xfree (user); - } - - return deleted; -} - - -/* Needs to be called after a merge_keys_and_selfsig() */ -void -clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only, int *uids_cleaned, int *sigs_cleaned) -{ - int dummy = 0; - - log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY - || keyblock->pkt->pkttype == PKT_SECRET_KEY); - log_assert (uidnode->pkt->pkttype==PKT_USER_ID); - - if (!uids_cleaned) - uids_cleaned = &dummy; - - if (!sigs_cleaned) - sigs_cleaned = &dummy; - - /* Do clean_uid_from_key first since if it fires off, we don't have - to bother with the other. */ - *uids_cleaned += clean_uid_from_key (keyblock, uidnode, noisy); - if (!uidnode->pkt->pkt.user_id->flags.compacted) - *sigs_cleaned += clean_sigs_from_uid (ctrl, keyblock, uidnode, - noisy, self_only); -} - - -/* NB: This function marks the deleted nodes only and the caller is - * responsible to skip or remove them. */ -void -clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned, int *sigs_cleaned) -{ - kbnode_t node; - - merge_keys_and_selfsig (ctrl, keyblock); - - for (node = keyblock->next; - node && !(node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY); - node = node->next) - { - if (node->pkt->pkttype == PKT_USER_ID) - clean_one_uid (ctrl, keyblock, node, noisy, self_only, - uids_cleaned, sigs_cleaned); - } - - /* Remove bogus subkey binding signatures: The only signatures - * allowed are of class 0x18 and 0x28. */ - log_assert (!node || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY)); - for (; node; node = node->next) - { - if (is_deleted_kbnode (node)) - continue; - if (node->pkt->pkttype == PKT_SIGNATURE - && !(IS_SUBKEY_SIG (node->pkt->pkt.signature) - || IS_SUBKEY_REV (node->pkt->pkt.signature))) - { - delete_kbnode (node); - if (sigs_cleaned) - ++*sigs_cleaned; - } - } -} diff --git a/g10/trustdb.c b/g10/trustdb.c index 2c2d2394a..8ef6db542 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -41,6 +41,7 @@ #include "tdbio.h" #include "trustdb.h" #include "tofu.h" +#include "key-clean.h" typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ diff --git a/g10/trustdb.h b/g10/trustdb.h index 4bc4ca971..d52fc53f2 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -46,36 +46,6 @@ #define NAMEHASH_LEN 20 -/* - * A structure to store key identification as well as some stuff needed - * for validation - */ -struct key_item { - struct key_item *next; - unsigned int ownertrust,min_ownertrust; - byte trust_depth; - byte trust_value; - char *trust_regexp; - u32 kid[2]; -}; - - -/* - * Check whether the signature SIG is in the klist K. - */ -static inline struct key_item * -is_in_klist (struct key_item *k, PKT_signature *sig) -{ - for (; k; k = k->next) - { - if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) - return k; - } - return NULL; -} - - - /*-- trust.c --*/ int cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk); void register_trusted_keyid (u32 *keyid); @@ -103,17 +73,6 @@ int get_validity_info (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, const char *get_validity_string (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid); -void mark_usable_uid_certs (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - u32 *main_kid, struct key_item *klist, - u32 curtime, u32 *next_expire); - -void clean_one_uid (ctrl_t ctrl, kbnode_t keyblock, kbnode_t uidnode, - int noisy, int self_only, - int *uids_cleaned, int *sigs_cleaned); -void clean_key (ctrl_t ctrl, kbnode_t keyblock, int noisy, int self_only, - int *uids_cleaned,int *sigs_cleaned); - - /*-- trustdb.c --*/ void tdb_register_trusted_keyid (u32 *keyid); diff --git a/po/Makevars b/po/Makevars index 485c72ca7..90b0c5b83 100644 --- a/po/Makevars +++ b/po/Makevars @@ -8,7 +8,63 @@ subdir = po top_builddir = .. # These options get passed to xgettext. -XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=L_ +XGETTEXT_OPTIONS = \ + --keyword=_ --keyword=N_ --keyword=L_ \ + --flag=gcry_log_debug:1:c-format \ + --flag=gpgrt_fprintf:2:c-format \ + --flag=gpgrt_fprintf_unlocked:2:c-format \ + --flag=gpgrt_printf:1:c-format \ + --flag=gpgrt_printf_unlocked:1:c-format \ + --flag=gpgrt_vfprintf:2:c-format \ + --flag=gpgrt_vfprintf_unlocked:2:c-format \ + --flag=gpgrt_asprintf:2:c-format \ + --flag=gpgrt_vasprintf:2:c-format \ + --flag=gpgrt_bsprintf:1:c-format \ + --flag=gpgrt_vbsprintf:1:c-format \ + --flag=gpgrt_snprintf:3:c-format \ + --flag=gpgrt_vsnprintf:3:c-format \ + --flag=gpgrt_log:2:c-format \ + --flag=gpgrt_log_bug:1:c-format \ + --flag=gpgrt_log_fatal:1:c-format \ + --flag=gpgrt_log_error:1:c-format \ + --flag=gpgrt_log_info:1:c-format \ + --flag=gpgrt_log_debug:1:c-format \ + --flag=gpgrt_log_debug_string:2:c-format \ + --flag=gpgrt_log_printf:1:c-format \ + --flag=gpgrt_log_printhex:3:c-format \ + --flag=gpgrt_log_clock:1:c-format \ + --flag=log_log:2:c-format \ + --flag=log_bug:1:c-format \ + --flag=log_fatal:1:c-format \ + --flag=log_error:1:c-format \ + --flag=log_info:1:c-format \ + --flag=log_debug:1:c-format \ + --flag=log_debug_string:2:c-format \ + --flag=log_printf:1:c-format \ + --flag=log_printhex:3:c-format \ + --flag=log_clock:1:c-format + --flag=put_membuf_printf:2:c-format \ + --flag=tty_printf:1:c-format \ + --flag=tty_fprintf:2:c-format \ + --flag=tty_getf:1:c-format \ + --flag=writeout_para:2:c-format \ + --flag=writeout_li:3:c-format \ + --flag=writeout_rem:2:c-format \ + --flag=xasprintf:1:c-format \ + --flag=xtryasprintf:1:c-format \ + --flag=log_debug_with_string:2:c-format \ + --flag=print_assuan_status:3:c-format \ + --flag=vprint_assuan_status:3:c-format \ + --flag=agent_print_status:3:c-format \ + --flag=dirmngr_status_helpf:2:c-format \ + --flag=dirmngr_status_printf:3:c-format \ + --flag=ks_printf_help:2:c-format \ + --flag=print_further_info:1:c-format \ + --flag=write_status_printf:2:c-format \ + --flag=kbxd_print_status:3:c-format \ + --flag=gpgconf_write_status:2:c-format \ + --flag=wks_write_status:2:c-format + # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/po/POTFILES.in b/po/POTFILES.in index f19cb49c4..fe8d45f7e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -26,7 +26,6 @@ common/gettime.c common/ksba-io-support.c common/argparse.c -common/logging.c common/utf8conv.c common/dotlock.c common/init.c diff --git a/tools/send-mail.c b/tools/send-mail.c index 9f07c7ac5..6492c43c1 100644 --- a/tools/send-mail.c +++ b/tools/send-mail.c @@ -33,7 +33,7 @@ static gpg_error_t run_sendmail (estream_t data) { gpg_error_t err; - const char pgmname[] = "/usr/lib/sendmail"; + const char pgmname[] = NAME_OF_SENDMAIL; const char *argv[3]; argv[0] = "-oi"; diff --git a/tools/watchgnupg.c b/tools/watchgnupg.c index fc58d1428..1db1c7827 100644 --- a/tools/watchgnupg.c +++ b/tools/watchgnupg.c @@ -253,7 +253,7 @@ static void print_version (int with_help) { fputs (MYVERSION_LINE "\n" - "Copyright (C) 2017 Free Software Foundation, Inc.\n" + GNUPG_DEF_COPYRIGHT_LINE "\n" "License GPLv3+: " "GNU GPL version 3 or later \n" "This is free software: you are free to change and redistribute it.\n"