mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Merge branch 'master' into gniibe/decryption-key
This commit is contained in:
commit
e8caa282d3
42
NEWS
42
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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
233
agent/command.c
233
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 <key>\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] <key> <ttl> [<percent_escaped_value>]\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 <key>\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)
|
||||
|
@ -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)))
|
||||
|
@ -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\
|
||||
|
@ -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);
|
||||
|
1121
common/logging.c
1121
common/logging.c
File diff suppressed because it is too large
Load Diff
@ -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. */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
30
configure.ac
30
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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
23
doc/DETAILS
23
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
|
||||
|
22
doc/gpg.texi
22
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"}.
|
||||
|
@ -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}.
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -851,6 +851,7 @@ fetch_url (ctrl_t ctrl)
|
||||
}
|
||||
}
|
||||
|
||||
agent_release_card_info (&info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
19
g10/export.c
19
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)
|
||||
{
|
||||
|
49
g10/getkey.c
49
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;
|
||||
}
|
||||
|
||||
|
17
g10/gpg.c
17
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;
|
||||
|
@ -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);
|
||||
|
||||
|
15
g10/gpgv.c
15
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;
|
||||
}
|
||||
|
550
g10/import.c
550
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 */
|
||||
|
241
g10/key-check.c
241
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];
|
||||
|
614
g10/key-clean.c
Normal file
614
g10/key-clean.c
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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-0x10<opt.min_cert_level)
|
||||
continue; /* treat anything under our min_cert_level as an
|
||||
invalid signature */
|
||||
if (klist && !is_in_klist (klist, sig))
|
||||
continue; /* no need to check it then */
|
||||
if ((rc=check_key_signature (ctrl, keyblock, node, NULL)))
|
||||
{
|
||||
/* we ignore anything that won't verify, but tag the
|
||||
no_pubkey case */
|
||||
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
|
||||
node->flag |= 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
g10/key-clean.h
Normal file
52
g10/key-clean.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
* 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*/
|
38
g10/keydb.h
38
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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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 --*/
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -772,7 +772,7 @@ write_signature_packets (ctrl_t ctrl,
|
||||
gpg_strerror (rc));
|
||||
}
|
||||
else
|
||||
xfree (sig);
|
||||
free_seckey_enc (sig);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -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;
|
||||
}
|
||||
|
388
g10/trust.c
388
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-0x10<opt.min_cert_level)
|
||||
continue; /* treat anything under our min_cert_level as an
|
||||
invalid signature */
|
||||
if (klist && !is_in_klist (klist, sig))
|
||||
continue; /* no need to check it then */
|
||||
if ((rc=check_key_signature (ctrl, keyblock, node, NULL)))
|
||||
{
|
||||
/* we ignore anything that won't verify, but tag the
|
||||
no_pubkey case */
|
||||
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
|
||||
node->flag |= 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() */
|
||||
|
@ -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);
|
||||
|
58
po/Makevars
58
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
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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 <https://gnu.org/licenses/gpl.html>\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user