diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index f38d75579..2b42bfbf9 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1,3083 +1,3085 @@ /* gpgcompose.c - Maintainer tool to create OpenPGP messages by hand. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include "gpg.h" #include "packet.h" #include "keydb.h" #include "main.h" #include "options.h" static int do_debug; #define debug(fmt, ...) \ do { if (do_debug) log_debug (fmt, ##__VA_ARGS__); } while (0) /* --encryption, for instance, adds a filter in front of out. There is an operator (--encryption-pop) to end this. We use the following infrastructure to make it easy to pop the state. */ struct filter { void *func; void *context; int pkttype; int partial_block_mode; struct filter *next; }; /* Hack to ass CTRL to some functions. */ static ctrl_t global_ctrl; static struct filter *filters; static void filter_push (iobuf_t out, void *func, void *context, int type, int partial_block_mode) { gpg_error_t err; struct filter *f = xmalloc_clear (sizeof (*f)); f->next = filters; f->func = func; f->context = context; f->pkttype = type; f->partial_block_mode = partial_block_mode; filters = f; err = iobuf_push_filter (out, func, context); if (err) log_fatal ("Adding filter: %s\n", gpg_strerror (err)); } static void filter_pop (iobuf_t out, int expected_type) { gpg_error_t err; struct filter *f = filters; log_assert (f); if (f->pkttype != expected_type) log_fatal ("Attempted to pop a %s container, " "but current container is a %s container.\n", pkttype_str (f->pkttype), pkttype_str (expected_type)); if (f->pkttype == PKT_ENCRYPTED) { err = iobuf_pop_filter (out, f->func, f->context); if (err) log_fatal ("Popping encryption filter: %s\n", gpg_strerror (err)); } else log_fatal ("FILTERS appears to be corrupted.\n"); if (f->partial_block_mode) iobuf_set_partial_body_length_mode (out, 0); filters = f->next; xfree (f); } /* Return if CIPHER_ID is a valid cipher. */ static int valid_cipher (int cipher_id) { return (cipher_id == CIPHER_ALGO_IDEA || cipher_id == CIPHER_ALGO_3DES || cipher_id == CIPHER_ALGO_CAST5 || cipher_id == CIPHER_ALGO_BLOWFISH || cipher_id == CIPHER_ALGO_AES || cipher_id == CIPHER_ALGO_AES192 || cipher_id == CIPHER_ALGO_AES256 || cipher_id == CIPHER_ALGO_TWOFISH || cipher_id == CIPHER_ALGO_CAMELLIA128 || cipher_id == CIPHER_ALGO_CAMELLIA192 || cipher_id == CIPHER_ALGO_CAMELLIA256); } /* Parse a session key encoded as a string of the form x:HEXDIGITS where x is the algorithm id. (This is the format emitted by gpg --show-session-key.) */ struct session_key { int algo; int keylen; char *key; }; static struct session_key parse_session_key (const char *option, char *p, int require_algo) { char *tail; struct session_key sk; memset (&sk, 0, sizeof (sk)); /* Check for the optional "cipher-id:" at the start of the string. */ errno = 0; sk.algo = strtol (p, &tail, 10); if (! errno && tail && *tail == ':') { if (! valid_cipher (sk.algo)) log_info ("%s: %d is not a known cipher (but using anyways)\n", option, sk.algo); p = tail + 1; } else if (require_algo) log_fatal ("%s: Session key must have the form algo:HEXCHARACTERS.\n", option); else sk.algo = 0; /* Ignore a leading 0x. */ if (p[0] == '0' && p[1] == 'x') p += 2; if (strlen (p) % 2 != 0) log_fatal ("%s: session key must consist of an even number of hexadecimal characters.\n", option); sk.keylen = strlen (p) / 2; sk.key = xmalloc (sk.keylen); if (hex2bin (p, sk.key, sk.keylen) == -1) log_fatal ("%s: Session key must only contain hexadecimal characters\n", option); return sk; } /* A callback. OPTION_STR is the option that was matched. ARGC is the number of arguments following the option and ARGV are those arguments. (Thus, argv[0] is the first string following the option and argv[-1] is the option.) COOKIE is the opaque value passed to process_options. */ typedef int (*option_prcessor_t) (const char *option_str, int argc, char *argv[], void *cookie); struct option { /* The option that this matches. This must start with "--" or be the empty string. The empty string matches bare arguments. */ const char *option; /* The function to call to process this option. */ option_prcessor_t func; /* Documentation. */ const char *help; }; /* Merge two lists of options. Note: this makes a shallow copy! The caller must xfree() the result. */ static struct option * merge_options (struct option a[], struct option b[]) { int i, j; struct option *c; for (i = 0; a[i].option; i ++) ; for (j = 0; b[j].option; j ++) ; c = xmalloc ((i + j + 1) * sizeof (struct option)); memcpy (c, a, i * sizeof (struct option)); memcpy (&c[i], b, j * sizeof (struct option)); c[i + j].option = NULL; if (a[i].help && b[j].help) c[i + j].help = xasprintf ("%s\n\n%s", a[i].help, b[j].help); else if (a[i].help) c[i + j].help = a[i].help; else if (b[j].help) c[i + j].help = b[j].help; return c; } /* Returns whether ARG is an option. All options start with --. */ static int is_option (const char *arg) { return arg[0] == '-' && arg[1] == '-'; } /* OPTIONS is a NULL terminated array of struct option:s. Finds the entry that is the same as ARG. Returns -1 if no entry is found. The empty string option matches bare arguments. */ static int match_option (const struct option options[], const char *arg) { int i; int bare_arg = ! is_option (arg); for (i = 0; options[i].option; i ++) if ((! bare_arg && strcmp (options[i].option, arg) == 0) /* Non-options match the empty string. */ || (bare_arg && options[i].option[0] == '\0')) return i; return -1; } static void show_help (struct option options[]) { int i; int max_length = 0; int space; for (i = 0; options[i].option; i ++) { const char *option = options[i].option[0] ? options[i].option : "ARG"; int l = strlen (option); if (l > max_length) max_length = l; } space = 72 - (max_length + 2); if (space < 40) space = 40; for (i = 0; ; i ++) { const char *option = options[i].option; const char *help = options[i].help; int l; int j; char *tmp; char *formatted; char *p; char *newline; if (! option && ! help) break; if (option) { const char *o = option[0] ? option : "ARG"; l = strlen (o); fprintf (stdout, "%s", o); } if (! help) { fputc ('\n', stdout); continue; } if (option) for (j = l; j < max_length + 2; j ++) fputc (' ', stdout); #define BOLD_START "\033[1m" #define NORMAL_RESTORE "\033[0m" #define BOLD(x) BOLD_START x NORMAL_RESTORE if (! option || options[i].func) tmp = (char *) help; else tmp = xasprintf ("%s " BOLD("(Unimplemented.)"), help); if (! option) space = 72; formatted = format_text (tmp, space, space + 4); if (!formatted) abort (); if (tmp != help) xfree (tmp); if (! option) { printf ("\n%s\n", formatted); break; } for (p = formatted; p && *p; p = (*newline == '\0') ? newline : newline + 1) { newline = strchr (p, '\n'); if (! newline) newline = &p[strlen (p)]; l = (size_t) newline - (size_t) p; if (p != formatted) for (j = 0; j < max_length + 2; j ++) fputc (' ', stdout); fwrite (p, l, 1, stdout); fputc ('\n', stdout); } xfree (formatted); } } /* Return value is number of consumed argv elements. */ static int process_options (const char *parent_option, struct option break_options[], struct option local_options[], void *lcookie, struct option global_options[], void *gcookie, int argc, char *argv[]) { int i; for (i = 0; i < argc; i ++) { int j; struct option *option; void *cookie; int bare_arg; option_prcessor_t func; int consumed; if (break_options) { j = match_option (break_options, argv[i]); if (j != -1) /* Match. Break out. */ return i; } j = match_option (local_options, argv[i]); if (j == -1) { if (global_options) j = match_option (global_options, argv[i]); if (j == -1) { if (strcmp (argv[i], "--help") == 0) { if (! global_options) show_help (local_options); else { struct option *combined = merge_options (local_options, global_options); show_help (combined); xfree (combined); } g10_exit (0); } if (parent_option) log_fatal ("%s: Unknown option: %s\n", parent_option, argv[i]); else log_fatal ("Unknown option: %s\n", argv[i]); } option = &global_options[j]; cookie = gcookie; } else { option = &local_options[j]; cookie = lcookie; } bare_arg = strcmp (option->option, "") == 0; func = option->func; if (! func) { if (bare_arg) log_fatal ("Bare arguments unimplemented.\n"); else log_fatal ("Unimplemented option: %s\n", option->option); } consumed = func (bare_arg ? parent_option : argv[i], argc - i - !bare_arg, &argv[i + !bare_arg], cookie); i += consumed; if (bare_arg) i --; } return i; } /* The keys, subkeys, user ids and user attributes in the order that they were added. */ PACKET components[20]; /* The number of components. */ int ncomponents; static int add_component (int pkttype, void *component) { int i = ncomponents ++; log_assert (i < sizeof (components) / sizeof (components[0])); log_assert (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_PUBLIC_SUBKEY || pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY || pkttype == PKT_USER_ID || pkttype == PKT_ATTRIBUTE); components[i].pkttype = pkttype; components[i].pkt.generic = component; return i; } static void dump_component (PACKET *pkt) { struct kbnode_struct kbnode; if (! do_debug) return; memset (&kbnode, 0, sizeof (kbnode)); kbnode.pkt = pkt; dump_kbnode (&kbnode); } /* Returns the first primary key in COMPONENTS or NULL if there is none. */ static PKT_public_key * primary_key (void) { int i; for (i = 0; i < ncomponents; i ++) if (components[i].pkttype == PKT_PUBLIC_KEY) return components[i].pkt.public_key; return NULL; } /* The last session key (updated when adding a SK-ESK, PK-ESK or SED packet. */ static DEK session_key; static int user_id (const char *option, int argc, char *argv[], void *cookie); static int public_key (const char *option, int argc, char *argv[], void *cookie); static int sk_esk (const char *option, int argc, char *argv[], void *cookie); static int pk_esk (const char *option, int argc, char *argv[], void *cookie); static int encrypted (const char *option, int argc, char *argv[], void *cookie); static int encrypted_pop (const char *option, int argc, char *argv[], void *cookie); static int literal (const char *option, int argc, char *argv[], void *cookie); static int signature (const char *option, int argc, char *argv[], void *cookie); static int copy (const char *option, int argc, char *argv[], void *cookie); static struct option major_options[] = { { "--user-id", user_id, "Create a user id packet." }, { "--public-key", public_key, "Create a public key packet." }, { "--private-key", NULL, "Create a private key packet." }, { "--public-subkey", public_key, "Create a subkey packet." }, { "--private-subkey", NULL, "Create a private subkey packet." }, { "--sk-esk", sk_esk, "Create a symmetric-key encrypted session key packet." }, { "--pk-esk", pk_esk, "Create a public-key encrypted session key packet." }, { "--encrypted", encrypted, "Create a symmetrically encrypted data packet." }, { "--encrypted-mdc", encrypted, "Create a symmetrically encrypted and integrity protected data packet." }, { "--encrypted-pop", encrypted_pop, "Pop the most recent encryption container started by either" " --encrypted or --encrypted-mdc." }, { "--compressed", NULL, "Create a compressed data packet." }, { "--literal", literal, "Create a literal (plaintext) data packet." }, { "--signature", signature, "Create a signature packet." }, { "--onepass-sig", NULL, "Create a one-pass signature packet." }, { "--copy", copy, "Copy the specified file." }, { NULL, NULL, "To get more information about a given command, use:\n\n" " $ gpgcompose --command --help to list a command's options."}, }; static struct option global_options[] = { { NULL, NULL, NULL }, }; /* Make our lives easier and use a static limit for the user name. 10k is way more than enough anyways... */ const int user_id_max_len = 10 * 1024; static int user_id_name (const char *option, int argc, char *argv[], void *cookie) { PKT_user_id *uid = cookie; int l; if (argc == 0) log_fatal ("Usage: %s USER_ID\n", option); if (uid->len) log_fatal ("Attempt to set user id multiple times.\n"); l = strlen (argv[0]); if (l > user_id_max_len) log_fatal ("user id too long (max: %d)\n", user_id_max_len); memcpy (uid->name, argv[0], l); uid->name[l] = 0; uid->len = l; return 1; } static struct option user_id_options[] = { { "", user_id_name, "Set the user id. This is usually in the format " "\"Name (comment) \"" }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --user-id \"USERID\" | " GPG_NAME " --list-packets" } }; static int user_id (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; PKT_user_id *uid = xmalloc_clear (sizeof (*uid) + user_id_max_len); int c = add_component (PKT_USER_ID, uid); int processed; processed = process_options (option, major_options, user_id_options, uid, global_options, NULL, argc, argv); if (! uid->len) log_fatal ("%s: user id not given", option); err = build_packet (out, &components[c]); if (err) log_fatal ("Serializing user id packet: %s\n", gpg_strerror (err)); debug ("Wrote user id packet:\n"); dump_component (&components[c]); return processed; } static int pk_search_terms (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC desc; kbnode_t kb; PKT_public_key *pk = cookie; PKT_public_key *pk_ref; int i; if (argc == 0) log_fatal ("Usage: %s KEYID\n", option); if (pk->pubkey_algo) log_fatal ("%s: multiple keys provided\n", option); err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); hd = keydb_new (); err = keydb_search (hd, &desc, 1, NULL); if (err) log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err)); err = keydb_get_keyblock (hd, &kb); if (err) log_fatal ("retrieving keyblock for '%s': %s\n", argv[0], gpg_strerror (err)); keydb_release (hd); pk_ref = kb->pkt->pkt.public_key; /* Copy the timestamp (if not already set), algo and public key parameters. */ if (! pk->timestamp) pk->timestamp = pk_ref->timestamp; pk->pubkey_algo = pk_ref->pubkey_algo; for (i = 0; i < pubkey_get_npkey (pk->pubkey_algo); i ++) pk->pkey[i] = gcry_mpi_copy (pk_ref->pkey[i]); release_kbnode (kb); return 1; } static int pk_timestamp (const char *option, int argc, char *argv[], void *cookie) { PKT_public_key *pk = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; pk->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } #define TIMESTAMP_HELP \ "Either as seconds since the epoch or as an ISO 8601 formatted " \ "string (yyyymmddThhmmss, where the T is a literal)." static struct option pk_options[] = { { "--timestamp", pk_timestamp, "The creation time. " TIMESTAMP_HELP }, { "", pk_search_terms, "The key to copy the creation time and public key parameters from." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --public-key $KEYID --user-id \"USERID\" \\\n" " | " GPG_NAME " --list-packets" } }; static int public_key (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; iobuf_t out = cookie; PKT_public_key *pk; int c; int processed; int t = (strcmp (option, "--public-key") == 0 ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY); (void) option; pk = xmalloc_clear (sizeof (*pk)); pk->version = 4; c = add_component (t, pk); processed = process_options (option, major_options, pk_options, pk, global_options, NULL, argc, argv); if (! pk->pubkey_algo) log_fatal ("%s: key to extract public key parameters from not given", option); /* Clear the keyid in case we updated one of the relevant fields after accessing it. */ pk->keyid[0] = pk->keyid[1] = 0; err = build_packet (out, &components[c]); if (err) log_fatal ("serializing %s packet: %s\n", t == PKT_PUBLIC_KEY ? "public key" : "subkey", gpg_strerror (err)); debug ("Wrote %s packet:\n", t == PKT_PUBLIC_KEY ? "public key" : "subkey"); dump_component (&components[c]); return processed; } struct signinfo { /* Key with which to sign. */ kbnode_t issuer_kb; PKT_public_key *issuer_pk; /* Overrides the issuer's key id. */ u32 issuer_keyid[2]; /* Sets the issuer's keyid to the primary key's key id. */ int issuer_keyid_self; /* Key to sign. */ PKT_public_key *pk; /* Subkey to sign. */ PKT_public_key *sk; /* User id to sign. */ PKT_user_id *uid; int class; int digest_algo; u32 timestamp; u32 key_expiration; byte *cipher_algorithms; int cipher_algorithms_len; byte *digest_algorithms; int digest_algorithms_len; byte *compress_algorithms; int compress_algorithms_len; u32 expiration; int exportable_set; int exportable; int revocable_set; int revocable; int trust_level_set; byte trust_args[2]; char *trust_scope; struct revocation_key *revocation_key; int nrevocation_keys; struct notation *notations; byte *key_server_preferences; int key_server_preferences_len; char *key_server; int primary_user_id_set; int primary_user_id; char *policy_uri; byte *key_flags; int key_flags_len; char *signers_user_id; byte reason_for_revocation_code; char *reason_for_revocation; byte *features; int features_len; /* Whether to corrupt the signature. */ int corrupt; }; static int sig_issuer (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC desc; struct signinfo *si = cookie; if (argc == 0) log_fatal ("Usage: %s KEYID\n", option); if (si->issuer_pk) log_fatal ("%s: multiple keys provided\n", option); err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); hd = keydb_new (); err = keydb_search (hd, &desc, 1, NULL); if (err) log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err)); err = keydb_get_keyblock (hd, &si->issuer_kb); if (err) log_fatal ("retrieving keyblock for '%s': %s\n", argv[0], gpg_strerror (err)); keydb_release (hd); si->issuer_pk = si->issuer_kb->pkt->pkt.public_key; return 1; } static int sig_issuer_keyid (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_SEARCH_DESC desc; struct signinfo *si = cookie; if (argc == 0) log_fatal ("Usage: %s KEYID|self\n", option); if (si->issuer_keyid[0] || si->issuer_keyid[1] || si->issuer_keyid_self) log_fatal ("%s given multiple times.\n", option); if (strcasecmp (argv[0], "self") == 0) { si->issuer_keyid_self = 1; return 1; } err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); if (desc.mode != KEYDB_SEARCH_MODE_LONG_KID) log_fatal ("%s is not a valid long key id.\n", argv[0]); keyid_copy (si->issuer_keyid, desc.u.kid); return 1; } static int sig_pk (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s COMPONENT_INDEX\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); if (i >= ncomponents) log_fatal ("%d: No such component (have %d components so far)\n", i, ncomponents); if (! (components[i].pkttype == PKT_PUBLIC_KEY || components[i].pkttype == PKT_PUBLIC_SUBKEY)) log_fatal ("Component %d is not a public key or a subkey.", i); if (strcmp (option, "--pk") == 0) { if (si->pk) log_fatal ("%s already given.\n", option); si->pk = components[i].pkt.public_key; } else if (strcmp (option, "--sk") == 0) { if (si->sk) log_fatal ("%s already given.\n", option); si->sk = components[i].pkt.public_key; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_user_id (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s COMPONENT_INDEX\n", option); if (si->uid) log_fatal ("%s already given.\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); if (i >= ncomponents) log_fatal ("%d: No such component (have %d components so far)\n", i, ncomponents); if (! (components[i].pkttype != PKT_USER_ID || components[i].pkttype == PKT_ATTRIBUTE)) log_fatal ("Component %d is not a public key or a subkey.", i); si->uid = components[i].pkt.user_id; return 1; } static int sig_class (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s CLASS\n", option); errno = 0; i = strtoul (argv[0], &tail, 0); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); si->class = i; return 1; } static int sig_digest (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s DIGEST_ALGO\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); si->digest_algo = i; return 1; } static int sig_timestamp (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; si->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } static int sig_expiration (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int is_expiration = strcmp (option, "--expiration") == 0; u32 *i = is_expiration ? &si->expiration : &si->key_expiration; if (! is_expiration) log_assert (strcmp (option, "--key-expiration") == 0); if (argc == 0) log_fatal ("Usage: %s DURATION\n", option); *i = parse_expire_string (argv[0]); if (*i == (u32)-1) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } static int sig_int_list (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int nvalues = 1; char *values = xmalloc (nvalues * sizeof (values[0])); char *tail = argv[0]; int i; byte **a; int *n; if (argc == 0) log_fatal ("Usage: %s VALUE[,VALUE...]\n", option); for (i = 0; tail && *tail; i ++) { int v; char *old_tail = tail; errno = 0; v = strtol (tail, &tail, 0); if (errno || old_tail == tail || (tail && !(*tail == ',' || *tail == 0))) log_fatal ("Invalid value passed to %s (%s). " "Expected a list of comma separated numbers\n", option, argv[0]); if (! (0 <= v && v <= 255)) log_fatal ("%s: %d is out of range (Expected: 0-255)\n", option, v); if (i == nvalues) { nvalues *= 2; values = xrealloc (values, nvalues * sizeof (values[0])); } values[i] = v; if (*tail == ',') tail ++; else log_assert (*tail == 0); } if (strcmp ("--cipher-algos", option) == 0) { a = &si->cipher_algorithms; n = &si->cipher_algorithms_len; } else if (strcmp ("--digest-algos", option) == 0) { a = &si->digest_algorithms; n = &si->digest_algorithms_len; } else if (strcmp ("--compress-algos", option) == 0) { a = &si->compress_algorithms; n = &si->compress_algorithms_len; } else log_fatal ("Cannot handle %s\n", option); if (*a) log_fatal ("Option %s given multiple times.\n", option); *a = values; *n = i; return 1; } static int sig_flag (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int range[2] = {0, 255}; char *tail; int v; if (strcmp (option, "--primary-user-id") == 0) range[1] = 1; if (argc <= 1) { if (range[0] == 0 && range[1] == 1) log_fatal ("Usage: %s 0|1\n", option); else log_fatal ("Usage: %s %d-%d\n", option, range[0], range[1]); } errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1])) log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n", option, argv[0], range[0], range[1]); if (strcmp (option, "--exportable") == 0) { si->exportable_set = 1; si->exportable = v; } else if (strcmp (option, "--revocable") == 0) { si->revocable_set = 1; si->revocable = v; } else if (strcmp (option, "--primary-user-id") == 0) { si->primary_user_id_set = 1; si->primary_user_id = v; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_trust_level (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail; if (argc <= 1) log_fatal ("Usage: %s DEPTH TRUST_AMOUNT\n", option); for (i = 0; i < sizeof (si->trust_args) / sizeof (si->trust_args[0]); i ++) { int v; errno = 0; v = strtol (argv[i], &tail, 0); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("Invalid value passed to %s (%s). Expected 0-255\n", option, argv[i]); si->trust_args[i] = v; } si->trust_level_set = 1; return 2; } static int sig_string_arg (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *p = argv[0]; char **s; if (argc == 0) log_fatal ("Usage: %s STRING\n", option); if (strcmp (option, "--trust-scope") == 0) s = &si->trust_scope; else if (strcmp (option, "--key-server") == 0) s = &si->key_server; else if (strcmp (option, "--signers-user-id") == 0) s = &si->signers_user_id; else if (strcmp (option, "--policy-uri") == 0) s = &si->policy_uri; else log_fatal ("Cannot handle %s\n", option); if (*s) log_fatal ("%s already given.\n", option); *s = xstrdup (p); return 1; } static int sig_revocation_key (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; struct signinfo *si = cookie; int v; char *tail; PKT_public_key pk; struct revocation_key *revkey; if (argc < 2) log_fatal ("Usage: %s CLASS KEYID\n", option); memset (&pk, 0, sizeof (pk)); errno = 0; v = strtol (argv[0], &tail, 16); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("%s: Invalid class value (%s). Expected 0-255\n", option, argv[0]); pk.req_usage = PUBKEY_USAGE_SIG; err = get_pubkey_byname (NULL, NULL, &pk, argv[1], NULL, NULL, 1, 1); if (err) log_fatal ("looking up key %s: %s\n", argv[1], gpg_strerror (err)); si->nrevocation_keys ++; si->revocation_key = xrealloc (si->revocation_key, si->nrevocation_keys * sizeof (*si->revocation_key)); revkey = &si->revocation_key[si->nrevocation_keys - 1]; revkey->class = v; revkey->algid = pk.pubkey_algo; fingerprint_from_pk (&pk, revkey->fpr, NULL); release_public_key_parts (&pk); return 2; } static int sig_notation (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int is_blob = strcmp (option, "--notation") != 0; struct notation *notation; char *p = argv[0]; int p_free = 0; char *data; int data_size; int data_len; if (argc == 0) log_fatal ("Usage: %s [!<]name=value\n", option); if ((p[0] == '!' && p[1] == '<') || p[0] == '<') /* Read from a file. */ { char *filename = NULL; iobuf_t in; int prefix; if (p[0] == '<') p ++; else { /* Remove the '<', which string_to_notation does not understand, and preserve the '!'. */ p = xstrdup (&p[1]); p_free = 1; p[0] = '!'; } filename = strchr (p, '='); if (! filename) log_fatal ("No value specified. Usage: %s [!<]name=value\n", option); filename ++; prefix = (size_t) filename - (size_t) p; errno = 0; in = iobuf_open (filename); if (! in) log_fatal ("Opening '%s': %s\n", filename, errno ? strerror (errno): "unknown error"); /* A notation can be at most about a few dozen bytes short of 64k. Since this is relatively small, we just allocate that much instead of trying to dynamically size a buffer. */ data_size = 64 * 1024; data = xmalloc (data_size); log_assert (prefix <= data_size); memcpy (data, p, prefix); data_len = iobuf_read (in, &data[prefix], data_size - prefix - 1); if (data_len == -1) /* EOF => 0 bytes read. */ data_len = 0; if (data_len == data_size - prefix - 1) /* Technically, we should do another read and check for EOF, but what's one byte more or less? */ log_fatal ("Notation data doesn't fit in the packet.\n"); iobuf_close (in); /* NUL terminate it. */ data[prefix + data_len] = 0; if (p_free) xfree (p); p = data; p_free = 1; data = &p[prefix]; if (is_blob) p[prefix - 1] = 0; } else if (is_blob) { data = strchr (p, '='); if (! data) { data = p; data_len = 0; } else { p = xstrdup (p); p_free = 1; data = strchr (p, '='); log_assert (data); /* NUL terminate the name. */ *data = 0; data ++; data_len = strlen (data); } } if (is_blob) notation = blob_to_notation (p, data, data_len); else notation = string_to_notation (p, 1); if (! notation) log_fatal ("creating notation: an unknown error occurred.\n"); notation->next = si->notations; si->notations = notation; if (p_free) xfree (p); return 1; } static int sig_big_endian_arg (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *p = argv[0]; int i; int l; char *bytes; if (argc == 0) log_fatal ("Usage: %s HEXDIGITS\n", option); /* Skip a leading "0x". */ if (p[0] == '0' && p[1] == 'x') p += 2; for (i = 0; i < strlen (p); i ++) if (!hexdigitp (&p[i])) log_fatal ("%s: argument ('%s') must consist of hex digits.\n", option, p); if (strlen (p) % 2 != 0) log_fatal ("%s: argument ('%s') must contain an even number of hex digits.\n", option, p); l = strlen (p) / 2; bytes = xmalloc (l); hex2bin (p, bytes, l); if (strcmp (option, "--key-server-preferences") == 0) { if (si->key_server_preferences) log_fatal ("%s given multiple times.\n", option); si->key_server_preferences = bytes; si->key_server_preferences_len = l; } else if (strcmp (option, "--key-flags") == 0) { if (si->key_flags) log_fatal ("%s given multiple times.\n", option); si->key_flags = bytes; si->key_flags_len = l; } else if (strcmp (option, "--features") == 0) { if (si->features) log_fatal ("%s given multiple times.\n", option); si->features = bytes; si->features_len = l; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_reason_for_revocation (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int v; char *tail; if (argc < 2) log_fatal ("Usage: %s REASON_CODE REASON_STRING\n", option); errno = 0; v = strtol (argv[0], &tail, 16); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("%s: Invalid reason code (%s). Expected 0-255\n", option, argv[0]); if (si->reason_for_revocation) log_fatal ("%s given multiple times.\n", option); si->reason_for_revocation_code = v; si->reason_for_revocation = xstrdup (argv[1]); return 2; } static int sig_corrupt (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; (void) option; (void) argc; (void) argv; (void) cookie; si->corrupt = 1; return 0; } static struct option sig_options[] = { { "--issuer", sig_issuer, "The key to use to generate the signature."}, { "--issuer-keyid", sig_issuer_keyid, "Set the issuer's key id. This is useful for creating a " "self-signature. As a special case, the value \"self\" refers " "to the primary key's key id. " "(RFC 4880, Section 5.2.3.5)" }, { "--pk", sig_pk, "The primary keyas an index into the components (keys and uids) " "created so far where the first component has the index 0." }, { "--sk", sig_pk, "The subkey as an index into the components (keys and uids) created " "so far where the first component has the index 0. Only needed for " "0x18, 0x19, and 0x28 signatures." }, { "--user-id", sig_user_id, "The user id as an index into the components (keys and uids) created " "so far where the first component has the index 0. Only needed for " "0x10-0x13 and 0x30 signatures." }, { "--class", sig_class, "The signature's class. Valid values are " "0x10-0x13 (user id and primary-key certification), " "0x18 (subkey binding), " "0x19 (primary key binding), " "0x1f (direct primary key signature), " "0x20 (key revocation), " "0x28 (subkey revocation), and " "0x30 (certification revocation)." }, { "--digest", sig_digest, "The digest algorithm" }, { "--timestamp", sig_timestamp, "The signature's creation time. " TIMESTAMP_HELP " 0 means now. " "(RFC 4880, Section 5.2.3.4)" }, { "--key-expiration", sig_expiration, "The number of days until the associated key expires. To specify " "seconds, prefix the value with \"seconds=\". It is also possible " "to use 'y', 'm' and 'w' as simple multipliers. For instance, 2y " "means 2 years, etc. " "(RFC 4880, Section 5.2.3.6)" }, { "--cipher-algos", sig_int_list, "A comma separated list of the preferred cipher algorithms (identified by " "their number, see RFC 4880, Section 9). " "(RFC 4880, Section 5.2.3.7)" }, { "--digest-algos", sig_int_list, "A comma separated list of the preferred algorithms (identified by " "their number, see RFC 4880, Section 9). " "(RFC 4880, Section 5.2.3.8)" }, { "--compress-algos", sig_int_list, "A comma separated list of the preferred algorithms (identified by " "their number, see RFC 4880, Section 9)." "(RFC 4880, Section 5.2.3.9)" }, { "--expiration", sig_expiration, "The number of days until the signature expires. To specify seconds, " "prefix the value with \"seconds=\". It is also possible to use 'y', " "'m' and 'w' as simple multipliers. For instance, 2y means 2 years, " "etc. " "(RFC 4880, Section 5.2.3.10)" }, { "--exportable", sig_flag, "Mark this signature as exportable (1) or local (0). " "(RFC 4880, Section 5.2.3.11)" }, { "--revocable", sig_flag, "Mark this signature as revocable (1, revocations are ignored) " "or non-revocable (0). " "(RFC 4880, Section 5.2.3.12)" }, { "--trust-level", sig_trust_level, "Set the trust level. This takes two integer arguments (0-255): " "the trusted-introducer level and the degree of trust. " "(RFC 4880, Section 5.2.3.13.)" }, { "--trust-scope", sig_string_arg, "A regular expression that limits the scope of --trust-level. " "(RFC 4880, Section 5.2.3.14.)" }, { "--revocation-key", sig_revocation_key, "Specify a designated revoker. Takes two arguments: the class " "(normally 0x80 or 0xC0 (sensitive)) and the key id of the " "designatured revoker. May be given multiple times. " "(RFC 4880, Section 5.2.3.15)" }, { "--notation", sig_notation, "Add a human-readable notation of the form \"[!<]name=value\" where " "\"!\" means that the critical flag should be set and \"<\" means " "that VALUE is a file to read the data from. " "(RFC 4880, Section 5.2.3.16)" }, { "--notation-binary", sig_notation, "Add a binary notation of the form \"[!<]name=value\" where " "\"!\" means that the critical flag should be set and \"<\" means " "that VALUE is a file to read the data from. " "(RFC 4880, Section 5.2.3.16)" }, { "--key-server-preferences", sig_big_endian_arg, "Big-endian number encoding the keyserver preferences. " "(RFC 4880, Section 5.2.3.17)" }, { "--key-server", sig_string_arg, "The preferred keyserver. (RFC 4880, Section 5.2.3.18)" }, { "--primary-user-id", sig_flag, "Sets the primary user id flag. (RFC 4880, Section 5.2.3.19)" }, { "--policy-uri", sig_string_arg, "URI of a document that describes the issuer's signing policy. " "(RFC 4880, Section 5.2.3.20)" }, { "--key-flags", sig_big_endian_arg, "Big-endian number encoding the key flags. " "(RFC 4880, Section 5.2.3.21)" }, { "--signers-user-id", sig_string_arg, "The user id (as a string) responsible for the signing. " "(RFC 4880, Section 5.2.3.22)" }, { "--reason-for-revocation", sig_reason_for_revocation, "Takes two arguments: a reason for revocation code and a " "user-provided string. " "(RFC 4880, Section 5.2.3.23)" }, { "--features", sig_big_endian_arg, "Big-endian number encoding the feature flags. " "(RFC 4880, Section 5.2.3.24)" }, { "--signature-target", NULL, "Takes three arguments: the target signature's public key algorithm " " (as an integer), the hash algorithm (as an integer) and the hash " " (as a hexadecimal string). " "(RFC 4880, Section 5.2.3.25)" }, { "--embedded-signature", NULL, "An embedded signature. This must be immediately followed by a " "signature packet (created using --signature ...) or a filename " "containing the packet." "(RFC 4880, Section 5.2.3.26)" }, { "--hashed", NULL, "The following attributes will be placed in the hashed area of " "the signature. (This is the default and it reset at the end of" "each signature.)" }, { "--unhashed", NULL, "The following attributes will be placed in the unhashed area of " "the signature (and thus not integrity protected)." }, { "--corrupt", sig_corrupt, "Corrupt the signature." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --public-key $KEYID --user-id USERID \\\n" " --signature --class 0x10 --issuer $KEYID --issuer-keyid self \\\n" " | " GPG_NAME " --list-packets"} }; static int mksubpkt_callback (PKT_signature *sig, void *cookie) { struct signinfo *si = cookie; int i; if (si->key_expiration) { char buf[4]; buf[0] = (si->key_expiration >> 24) & 0xff; buf[1] = (si->key_expiration >> 16) & 0xff; buf[2] = (si->key_expiration >> 8) & 0xff; buf[3] = si->key_expiration & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } if (si->cipher_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, si->cipher_algorithms, si->cipher_algorithms_len); if (si->digest_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, si->digest_algorithms, si->digest_algorithms_len); if (si->compress_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, si->compress_algorithms, si->compress_algorithms_len); if (si->exportable_set) { char buf = si->exportable; build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, &buf, 1); } if (si->trust_level_set) build_sig_subpkt (sig, SIGSUBPKT_TRUST, si->trust_args, sizeof (si->trust_args)); if (si->trust_scope) build_sig_subpkt (sig, SIGSUBPKT_REGEXP, si->trust_scope, strlen (si->trust_scope)); for (i = 0; i < si->nrevocation_keys; i ++) { struct revocation_key *revkey = &si->revocation_key[i]; gpg_error_t err = keygen_add_revkey (sig, revkey); if (err) { u32 keyid[2]; keyid_from_fingerprint (global_ctrl, revkey->fpr, 20, keyid); log_fatal ("adding revocation key %s: %s\n", keystr (keyid), gpg_strerror (err)); } } /* keygen_add_revkey sets revocable=0 so be sure to do this after adding the rev keys. */ if (si->revocable_set) { char buf = si->revocable; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, &buf, 1); } keygen_add_notations (sig, si->notations); if (si->key_server_preferences) build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, si->key_server_preferences, si->key_server_preferences_len); if (si->key_server) build_sig_subpkt (sig, SIGSUBPKT_PREF_KS, si->key_server, strlen (si->key_server)); if (si->primary_user_id_set) { char buf = si->primary_user_id; build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, &buf, 1); } if (si->policy_uri) build_sig_subpkt (sig, SIGSUBPKT_POLICY, si->policy_uri, strlen (si->policy_uri)); if (si->key_flags) build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, si->key_flags, si->key_flags_len); if (si->signers_user_id) build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, si->signers_user_id, strlen (si->signers_user_id)); if (si->reason_for_revocation) { int len = 1 + strlen (si->reason_for_revocation); char *buf; buf = xmalloc (len); buf[0] = si->reason_for_revocation_code; memcpy (&buf[1], si->reason_for_revocation, len - 1); build_sig_subpkt (sig, SIGSUBPKT_REVOC_REASON, buf, len); xfree (buf); } if (si->features) build_sig_subpkt (sig, SIGSUBPKT_FEATURES, si->features, si->features_len); return 0; } static int signature (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; iobuf_t out = cookie; struct signinfo si; int processed; PKT_public_key *pk; PKT_signature *sig; PACKET pkt; u32 keyid_orig[2], keyid[2]; (void) option; memset (&si, 0, sizeof (si)); memset (&pkt, 0, sizeof (pkt)); processed = process_options (option, major_options, sig_options, &si, global_options, NULL, argc, argv); if (ncomponents) { int pkttype = components[ncomponents - 1].pkttype; if (pkttype == PKT_PUBLIC_KEY) { if (! si.class) /* Direct key sig. */ si.class = 0x1F; } else if (pkttype == PKT_PUBLIC_SUBKEY) { if (! si.sk) si.sk = components[ncomponents - 1].pkt.public_key; if (! si.class) /* Subkey binding sig. */ si.class = 0x18; } else if (pkttype == PKT_USER_ID) { if (! si.uid) si.uid = components[ncomponents - 1].pkt.user_id; if (! si.class) /* Certification of a user id and public key packet. */ si.class = 0x10; } } pk = NULL; if (! si.pk || ! si.issuer_pk) /* No primary key specified. Default to the first one that we find. */ { int i; for (i = 0; i < ncomponents; i ++) if (components[i].pkttype == PKT_PUBLIC_KEY) { pk = components[i].pkt.public_key; break; } } if (! si.pk) { if (! pk) log_fatal ("%s: no primary key given and no primary key available", "--pk"); si.pk = pk; } if (! si.issuer_pk) { if (! pk) log_fatal ("%s: no issuer key given and no primary key available", "--issuer"); si.issuer_pk = pk; } if (si.class == 0x18 || si.class == 0x19 || si.class == 0x28) /* Requires the primary key and a subkey. */ { if (! si.sk) log_fatal ("sig class 0x%x requires a subkey (--sk)\n", si.class); } else if (si.class == 0x10 || si.class == 0x11 || si.class == 0x12 || si.class == 0x13 || si.class == 0x30) /* Requires the primary key and a user id. */ { if (! si.uid) log_fatal ("sig class 0x%x requires a uid (--uid)\n", si.class); } else if (si.class == 0x1F || si.class == 0x20) /* Just requires the primary key. */ ; else log_fatal ("Unsupported signature class: 0x%x\n", si.class); sig = xmalloc_clear (sizeof (*sig)); /* Save SI.ISSUER_PK->KEYID. */ keyid_copy (keyid_orig, pk_keyid (si.issuer_pk)); if (si.issuer_keyid[0] || si.issuer_keyid[1]) keyid_copy (si.issuer_pk->keyid, si.issuer_keyid); else if (si.issuer_keyid_self) { PKT_public_key *pripk = primary_key(); if (! pripk) log_fatal ("--issuer-keyid self given, but no primary key available.\n"); keyid_copy (si.issuer_pk->keyid, pk_keyid (pripk)); } /* Changing the issuer's key id is fragile. Check to make sure make_keysig_packet didn't recompute the keyid. */ keyid_copy (keyid, si.issuer_pk->keyid); err = make_keysig_packet (global_ctrl, &sig, si.pk, si.uid, si.sk, si.issuer_pk, si.class, si.digest_algo, si.timestamp, si.expiration, mksubpkt_callback, &si, NULL); log_assert (keyid_cmp (keyid, si.issuer_pk->keyid) == 0); if (err) log_fatal ("Generating signature: %s\n", gpg_strerror (err)); /* Restore SI.PK->KEYID. */ keyid_copy (si.issuer_pk->keyid, keyid_orig); if (si.corrupt) { /* Set the top 32-bits to 0xBAD0DEAD. */ int bits = gcry_mpi_get_nbits (sig->data[0]); gcry_mpi_t x = gcry_mpi_new (0); gcry_mpi_add_ui (x, x, 0xBAD0DEAD); gcry_mpi_lshift (x, x, bits > 32 ? bits - 32 : bits); gcry_mpi_clear_highbit (sig->data[0], bits > 32 ? bits - 32 : 0); gcry_mpi_add (sig->data[0], sig->data[0], x); gcry_mpi_release (x); } pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; err = build_packet (out, &pkt); if (err) log_fatal ("serializing public key packet: %s\n", gpg_strerror (err)); debug ("Wrote signature packet:\n"); dump_component (&pkt); xfree (sig); release_kbnode (si.issuer_kb); xfree (si.revocation_key); return processed; } struct sk_esk_info { /* The cipher used for encrypting the session key (when a session key is used). */ int cipher; /* The cipher used for encryping the SED packet. */ int sed_cipher; /* S2K related data. */ int hash; int mode; int mode_set; byte salt[8]; int salt_set; int iterations; /* If applying the S2K function to the passphrase is the session key or if it is the decryption key for the session key. */ int s2k_is_session_key; /* Generate a new, random session key. */ int new_session_key; /* The unencrypted session key. */ int session_key_len; char *session_key; char *password; }; static int sk_esk_cipher (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|IDEA|3DES|CAST5|BLOWFISH|AES|AES192|AES256|CAMELLIA128|CAMELLIA192|CAMELLIA256"; int cipher; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (strcasecmp (argv[0], "IDEA") == 0) cipher = CIPHER_ALGO_IDEA; else if (strcasecmp (argv[0], "3DES") == 0) cipher = CIPHER_ALGO_3DES; else if (strcasecmp (argv[0], "CAST5") == 0) cipher = CIPHER_ALGO_CAST5; else if (strcasecmp (argv[0], "BLOWFISH") == 0) cipher = CIPHER_ALGO_BLOWFISH; else if (strcasecmp (argv[0], "AES") == 0) cipher = CIPHER_ALGO_AES; else if (strcasecmp (argv[0], "AES192") == 0) cipher = CIPHER_ALGO_AES192; else if (strcasecmp (argv[0], "TWOFISH") == 0) cipher = CIPHER_ALGO_TWOFISH; else if (strcasecmp (argv[0], "CAMELLIA128") == 0) cipher = CIPHER_ALGO_CAMELLIA128; else if (strcasecmp (argv[0], "CAMELLIA192") == 0) cipher = CIPHER_ALGO_CAMELLIA192; else if (strcasecmp (argv[0], "CAMELLIA256") == 0) cipher = CIPHER_ALGO_CAMELLIA256; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! valid_cipher (v)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); cipher = v; } if (strcmp (option, "--cipher") == 0) { if (si->cipher) log_fatal ("%s given multiple times.", option); si->cipher = cipher; } else if (strcmp (option, "--sed-cipher") == 0) { if (si->sed_cipher) log_fatal ("%s given multiple times.", option); si->sed_cipher = cipher; } return 1; } static int sk_esk_mode (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|simple|salted|iterated"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->mode) log_fatal ("%s given multiple times.", option); if (strcasecmp (argv[0], "simple") == 0) si->mode = 0; else if (strcasecmp (argv[0], "salted") == 0) si->mode = 1; else if (strcasecmp (argv[0], "iterated") == 0) si->mode = 3; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! (v == 0 || v == 1 || v == 3)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); si->mode = v; } si->mode_set = 1; return 1; } static int sk_esk_hash_algorithm (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|MD5|SHA1|RMD160|SHA256|SHA384|SHA512|SHA224"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->hash) log_fatal ("%s given multiple times.", option); if (strcasecmp (argv[0], "MD5") == 0) si->hash = DIGEST_ALGO_MD5; else if (strcasecmp (argv[0], "SHA1") == 0) si->hash = DIGEST_ALGO_SHA1; else if (strcasecmp (argv[0], "RMD160") == 0) si->hash = DIGEST_ALGO_RMD160; else if (strcasecmp (argv[0], "SHA256") == 0) si->hash = DIGEST_ALGO_SHA256; else if (strcasecmp (argv[0], "SHA384") == 0) si->hash = DIGEST_ALGO_SHA384; else if (strcasecmp (argv[0], "SHA512") == 0) si->hash = DIGEST_ALGO_SHA512; else if (strcasecmp (argv[0], "SHA224") == 0) si->hash = DIGEST_ALGO_SHA224; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! (v == DIGEST_ALGO_MD5 || v == DIGEST_ALGO_SHA1 || v == DIGEST_ALGO_RMD160 || v == DIGEST_ALGO_SHA256 || v == DIGEST_ALGO_SHA384 || v == DIGEST_ALGO_SHA512 || v == DIGEST_ALGO_SHA224)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); si->hash = v; } return 1; } static int sk_esk_salt (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "16-HEX-CHARACTERS"; char *p = argv[0]; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->salt_set) log_fatal ("%s given multiple times.", option); if (p[0] == '0' && p[1] == 'x') p += 2; if (strlen (p) != 16) log_fatal ("%s: Salt must be exactly 16 hexadecimal characters (have: %zd)\n", option, strlen (p)); if (hex2bin (p, si->salt, sizeof (si->salt)) == -1) log_fatal ("%s: Salt must only contain hexadecimal characters\n", option); si->salt_set = 1; return 1; } static int sk_esk_iterations (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "ITERATION-COUNT"; char *tail; int v; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || v < 0) log_fatal ("%s: Non-negative integer expected.\n", option); si->iterations = v; return 1; } static int sk_esk_session_key (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "HEX-CHARACTERS|auto|none"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->session_key || si->s2k_is_session_key || si->new_session_key) log_fatal ("%s given multiple times.", option); if (strcasecmp (p, "none") == 0) { si->s2k_is_session_key = 1; return 1; } if (strcasecmp (p, "new") == 0) { si->new_session_key = 1; return 1; } if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 0); if (si->session_key) log_fatal ("%s given multiple times.", option); if (sk.algo) si->sed_cipher = sk.algo; si->session_key_len = sk.keylen; si->session_key = sk.key; return 1; } static int sk_esk_password (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "PASSWORD"; if (argc == 0) log_fatal ("Usage: --sk-esk %s\n", usage); if (si->password) log_fatal ("%s given multiple times.", option); si->password = xstrdup (argv[0]); return 1; } static struct option sk_esk_options[] = { { "--cipher", sk_esk_cipher, "The encryption algorithm for encrypting the session key. " "One of IDEA, 3DES, CAST5, BLOWFISH, AES (default), AES192, " "AES256, TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." }, { "--sed-cipher", sk_esk_cipher, "The encryption algorithm for encrypting the SED packet. " "One of IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, " "AES256 (default), TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." }, { "--mode", sk_esk_mode, "The S2K mode. Either one of the strings \"simple\", \"salted\" " "or \"iterated\" or an integer." }, { "--hash", sk_esk_hash_algorithm, "The hash algorithm to used to derive the key. One of " "MD5, SHA1 (default), RMD160, SHA256, SHA384, SHA512, or SHA224." }, { "--salt", sk_esk_salt, "The S2K salt encoded as 16 hexadecimal characters. One needed " "if the S2K function is in salted or iterated mode." }, { "--iterations", sk_esk_iterations, "The iteration count. If not provided, a reasonable value is chosen. " "Note: due to the encoding scheme, not every value is valid. For " "convenience, the provided value will be rounded appropriately. " "Only needed if the S2K function is in iterated mode." }, { "--session-key", sk_esk_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is \"new\", then a new session key is generated." "If this is \"auto\", then either the last session key is " "used, if the was none, one is generated. If this is \"none\", then " "the session key is the result of applying the S2K algorithms to the " "password. The session key may be prefaced with an integer and a colon " "to indicate the cipher to use for the SED packet (making --sed-cipher " "unnecessary and allowing the direct use of the result of " "\"" GPG_NAME " --show-session-key\")." }, { "", sk_esk_password, "The password." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --sk-esk foobar --encrypted \\\n" " --literal --value foo | " GPG_NAME " --list-packets" } }; static int sk_esk (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct sk_esk_info si; DEK sesdek; DEK s2kdek; PKT_symkey_enc *ske; PACKET pkt; memset (&si, 0, sizeof (si)); processed = process_options (option, major_options, sk_esk_options, &si, global_options, NULL, argc, argv); if (! si.password) log_fatal ("%s: missing password. Usage: %s PASSWORD", option, option); /* Fill in defaults, if appropriate. */ if (! si.cipher) si.cipher = CIPHER_ALGO_AES; if (! si.sed_cipher) si.sed_cipher = CIPHER_ALGO_AES256; if (! si.hash) si.hash = DIGEST_ALGO_SHA1; if (! si.mode_set) /* Salted and iterated. */ si.mode = 3; if (si.mode != 0 && ! si.salt_set) /* Generate a salt. */ gcry_randomize (si.salt, 8, GCRY_STRONG_RANDOM); if (si.mode == 0) { if (si.iterations) log_info ("%s: --iterations provided, but not used for mode=0\n", option); si.iterations = 0; } else if (! si.iterations) si.iterations = 10000; memset (&sesdek, 0, sizeof (sesdek)); /* The session key is used to encrypt the SED packet. */ sesdek.algo = si.sed_cipher; if (si.session_key) /* Copy the unencrypted session key into SESDEK. */ { sesdek.keylen = openpgp_cipher_get_algo_keylen (sesdek.algo); if (sesdek.keylen != si.session_key_len) log_fatal ("%s: Cipher algorithm requires a %d byte session key, but provided session key is %d bytes.", option, sesdek.keylen, si.session_key_len); log_assert (sesdek.keylen <= sizeof (sesdek.key)); memcpy (sesdek.key, si.session_key, sesdek.keylen); } else if (! si.s2k_is_session_key || si.new_session_key) /* We need a session key, but one wasn't provided. Generate it. */ make_session_key (&sesdek); /* The encrypted session key needs 1 + SESDEK.KEYLEN bytes of space. */ ske = xmalloc_clear (sizeof (*ske) + sesdek.keylen); ske->version = 4; ske->cipher_algo = si.cipher; ske->s2k.mode = si.mode; ske->s2k.hash_algo = si.hash; log_assert (sizeof (si.salt) == sizeof (ske->s2k.salt)); memcpy (ske->s2k.salt, si.salt, sizeof (ske->s2k.salt)); if (! si.s2k_is_session_key) /* 0 means get the default. */ ske->s2k.count = encode_s2k_iterations (si.iterations); /* Derive the symmetric key that is either the session key or the key used to encrypt the session key. */ memset (&s2kdek, 0, sizeof (s2kdek)); s2kdek.algo = si.cipher; s2kdek.keylen = openpgp_cipher_get_algo_keylen (s2kdek.algo); err = gcry_kdf_derive (si.password, strlen (si.password), ske->s2k.mode == 3 ? GCRY_KDF_ITERSALTED_S2K : ske->s2k.mode == 1 ? GCRY_KDF_SALTED_S2K : GCRY_KDF_SIMPLE_S2K, ske->s2k.hash_algo, ske->s2k.salt, 8, S2K_DECODE_COUNT (ske->s2k.count), /* The size of the desired key and its buffer. */ s2kdek.keylen, s2kdek.key); if (err) log_fatal ("gcry_kdf_derive failed: %s", gpg_strerror (err)); if (si.s2k_is_session_key) { ske->seskeylen = 0; session_key = s2kdek; } else /* Encrypt the session key using the s2k specifier. */ { DEK *sesdekp = &sesdek; /* Now encrypt the session key (or rather, the algorithm used to encrypt the SED plus the session key) using ENCKEY. */ ske->seskeylen = 1 + sesdek.keylen; encrypt_seskey (&s2kdek, &sesdekp, ske->seskey); /* Save the session key for later. */ session_key = sesdek; } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = ske; err = build_packet (out, &pkt); if (err) log_fatal ("Serializing sym-key encrypted packet: %s\n", gpg_strerror (err)); debug ("Wrote sym-key encrypted packet:\n"); dump_component (&pkt); xfree (si.session_key); xfree (si.password); xfree (ske); return processed; } struct pk_esk_info { int session_key_set; int new_session_key; int sed_cipher; int session_key_len; char *session_key; int throw_keyid; char *keyid; }; static int pk_esk_session_key (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; char *usage = "HEX-CHARACTERS|auto|none"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (pi->session_key_set) log_fatal ("%s given multiple times.", option); pi->session_key_set = 1; if (strcasecmp (p, "new") == 0) { pi->new_session_key = 1; return 1; } if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 0); if (pi->session_key) log_fatal ("%s given multiple times.", option); if (sk.algo) pi->sed_cipher = sk.algo; pi->session_key_len = sk.keylen; pi->session_key = sk.key; return 1; } static int pk_esk_throw_keyid (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; (void) option; (void) argc; (void) argv; pi->throw_keyid = 1; return 0; } static int pk_esk_keyid (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; char *usage = "KEYID"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (pi->keyid) log_fatal ("Multiple key ids given, but only one is allowed."); pi->keyid = xstrdup (argv[0]); return 1; } static struct option pk_esk_options[] = { { "--session-key", pk_esk_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is not given or is \"auto\", then the current " "session key is used. If there is no session key or this is \"new\", " "then a new session key is generated. The session key may be " "prefaced with an integer and a colon to indicate the cipher to use " "for the SED packet (making --sed-cipher unnecessary and allowing the " "direct use of the result of \"" GPG_NAME " --show-session-key\")." }, { "--throw-keyid", pk_esk_throw_keyid, "Throw the keyid." }, { "", pk_esk_keyid, "The key id." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --pk-esk $KEYID --encrypted --literal --value foo \\\n" " | " GPG_NAME " --list-packets"} }; static int pk_esk (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct pk_esk_info pi; PKT_public_key pk; memset (&pi, 0, sizeof (pi)); processed = process_options (option, major_options, pk_esk_options, &pi, global_options, NULL, argc, argv); if (! pi.keyid) log_fatal ("%s: missing keyid. Usage: %s KEYID", option, option); memset (&pk, 0, sizeof (pk)); pk.req_usage = PUBKEY_USAGE_ENC; err = get_pubkey_byname (NULL, NULL, &pk, pi.keyid, NULL, NULL, 1, 1); if (err) log_fatal ("%s: looking up key %s: %s\n", option, pi.keyid, gpg_strerror (err)); if (pi.sed_cipher) /* Have a session key. */ { session_key.algo = pi.sed_cipher; session_key.keylen = pi.session_key_len; log_assert (session_key.keylen <= sizeof (session_key.key)); memcpy (session_key.key, pi.session_key, session_key.keylen); } if (pi.new_session_key || ! session_key.algo) { if (! pi.new_session_key) /* Default to AES256. */ session_key.algo = CIPHER_ALGO_AES256; make_session_key (&session_key); } err = write_pubkey_enc (global_ctrl, &pk, pi.throw_keyid, &session_key, out); if (err) log_fatal ("%s: writing pk_esk packet for %s: %s\n", option, pi.keyid, gpg_strerror (err)); debug ("Wrote pk_esk packet for %s\n", pi.keyid); xfree (pi.keyid); xfree (pi.session_key); return processed; } struct encinfo { int saw_session_key; }; static int encrypted_session_key (const char *option, int argc, char *argv[], void *cookie) { struct encinfo *ei = cookie; char *usage = "HEX-CHARACTERS|auto"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (ei->saw_session_key) log_fatal ("%s given multiple times.", option); ei->saw_session_key = 1; if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 1); session_key.algo = sk.algo; log_assert (sk.keylen <= sizeof (session_key.key)); memcpy (session_key.key, sk.key, sk.keylen); xfree (sk.key); return 1; } static struct option encrypted_options[] = { { "--session-key", encrypted_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is not given or is \"auto\", then the last session key " "is used. If there was none, then an error is raised. The session key " "must be prefaced with an integer and a colon to indicate the cipher " "to use (this is format used by \"" GPG_NAME " --show-session-key\")." }, { NULL, NULL, "After creating the packet, this command clears the current " "session key.\n\n" "Example: nested encryption packets:\n\n" " $ gpgcompose --sk-esk foo --encrypted-mdc \\\n" " --sk-esk bar --encrypted-mdc \\\n" " --literal --value 123 --encrypted-pop --encrypted-pop | " GPG_NAME" -d" } }; static int encrypted (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; int processed; struct encinfo ei; PKT_encrypted e; cipher_filter_context_t *cfx; memset (&ei, 0, sizeof (ei)); processed = process_options (option, major_options, encrypted_options, &ei, global_options, NULL, argc, argv); if (! session_key.algo) log_fatal ("%s: no session key configured\n" " (use e.g. --sk-esk PASSWORD or --pk-esk KEYID).\n", option); memset (&e, 0, sizeof (e)); /* We only need to set E->LEN, E->EXTRALEN (if E->LEN is not 0), and E->NEW_CTB. */ e.len = 0; e.new_ctb = 1; /* Register the cipher filter. */ cfx = xmalloc_clear (sizeof (*cfx)); /* Copy the session key. */ cfx->dek = xmalloc (sizeof (*cfx->dek)); *cfx->dek = session_key; if (do_debug) { char *buf; buf = xmalloc (2 * session_key.keylen + 1); debug ("session key: algo: %d; keylen: %d; key: %s\n", session_key.algo, session_key.keylen, bin2hex (session_key.key, session_key.keylen, buf)); xfree (buf); } if (strcmp (option, "--encrypted-mdc") == 0) cfx->dek->use_mdc = 1; else if (strcmp (option, "--encrypted") == 0) cfx->dek->use_mdc = 0; else log_fatal ("%s: option not handled by this function!\n", option); cfx->datalen = 0; filter_push (out, cipher_filter, cfx, PKT_ENCRYPTED, cfx->datalen == 0); debug ("Wrote encrypted packet:\n"); /* Clear the current session key. */ memset (&session_key, 0, sizeof (session_key)); return processed; } static struct option encrypted_pop_options[] = { { NULL, NULL, "Example:\n\n" " $ gpgcompose --sk-esk PASSWORD \\\n" " --encrypted-mdc \\\n" " --literal --value foo \\\n" " --encrypted-pop | " GPG_NAME " --list-packets" } }; static int encrypted_pop (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; int processed; processed = process_options (option, major_options, encrypted_pop_options, NULL, global_options, NULL, argc, argv); /* We only support a single option, --help, which causes the program * to exit. */ log_assert (processed == 0); filter_pop (out, PKT_ENCRYPTED); debug ("Popped encryption container.\n"); return processed; } struct data { int file; union { char *data; char *filename; }; struct data *next; }; /* This must be the first member of the struct to be able to use add_value! */ struct datahead { struct data *head; struct data **last_next; }; static int add_value (const char *option, int argc, char *argv[], void *cookie) { struct datahead *dh = cookie; struct data *d = xmalloc_clear (sizeof (struct data)); d->file = strcmp ("--file", option) == 0; if (! d->file) log_assert (strcmp ("--value", option) == 0); if (argc == 0) { if (d->file) log_fatal ("Usage: %s FILENAME\n", option); else log_fatal ("Usage: %s STRING\n", option); } if (! dh->last_next) /* First time through. Initialize DH->LAST_NEXT. */ { log_assert (! dh->head); dh->last_next = &dh->head; } if (d->file) d->filename = argv[0]; else d->data = argv[0]; /* Append it. */ *dh->last_next = d; dh->last_next = &d->next; return 1; } struct litinfo { /* This must be the first element for add_value to work! */ struct datahead data; int timestamp_set; u32 timestamp; char mode; int partial_body_length_encoding; char *name; }; static int literal_timestamp (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; li->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); li->timestamp_set = 1; return 1; } static int literal_mode (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; if (argc == 0 || ! (strcmp (argv[0], "b") == 0 || strcmp (argv[0], "t") == 0 || strcmp (argv[0], "u") == 0)) log_fatal ("Usage: %s [btu]\n", option); li->mode = argv[0][0]; return 1; } static int literal_partial_body_length (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; char *tail; int v; int range[2] = {0, 1}; if (argc <= 1) log_fatal ("Usage: %s [0|1]\n", option); errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1])) log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n", option, argv[0], range[0], range[1]); li->partial_body_length_encoding = v; return 1; } static int literal_name (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; if (argc <= 1) log_fatal ("Usage: %s NAME\n", option); if (strlen (argv[0]) > 255) log_fatal ("%s: name is too long (%zd > 255 characters).\n", option, strlen (argv[0])); li->name = argv[0]; return 1; } static struct option literal_options[] = { { "--value", add_value, "A string to store in the literal packet." }, { "--file", add_value, "A file to copy into the literal packet." }, { "--timestamp", literal_timestamp, "The literal packet's time stamp. This defaults to the current time." }, { "--mode", literal_mode, "The content's mode (normally 'b' (default), 't' or 'u')." }, { "--partial-body-length", literal_partial_body_length, "Force partial body length encoding." }, { "--name", literal_name, "The literal's name." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --literal --value foobar | " GPG_NAME " -d"} }; static int literal (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct litinfo li; PKT_plaintext *pt; PACKET pkt; struct data *data; memset (&li, 0, sizeof (li)); processed = process_options (option, major_options, literal_options, &li, global_options, NULL, argc, argv); if (! li.data.head) log_fatal ("%s: no data provided (use --value or --file)", option); pt = xmalloc_clear (sizeof (*pt) + (li.name ? strlen (li.name) : 0)); pt->new_ctb = 1; if (li.timestamp_set) pt->timestamp = li.timestamp; else /* Default to the current time. */ pt->timestamp = make_timestamp (); pt->mode = li.mode; if (! pt->mode) /* Default to binary. */ pt->mode = 'b'; if (li.name) { strcpy (pt->name, li.name); pt->namelen = strlen (pt->name); } pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; if (! li.partial_body_length_encoding) /* Compute the amount of data. */ { pt->len = 0; for (data = li.data.head; data; data = data->next) { if (data->file) { iobuf_t in; int overflow; off_t off; in = iobuf_open (data->filename); if (! in) /* An error opening the file. We do error handling below so just break here. */ { pt->len = 0; break; } off = iobuf_get_filelength (in, &overflow); iobuf_close (in); if (overflow || off == 0) /* Length is unknown or there was an error (unfortunately, iobuf_get_filelength doesn't distinguish between 0 length files and an error!). Fall back to partial body mode. */ { pt->len = 0; break; } pt->len += off; } else pt->len += strlen (data->data); } } err = build_packet (out, &pkt); if (err) log_fatal ("Serializing literal packet: %s\n", gpg_strerror (err)); /* Write out the data. */ for (data = li.data.head; data; data = data->next) { if (data->file) { iobuf_t in; errno = 0; in = iobuf_open (data->filename); if (! in) log_fatal ("Opening '%s': %s\n", data->filename, errno ? strerror (errno): "unknown error"); iobuf_copy (out, in); if (iobuf_error (in)) log_fatal ("Reading from %s: %s\n", data->filename, gpg_strerror (iobuf_error (in))); if (iobuf_error (out)) log_fatal ("Writing literal data from %s: %s\n", data->filename, gpg_strerror (iobuf_error (out))); iobuf_close (in); } else { err = iobuf_write (out, data->data, strlen (data->data)); if (err) log_fatal ("Writing literal data: %s\n", gpg_strerror (err)); } } if (! pt->len) { /* Disable partial body length mode. */ log_assert (pt->new_ctb == 1); iobuf_set_partial_body_length_mode (out, 0); } debug ("Wrote literal packet:\n"); dump_component (&pkt); while (li.data.head) { data = li.data.head->next; xfree (li.data.head); li.data.head = data; } xfree (pt); return processed; } static int copy_file (const char *option, int argc, char *argv[], void *cookie) { char **filep = cookie; if (argc == 0) log_fatal ("Usage: %s FILENAME\n", option); *filep = argv[0]; return 1; } static struct option copy_options[] = { { "", copy_file, "Copy the specified file to stdout." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --copy /etc/hostname\n\n" "This is particularly useful when combined with gpgsplit." } }; static int copy (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; char *file = NULL; iobuf_t in; int processed; processed = process_options (option, major_options, copy_options, &file, global_options, NULL, argc, argv); if (! file) log_fatal ("Usage: %s FILE\n", option); errno = 0; in = iobuf_open (file); if (! in) log_fatal ("Error opening %s: %s.\n", file, errno ? strerror (errno): "unknown error"); iobuf_copy (out, in); if (iobuf_error (out)) log_fatal ("Copying data to destination: %s\n", gpg_strerror (iobuf_error (out))); if (iobuf_error (in)) log_fatal ("Reading data from %s: %s\n", argv[0], gpg_strerror (iobuf_error (in))); iobuf_close (in); return processed; } int main (int argc, char *argv[]) { const char *filename = "-"; iobuf_t out; int preprocessed = 1; int processed; ctrl_t ctrl; opt.ignore_time_conflict = 1; /* Allow notations in the IETF space, for instance. */ opt.expert = 1; global_ctrl = ctrl = xcalloc (1, sizeof *ctrl); keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, KEYDB_RESOURCE_FLAG_DEFAULT); if (argc == 1) /* Nothing to do. */ return 0; if (strcmp (argv[1], "--output") == 0 || strcmp (argv[1], "-o") == 0) { filename = argv[2]; log_info ("Writing to %s\n", filename); preprocessed += 2; } out = iobuf_create (filename, 0); if (! out) log_fatal ("Failed to open stdout for writing\n"); processed = process_options (NULL, NULL, major_options, out, global_options, NULL, argc - preprocessed, &argv[preprocessed]); if (processed != argc - preprocessed) log_fatal ("Didn't process %d options.\n", argc - preprocessed - processed); iobuf_close (out); return 0; } /* Stubs duplicated from gpg.c. */ int g10_errors_seen = 0; /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) { gcry_control (GCRYCTL_TERM_SECMEM ); } void g10_exit( int rc ) { gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; exit (rc); } void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check) { (void) ctrl; (void) username; (void) locusr; (void) commands; (void) quiet; (void) seckey_check; } void show_basic_key_info (ctrl_t ctrl, KBNODE keyblock) { (void)ctrl; (void) keyblock; } int -keyedit_print_one_sig (ctrl_t ctrl, int rc, kbnode_t keyblock, kbnode_t node, +keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, + int rc, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended) { (void) ctrl; + (void) fp; (void) rc; (void) keyblock; (void) node; (void) inv_sigs; (void) no_key; (void) oth_err; (void) is_selfsig; (void) print_without_key; (void) extended; return 0; } diff --git a/g10/import.c b/g10/import.c index 6e8322587..813662537 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,3708 +1,3708 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998-2007, 2010-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2016, 2017 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 . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "trustdb.h" #include "main.h" #include "../common/i18n.h" #include "../common/ttyio.h" #include "../common/recsel.h" #include "keyserver-internal.h" #include "call-agent.h" #include "../common/membuf.h" #include "../common/init.h" #include "../common/mbox-util.h" #include "key-check.h" struct import_stats_s { ulong count; ulong no_user_id; ulong imported; ulong n_uids; ulong n_sigs; ulong n_subk; ulong unchanged; ulong n_revoc; ulong secret_read; ulong secret_imported; ulong secret_dups; ulong skipped_new_keys; ulong not_imported; ulong n_sigs_cleaned; ulong n_uids_cleaned; ulong v3keys; /* Number of V3 keys seen. */ }; /* Node flag to indicate that a user ID or a subkey has a * valid self-signature. */ #define NODE_GOOD_SELFSIG 1 /* Node flag to indicate that a user ID or subkey has * an invalid self-signature. */ #define NODE_BAD_SELFSIG 2 /* Node flag to indicate that the node shall be deleted. */ #define NODE_DELETION_MARK 4 /* A node flag used to temporary mark a node. */ #define NODE_FLAG_A 8 /* An object and a global instance to store selectors created from * --import-filter keep-uid=EXPR. * --import-filter drop-sig=EXPR. * * FIXME: We should put this into the CTRL object but that requires a * lot more changes right now. For now we use save and restore * function to temporary change them. */ /* Definition of the import filters. */ struct import_filter_s { recsel_expr_t keep_uid; recsel_expr_t drop_sig; }; /* The current instance. */ struct import_filter_s import_filter; static int import (ctrl_t ctrl, IOBUF inp, const char* fname, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); static int read_block (IOBUF a, int with_meta, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys); static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); static int import_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, int origin, const char *url); 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 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, u32 *keyid, unsigned int options); static int any_uid_left (kbnode_t keyblock); static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, u32 curtime, int origin, const char *url, int *n_uids, int *n_sigs, int *n_subk ); static gpg_error_t append_new_uid (unsigned int options, kbnode_t keyblock, kbnode_t node, u32 curtime, int origin, const char *url, int *n_sigs); static int append_key (kbnode_t keyblock, kbnode_t node, int *n_sigs); static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs); static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs); static void release_import_filter (import_filter_t filt) { recsel_release (filt->keep_uid); filt->keep_uid = NULL; recsel_release (filt->drop_sig); filt->drop_sig = NULL; } static void cleanup_import_globals (void) { release_import_filter (&import_filter); } int parse_import_options(char *str,unsigned int *options,int noisy) { struct parse_options import_opts[]= { {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL, N_("import signatures that are marked as local-only")}, {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL, N_("repair damage from the pks keyserver during import")}, {"keep-ownertrust", IMPORT_KEEP_OWNERTTRUST, NULL, N_("do not clear the ownertrust values during import")}, {"fast-import",IMPORT_FAST,NULL, N_("do not update the trustdb after import")}, {"import-show",IMPORT_SHOW,NULL, N_("show key during import")}, {"merge-only",IMPORT_MERGE_ONLY,NULL, N_("only accept updates to existing keys")}, {"import-clean",IMPORT_CLEAN,NULL, N_("remove unusable parts from key after import")}, {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL, N_("remove as much as possible from key after import")}, {"import-export", IMPORT_EXPORT, NULL, N_("run import filters and export key immediately")}, {"restore", IMPORT_RESTORE, NULL, N_("assume the GnuPG key backup format")}, {"import-restore", IMPORT_RESTORE, NULL, NULL}, {"repair-keys", IMPORT_REPAIR_KEYS, NULL, N_("repair keys on import")}, /* Aliases for backward compatibility */ {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL}, {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL}, /* dummy */ {"import-unusable-sigs",0,NULL,NULL}, {"import-clean-sigs",0,NULL,NULL}, {"import-clean-uids",0,NULL,NULL}, {"convert-sk-to-pk",0, NULL,NULL}, /* Not anymore needed due to the new design. */ {NULL,0,NULL,NULL} }; int rc; rc = parse_options (str, options, import_opts, noisy); if (rc && (*options & IMPORT_RESTORE)) { /* Alter other options we want or don't want for restore. */ *options |= (IMPORT_LOCAL_SIGS | IMPORT_KEEP_OWNERTTRUST); *options &= ~(IMPORT_MINIMAL | IMPORT_CLEAN | IMPORT_REPAIR_PKS_SUBKEY_BUG | IMPORT_MERGE_ONLY); } return rc; } /* Parse and set an import filter from string. STRING has the format * "NAME=EXPR" with NAME being the name of the filter. Spaces before * and after NAME are not allowed. If this function is all called * several times all expressions for the same NAME are concatenated. * Supported filter names are: * * - keep-uid :: If the expression evaluates to true for a certain * user ID packet, that packet and all it dependencies * will be imported. The expression may use these * variables: * * - uid :: The entire user ID. * - mbox :: The mail box part of the user ID. * - primary :: Evaluate to true for the primary user ID. */ gpg_error_t parse_and_set_import_filter (const char *string) { gpg_error_t err; /* Auto register the cleanup function. */ register_mem_cleanup_func (cleanup_import_globals); if (!strncmp (string, "keep-uid=", 9)) err = recsel_parse_expr (&import_filter.keep_uid, string+9); else if (!strncmp (string, "drop-sig=", 9)) err = recsel_parse_expr (&import_filter.drop_sig, string+9); else err = gpg_error (GPG_ERR_INV_NAME); return err; } /* Save the current import filters, return them, and clear the current * filters. Returns NULL on error and sets ERRNO. */ import_filter_t save_and_clear_import_filter (void) { import_filter_t filt; filt = xtrycalloc (1, sizeof *filt); if (!filt) return NULL; *filt = import_filter; memset (&import_filter, 0, sizeof import_filter); return filt; } /* Release the current import filters and restore them from NEWFILT. * Ownership of NEWFILT is moved to this function. */ void restore_import_filter (import_filter_t filt) { if (filt) { release_import_filter (&import_filter); import_filter = *filt; xfree (filt); } } import_stats_t import_new_stats_handle (void) { return xmalloc_clear ( sizeof (struct import_stats_s) ); } void import_release_stats_handle (import_stats_t p) { xfree (p); } /* Read a key from a file. Only the first key in the file is * considered and stored at R_KEYBLOCK. FNAME is the name of the * file. */ gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock) { gpg_error_t err; iobuf_t inp; PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; u32 keyid[2]; int v3keys; /* Dummy */ int non_self; /* Dummy */ (void)ctrl; *r_keyblock = NULL; inp = iobuf_open (fname); if (!inp) err = gpg_error_from_syserror (); else if (is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; err = gpg_error (GPG_ERR_EPERM); } else err = 0; if (err) { log_error (_("can't open '%s': %s\n"), iobuf_is_pipe_filename (fname)? "[stdin]": fname, gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } /* Push the armor filter. */ { armor_filter_context_t *afx; afx = new_armor_context (); afx->only_keyblocks = 1; push_armor_filter (afx, inp); release_armor_context (afx); } /* Read the first non-v3 keyblock. */ while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys))) { if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) break; log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); release_kbnode (keyblock); keyblock = NULL; } if (err) { if (gpg_err_code (err) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), iobuf_is_pipe_filename (fname)? "[stdin]": fname, gpg_strerror (err)); goto leave; } keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); if (!find_next_kbnode (keyblock, PKT_USER_ID)) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } collapse_uids (&keyblock); clear_kbnode_flags (keyblock); if (chk_self_sigs (ctrl, keyblock, keyid, &non_self)) { err = gpg_error (GPG_ERR_INV_KEYRING); goto leave; } if (!delete_inv_parts (ctrl, keyblock, keyid, 0) ) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } *r_keyblock = keyblock; keyblock = NULL; leave: if (inp) { iobuf_close (inp); /* Must invalidate that ugly cache to actually close the file. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); } release_kbnode (keyblock); /* FIXME: Do we need to free PENDING_PKT ? */ return err; } /* * Import the public keys from the given filename. Input may be armored. * This function rejects all keys which are not validly self signed on at * least one userid. Only user ids which are self signed will be imported. * Other signatures are not checked. * * Actually this function does a merge. It works like this: * * - get the keyblock * - check self-signatures and remove all userids and their signatures * without/invalid self-signatures. * - reject the keyblock, if we have no valid userid. * - See whether we have this key already in one of our pubrings. * If not, simply add it to the default keyring. * - Compare the key and the self-signatures of the new and the one in * our keyring. If they are different something weird is going on; * ask what to do. * - See whether we have only non-self-signature on one user id; if not * ask the user what to do. * - compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * (consider looking at the timestamp and use the newest?) * - Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * - Proceed with next signature. * * Key revocation certificates have special handling. */ static int import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { int i; int rc = 0; struct import_stats_s *stats = stats_handle; if (!stats) stats = import_new_stats_handle (); if (inp) { rc = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options, screener, screener_arg, origin, url); } else { if (!fnames && !nnames) nnames = 1; /* Ohh what a ugly hack to jump into the loop */ for (i=0; i < nnames; i++) { const char *fname = fnames? fnames[i] : NULL; IOBUF inp2 = iobuf_open(fname); if (!fname) fname = "[stdin]"; if (inp2 && is_secured_file (iobuf_get_fd (inp2))) { iobuf_close (inp2); inp2 = NULL; gpg_err_set_errno (EPERM); } if (!inp2) log_error (_("can't open '%s': %s\n"), fname, strerror (errno)); else { rc = import (ctrl, inp2, fname, stats, fpr, fpr_len, options, screener, screener_arg, origin, url); iobuf_close (inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); if (rc) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (rc) ); } if (!fname) break; } } if (!stats_handle) { import_print_stats (stats); import_release_stats_handle (stats); } /* If no fast import and the trustdb is dirty (i.e. we added a key or userID that had something other than a selfsig, a signature that was other than a selfsig, or any revocation), then update/check the trustdb if the user specified by setting interactive or by not setting no-auto-check-trustdb */ if (!(options & IMPORT_FAST)) check_or_update_trustdb (ctrl); return rc; } void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_handle, unsigned int options, int origin, const char *url) { import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle, NULL, NULL, options, NULL, NULL, origin, url); } int import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { int rc; iobuf_t inp; inp = iobuf_esopen (fp, "rb", 1); if (!inp) { rc = gpg_error_from_syserror (); log_error ("iobuf_esopen failed: %s\n", gpg_strerror (rc)); return rc; } rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle, fpr, fpr_len, options, screener, screener_arg, origin, url); iobuf_close (inp); return rc; } static int import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, unsigned char **fpr,size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ int rc = 0; int v3keys; getkey_disable_caches (); if (!opt.no_armor) /* Armored reading is not disabled. */ { armor_filter_context_t *afx; afx = new_armor_context (); afx->only_keyblocks = 1; push_armor_filter (afx, inp); release_armor_context (afx); } while (!(rc = read_block (inp, !!(options & IMPORT_RESTORE), &pending_pkt, &keyblock, &v3keys))) { stats->v3keys += v3keys; if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) rc = import_one (ctrl, keyblock, stats, fpr, fpr_len, options, 0, 0, screener, screener_arg, origin, url); else if (keyblock->pkt->pkttype == PKT_SECRET_KEY) rc = import_secret_one (ctrl, keyblock, stats, opt.batch, options, 0, screener, screener_arg); else if (keyblock->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (keyblock->pkt->pkt.signature) ) rc = import_revoke_cert (ctrl, keyblock, stats); else { log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); } release_kbnode (keyblock); /* fixme: we should increment the not imported counter but this does only make sense if we keep on going despite of errors. For now we do this only if the imported key is too large. */ if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { stats->not_imported++; } else if (rc) break; if (!(++stats->count % 100) && !opt.quiet) log_info (_("%lu keys processed so far\n"), stats->count ); } stats->v3keys += v3keys; if (rc == -1) rc = 0; else if (rc && gpg_err_code (rc) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (rc)); return rc; } /* Helper to migrate secring.gpg to GnuPG 2.1. */ gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname) { gpg_error_t err; iobuf_t inp; PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ struct import_stats_s *stats; int v3keys; inp = iobuf_open (fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { err = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); return err; } getkey_disable_caches(); stats = import_new_stats_handle (); while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys))) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) err = import_secret_one (ctrl, keyblock, stats, 1, 0, 1, NULL, NULL); release_kbnode (keyblock); if (err) break; } import_release_stats_handle (stats); if (err == -1) err = 0; else if (err && gpg_err_code (err) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); else if (err) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (err)); iobuf_close (inp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); return err; } void import_print_stats (import_stats_t stats) { if (!opt.quiet) { log_info(_("Total number processed: %lu\n"), stats->count + stats->v3keys); if (stats->v3keys) log_info(_(" skipped PGP-2 keys: %lu\n"), stats->v3keys); if (stats->skipped_new_keys ) log_info(_(" skipped new keys: %lu\n"), stats->skipped_new_keys ); if (stats->no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats->no_user_id ); if (stats->imported) { log_info(_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged ) log_info(_(" unchanged: %lu\n"), stats->unchanged ); if (stats->n_uids ) log_info(_(" new user IDs: %lu\n"), stats->n_uids ); if (stats->n_subk ) log_info(_(" new subkeys: %lu\n"), stats->n_subk ); if (stats->n_sigs ) log_info(_(" new signatures: %lu\n"), stats->n_sigs ); if (stats->n_revoc ) log_info(_(" new key revocations: %lu\n"), stats->n_revoc ); if (stats->secret_read ) log_info(_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported ) log_info(_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups ) log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported ) log_info(_(" not imported: %lu\n"), stats->not_imported ); if (stats->n_sigs_cleaned) log_info(_(" signatures cleaned: %lu\n"),stats->n_sigs_cleaned); if (stats->n_uids_cleaned) log_info(_(" user IDs cleaned: %lu\n"),stats->n_uids_cleaned); } if (is_status_enabled ()) { char buf[15*20]; snprintf (buf, sizeof buf, "%lu %lu %lu 0 %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count + stats->v3keys, stats->no_user_id, stats->imported, stats->unchanged, stats->n_uids, stats->n_subk, stats->n_sigs, stats->n_revoc, stats->secret_read, stats->secret_imported, stats->secret_dups, stats->skipped_new_keys, stats->not_imported, stats->v3keys ); write_status_text (STATUS_IMPORT_RES, buf); } } /* Return true if PKTTYPE is valid in a keyblock. */ static int valid_keyblock_packet (int pkttype) { switch (pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_RING_TRUST: return 1; default: return 0; } } /**************** * Read the next keyblock from stream A. * Meta data (ring trust packets) are only considered of WITH_META is set. * PENDING_PKT should be initialized to NULL and not changed by the caller. * Return: 0 = okay, -1 no more blocks or another errorcode. * The int at at R_V3KEY counts the number of unsupported v3 * keyblocks. */ static int read_block( IOBUF a, int with_meta, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) { int rc; struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t root = NULL; int in_cert, in_v3key; *r_v3keys = 0; if (*pending_pkt) { root = new_kbnode( *pending_pkt ); *pending_pkt = NULL; in_cert = 1; } else in_cert = 0; pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); if (!with_meta) parsectx.skip_meta = 1; in_v3key = 0; while ((rc=parse_packet (&parsectx, pkt)) != -1) { if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY))) { in_v3key = 1; ++*r_v3keys; free_packet (pkt, &parsectx); init_packet (pkt); continue; } else if (rc ) /* (ignore errors) */ { if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) ; /* Do not show a diagnostic. */ else { log_error("read_block: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; goto ready; } free_packet (pkt, &parsectx); init_packet(pkt); 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 (!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; } /* 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; push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1); } free_packet (pkt, &parsectx); 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: if (rc == -1 && root ) rc = 0; if (rc ) release_kbnode( root ); else *ret_root = root; free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree( pkt ); return rc; } /* Walk through the subkeys on a pk to find if we have the PKS disease: multiple subkeys with their binding sigs stripped, and the sig for the first subkey placed after the last subkey. That is, instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have "pk uid sig sub1 sub2 sub3 bind1". We can't do anything about sub2 and sub3, as they are already lost, but we can try and rescue sub1 by reordering the keyblock so that it reads "pk uid sig sub1 bind1 sub2 sub3". Returns TRUE if the keyblock was modified. */ static int fix_pks_corruption (ctrl_t ctrl, kbnode_t keyblock) { int changed = 0; int keycount = 0; kbnode_t node; kbnode_t last = NULL; kbnode_t sknode=NULL; /* First determine if we have the problem at all. Look for 2 or more subkeys in a row, followed by a single binding sig. */ for (node=keyblock; node; last=node, node=node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { keycount++; if(!sknode) sknode=node; } else if (node->pkt->pkttype == PKT_SIGNATURE && IS_SUBKEY_SIG (node->pkt->pkt.signature) && keycount >= 2 && !node->next) { /* We might have the problem, as this key has two subkeys in a row without any intervening packets. */ /* Sanity check */ if (!last) break; /* Temporarily attach node to sknode. */ node->next = sknode->next; sknode->next = node; last->next = NULL; /* Note we aren't checking whether this binding sig is a selfsig. This is not necessary here as the subkey and binding sig will be rejected later if that is the case. */ if (check_key_signature (ctrl, keyblock,node,NULL)) { /* Not a match, so undo the changes. */ sknode->next = node->next; last->next = node; node->next = NULL; break; } else { /* Mark it good so we don't need to check it again */ sknode->flag |= NODE_GOOD_SELFSIG; changed = 1; break; } } else keycount = 0; } return changed; } /* Versions of GnuPG before 1.4.11 and 2.0.16 allowed to import bogus direct key signatures. A side effect of this was that a later import of the same good direct key signatures was not possible because the cmp_signature check in merge_blocks considered them equal. Although direct key signatures are now checked during import, there might still be bogus signatures sitting in a keyring. We need to detect and delete them before doing a merge. This function returns the number of removed sigs. */ static int fix_bad_direct_key_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid) { gpg_error_t err; kbnode_t node; int count = 0; for (node = keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (node->pkt->pkt.signature)) { err = check_key_signature (ctrl, keyblock, node, NULL); if (err && gpg_err_code (err) != GPG_ERR_PUBKEY_ALGO ) { /* If we don't know the error, we can't decide; this is not a problem because cmp_signature can't compare the signature either. */ log_info ("key %s: invalid direct key signature removed\n", keystr (keyid)); delete_kbnode (node); count++; } } } return count; } static void print_import_ok (PKT_public_key *pk, unsigned int reason) { byte array[MAX_FINGERPRINT_LEN], *s; char buf[MAX_FINGERPRINT_LEN*2+30], *p; size_t i, n; snprintf (buf, sizeof buf, "%u ", reason); p = buf + strlen (buf); fingerprint_from_pk (pk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); write_status_text (STATUS_IMPORT_OK, buf); } static void print_import_check (PKT_public_key * pk, PKT_user_id * id) { char * buf; byte fpr[24]; u32 keyid[2]; size_t i, n; size_t pos = 0; buf = xmalloc (17+41+id->len+32); keyid_from_pk (pk, keyid); sprintf (buf, "%08X%08X ", keyid[0], keyid[1]); pos = 17; fingerprint_from_pk (pk, fpr, &n); for (i = 0; i < n; i++, pos += 2) sprintf (buf+pos, "%02X", fpr[i]); strcat (buf, " "); strcat (buf, id->name); write_status_text (STATUS_IMPORT_CHECK, buf); xfree (buf); } static void check_prefs_warning(PKT_public_key *pk) { log_info(_("WARNING: key %s contains preferences for unavailable\n" "algorithms on these user IDs:\n"), keystr_from_pk(pk)); } static void check_prefs (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t node; PKT_public_key *pk; int problem=0; merge_keys_and_selfsig (ctrl, keyblock); pk=keyblock->pkt->pkt.public_key; for(node=keyblock;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID && node->pkt->pkt.user_id->created && node->pkt->pkt.user_id->prefs) { PKT_user_id *uid = node->pkt->pkt.user_id; prefitem_t *prefs = uid->prefs; char *user = utf8_to_native(uid->name,strlen(uid->name),0); for(;prefs->type;prefs++) { char num[10]; /* prefs->value is a byte, so we're over safe here */ sprintf(num,"%u",prefs->value); if(prefs->type==PREFTYPE_SYM) { if (openpgp_cipher_test_algo (prefs->value)) { const char *algo = (openpgp_cipher_test_algo (prefs->value) ? num : openpgp_cipher_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for cipher" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_HASH) { if(openpgp_md_test_algo(prefs->value)) { const char *algo = (gcry_md_test_algo (prefs->value) ? num : gcry_md_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for digest" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_ZIP) { if(check_compress_algo (prefs->value)) { const char *algo=compress_algo_to_string(prefs->value); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for compression" " algorithm %s\n"),user,algo?algo:num); problem=1; } } } xfree(user); } } if(problem) { log_info(_("it is strongly suggested that you update" " your preferences and\n")); log_info(_("re-distribute this key to avoid potential algorithm" " mismatch problems\n")); if(!opt.batch) { strlist_t sl = NULL; strlist_t locusr = NULL; size_t fprlen=0; byte fpr[MAX_FINGERPRINT_LEN], *p; char username[(MAX_FINGERPRINT_LEN*2)+1]; unsigned int i; p = fingerprint_from_pk (pk,fpr,&fprlen); for(i=0;ictrl; kbnode_t node = parm->node; static char numbuf[20]; const char *result; log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_ATTRIBUTE) { PKT_user_id *uid = node->pkt->pkt.user_id; if (!strcmp (propname, "uid")) result = uid->name; else if (!strcmp (propname, "mbox")) { if (!uid->mbox) { uid->mbox = mailbox_from_userid (uid->name); } result = uid->mbox; } else if (!strcmp (propname, "primary")) { result = uid->flags.primary? "1":"0"; } else if (!strcmp (propname, "expired")) { result = uid->flags.expired? "1":"0"; } else if (!strcmp (propname, "revoked")) { result = uid->flags.revoked? "1":"0"; } else result = NULL; } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (!strcmp (propname, "sig_created")) { snprintf (numbuf, sizeof numbuf, "%lu", (ulong)sig->timestamp); result = numbuf; } else if (!strcmp (propname, "sig_created_d")) { result = datestr_from_sig (sig); } else if (!strcmp (propname, "sig_algo")) { snprintf (numbuf, sizeof numbuf, "%d", sig->pubkey_algo); result = numbuf; } else if (!strcmp (propname, "sig_digest_algo")) { snprintf (numbuf, sizeof numbuf, "%d", sig->digest_algo); result = numbuf; } else if (!strcmp (propname, "expired")) { result = sig->flags.expired? "1":"0"; } else result = NULL; } else if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { PKT_public_key *pk = node->pkt->pkt.public_key; if (!strcmp (propname, "secret")) { result = (node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY)? "1":"0"; } else if (!strcmp (propname, "key_algo")) { snprintf (numbuf, sizeof numbuf, "%d", pk->pubkey_algo); result = numbuf; } else if (!strcmp (propname, "key_created")) { snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->timestamp); result = numbuf; } else if (!strcmp (propname, "key_created_d")) { result = datestr_from_pk (pk); } else if (!strcmp (propname, "expired")) { result = pk->has_expired? "1":"0"; } else if (!strcmp (propname, "revoked")) { result = pk->flags.revoked? "1":"0"; } else if (!strcmp (propname, "disabled")) { result = pk_is_disabled (pk)? "1":"0"; } else result = NULL; } else result = NULL; return result; } /* * Apply the keep-uid filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { parm.node = node; if (!recsel_select (selector, impex_filter_getval, &parm)) { /* log_debug ("keep-uid: deleting '%s'\n", */ /* node->pkt->pkt.user_id->name); */ /* The UID packet and all following packets up to the * next UID or a subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } /* else */ /* log_debug ("keep-uid: keeping '%s'\n", */ /* node->pkt->pkt.user_id->name); */ } } } /* * Apply the drop-sig filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_drop_sig_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; int active = 0; u32 main_keyid[2]; PKT_signature *sig; struct impex_filter_parm_s parm; parm.ctrl = ctrl; keyid_from_pk (keyblock->pkt->pkt.public_key, main_keyid); /* Loop over all signatures for user id and attribute packets which * are not self signatures. */ for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) break; /* ready. */ if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_ATTRIBUTE) active = 1; if (!active) continue; if (node->pkt->pkttype != PKT_SIGNATURE) continue; sig = node->pkt->pkt.signature; if (main_keyid[0] == sig->keyid[0] || main_keyid[1] == sig->keyid[1]) continue; /* Skip self-signatures. */ if (IS_UID_SIG(sig) || IS_UID_REV(sig)) { parm.node = node; if (recsel_select (selector, impex_filter_getval, &parm)) delete_kbnode (node); } } } /* Insert a key origin into a public key packet. */ static gpg_error_t insert_key_origin_pk (PKT_public_key *pk, u32 curtime, int origin, const char *url) { if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* For WKD and DANE we insert origin information also for the * key but we don't record the URL because we have have no use * for that: An update using a keyserver has higher precedence * and will thus update this origin info. For refresh using WKD * or DANE we need to go via the User ID anyway. Recall that we * are only inserting a new key. */ pk->keyorg = origin; pk->keyupdate = curtime; } else if (origin == KEYORG_KS && url) { /* If the key was retrieved from a keyserver using a fingerprint * request we add the meta information. Note that the use of a * fingerprint needs to be enforced by the caller of the import * function. This is commonly triggered by verifying a modern * signature which has an Issuer Fingerprint signature * subpacket. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } else if (origin == KEYORG_FILE) { pk->keyorg = origin; pk->keyupdate = curtime; } else if (origin == KEYORG_URL) { pk->keyorg = origin; pk->keyupdate = curtime; if (url) { xfree (pk->updateurl); pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } return 0; } /* Insert a key origin into a user id packet. */ static gpg_error_t insert_key_origin_uid (PKT_user_id *uid, u32 curtime, int origin, const char *url) { if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* We insert origin information on a UID only when we received * them via the Web Key Directory or a DANE record. The key we * receive here from the WKD has been filtered to contain only * the user ID as looked up in the WKD. For a DANE origin we * this should also be the case. Thus we will see here only one * user id. */ uid->keyorg = origin; uid->keyupdate = curtime; if (url) { xfree (uid->updateurl); uid->updateurl = xtrystrdup (url); if (!uid->updateurl) return gpg_error_from_syserror (); } } else if (origin == KEYORG_KS && url) { /* If the key was retrieved from a keyserver using a fingerprint * request we mark that also in the user ID. However we do not * store the keyserver URL in the UID. A later update (merge) * from a more trusted source will replace this info. */ uid->keyorg = origin; uid->keyupdate = curtime; } else if (origin == KEYORG_FILE) { uid->keyorg = origin; uid->keyupdate = curtime; } else if (origin == KEYORG_URL) { uid->keyorg = origin; uid->keyupdate = curtime; } return 0; } /* Apply meta data to KEYBLOCK. This sets the origin of the key to * ORIGIN and the updateurl to URL. Note that this function is only * used for a new key, that is not when we are merging keys. */ static gpg_error_t insert_key_origin (kbnode_t keyblock, int origin, const char *url) { gpg_error_t err; kbnode_t node; u32 curtime = make_timestamp (); for (node = keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) ; else if (node->pkt->pkttype == PKT_PUBLIC_KEY) { err = insert_key_origin_pk (node->pkt->pkt.public_key, curtime, origin, url); if (err) return err; } else if (node->pkt->pkttype == PKT_USER_ID) { err = insert_key_origin_uid (node->pkt->pkt.user_id, curtime, origin, url); if (err) return err; } } return 0; } /* Update meta data on KEYBLOCK. This updates the key origin on the * public key according to ORIGIN and URL. The UIDs are already * updated when this function is called. */ static gpg_error_t update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) { PKT_public_key *pk; log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = keyblock->pkt->pkt.public_key; if (pk->keyupdate > curtime) ; /* Don't do it for a time warp. */ else if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* We only update the origin info if they either have never been * set or are the origin was the same as the new one. If this * is WKD we also update the UID to show from which user id this * was updated. */ if (!pk->keyorg || pk->keyorg == KEYORG_WKD || pk->keyorg == KEYORG_DANE) { pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (origin == KEYORG_WKD && url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } } else if (origin == KEYORG_KS) { /* All updates from a keyserver are considered to have the * freshed key. Thus we always set the new key origin. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } else if (origin == KEYORG_FILE) { /* Updates from a file are considered to be fresh. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; } else if (origin == KEYORG_URL) { /* Updates from a URL are considered to be fresh. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } return 0; } /* * Try to import one keyblock. Return an error only in serious cases, * but never for an invalid keyblock. It uses log_error to increase * the internal errorcount, so that invalid input can be detected by * programs which called gpg. If SILENT is no messages are printed - * even most error messages are suppressed. ORIGIN is the origin of * the key (0 for unknown) and URL the corresponding URL. */ static int import_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, int origin, const char *url) { PKT_public_key *pk; PKT_public_key *pk_orig = NULL; kbnode_t node, uidnode; kbnode_t keyblock_orig = NULL; byte fpr2[MAX_FINGERPRINT_LEN]; size_t fpr2len; u32 keyid[2]; int rc = 0; int new_key = 0; int mod_key = 0; int same_key = 0; int non_self = 0; size_t an; char pkstrbuf[PUBKEY_STRING_SIZE]; int merge_keys_done = 0; int any_filter = 0; /* Get the key and print some info about it. */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if (!node ) BUG(); pk = node->pkt->pkt.public_key; fingerprint_from_pk (pk, fpr2, &fpr2len); for (an = fpr2len; an < MAX_FINGERPRINT_LEN; an++) fpr2[an] = 0; keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if (opt.verbose && !opt.interactive && !silent) { log_info( "pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk(pk) ); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len ); log_printf ("\n"); } if (!uidnode ) { if (!silent) log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } if (screener && screener (keyblock, screener_arg)) { log_error (_("key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); return 0; } if (opt.interactive && !silent) { if (is_status_enabled()) print_import_check (pk, uidnode->pkt->pkt.user_id); merge_keys_and_selfsig (ctrl, keyblock); tty_printf ("\n"); show_basic_key_info (ctrl, keyblock); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("import.okay", "Do you want to import this key? (y/N) ")) return 0; } collapse_uids(&keyblock); /* Clean the key that we're about to import, to cut down on things 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); clear_kbnode_flags( keyblock ); if ((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption (ctrl, keyblock) && opt.verbose) log_info (_("key %s: PKS subkey corruption repaired\n"), keystr_from_pk(pk)); if ((options & IMPORT_REPAIR_KEYS)) - key_check_all_keysigs (ctrl, keyblock, 0, 0); + key_check_all_keysigs (ctrl, 1, keyblock, 0, 0); if (chk_self_sigs (ctrl, keyblock, keyid, &non_self)) return 0; /* Invalid keyblock - error already printed. */ /* If we allow such a thing, mark unsigned uids as valid */ if (opt.allow_non_selfsigned_uid) { for (node=keyblock; node; node = node->next ) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODE_GOOD_SELFSIG) && !(node->flag & NODE_BAD_SELFSIG) ) { char *user=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); /* Fake a good signature status for the user id. */ node->flag |= NODE_GOOD_SELFSIG; log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"), keystr_from_pk(pk),user); xfree(user); } } if (!delete_inv_parts (ctrl, keyblock, keyid, options ) ) { if (!silent) { log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk)); if (!opt.quiet ) log_info(_("this may be caused by a missing self-signature\n")); } stats->no_user_id++; return 0; } /* Get rid of deleted nodes. */ commit_kbnode (&keyblock); /* Apply import filter. */ if (import_filter.keep_uid) { apply_keep_uid_filter (ctrl, keyblock, import_filter.keep_uid); commit_kbnode (&keyblock); any_filter = 1; } if (import_filter.drop_sig) { apply_drop_sig_filter (ctrl, keyblock, import_filter.drop_sig); commit_kbnode (&keyblock); any_filter = 1; } /* If we ran any filter we need to check that at least one user id * is left in the keyring. Note that we do not use log_error in * this case. */ if (any_filter && !any_uid_left (keyblock)) { if (!opt.quiet ) log_info ( _("key %s: no valid user IDs\n"), keystr_from_pk (pk)); stats->no_user_id++; return 0; } /* Show the key in the form it is merged or inserted. We skip this * if "import-export" is also active without --armor or the output * file has explicily been given. */ if ((options & IMPORT_SHOW) && !((options & IMPORT_EXPORT) && !opt.armor && !opt.outfile)) { merge_keys_and_selfsig (ctrl, keyblock); merge_keys_done = 1; /* Note that we do not want to show the validity because the key * has not yet imported. */ list_keyblock_direct (ctrl, keyblock, 0, 0, 1, 1); es_fflush (es_stdout); } /* Write the keyblock to the output and do not actually import. */ if ((options & IMPORT_EXPORT)) { if (!merge_keys_done) { merge_keys_and_selfsig (ctrl, keyblock); merge_keys_done = 1; } rc = write_keyblock_to_output (keyblock, opt.armor, opt.export_options); goto leave; } if (opt.dry_run) goto leave; /* Do we have this key already in one of our pubrings ? */ pk_orig = xmalloc_clear( sizeof *pk_orig ); rc = get_pubkey_byfprint_fast (pk_orig, fpr2, fpr2len); if (rc && gpg_err_code (rc) != GPG_ERR_NO_PUBKEY && gpg_err_code (rc) != GPG_ERR_UNUSABLE_PUBKEY ) { if (!silent) log_error (_("key %s: public key not found: %s\n"), keystr(keyid), gpg_strerror (rc)); } else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) ) { if (opt.verbose && !silent ) log_info( _("key %s: new key - skipped\n"), keystr(keyid)); rc = 0; stats->skipped_new_keys++; } else if (rc ) /* Insert this key. */ { KEYDB_HANDLE hd; int n_sigs_cleaned, n_uids_cleaned; hd = keydb_new (); if (!hd) return gpg_error_from_syserror (); rc = keydb_locate_writable (hd); if (rc) { log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc)); keydb_release (hd); return GPG_ERR_GENERAL; } if (opt.verbose > 1 ) 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); /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet * and thus the address of KEYBLOCK won't change. */ if ( !(options & IMPORT_RESTORE) ) { rc = insert_key_origin (keyblock, origin, url); if (rc) { log_error ("insert_key_origin failed: %s\n", gpg_strerror (rc)); keydb_release (hd); return GPG_ERR_GENERAL; } } rc = keydb_insert_keyblock (hd, keyblock ); if (rc) log_error (_("error writing keyring '%s': %s\n"), keydb_get_resource_name (hd), gpg_strerror (rc)); else if (!(opt.import_options & IMPORT_KEEP_OWNERTTRUST)) { /* This should not be possible since we delete the ownertrust when a key is deleted, but it can happen if the keyring and trustdb are out of sync. It can also be made to happen with the trusted-key command and by importing and locally exported key. */ clear_ownertrusts (ctrl, pk); if (non_self) revalidation_mark (ctrl); } keydb_release (hd); /* We are ready. */ if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2); log_info (_("key %s: public key \"%s\" imported\n"), keystr(keyid), p); xfree(p); } if (is_status_enabled()) { char *us = get_long_user_id_string (ctrl, keyid); write_status_text( STATUS_IMPORTED, us ); xfree(us); print_import_ok (pk, 1); } stats->imported++; new_key = 1; } else /* Merge the key. */ { KEYDB_HANDLE hd; int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned; u32 curtime = make_timestamp (); /* Compare the original against the new key; just to be sure nothing * weird is going on */ if (cmp_public_keys( pk_orig, pk ) ) { if (!silent) log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; } /* Now read the original keyblock again so that we can use that handle for updating the keyblock. */ hd = keydb_new (); if (!hd) { rc = gpg_error_from_syserror (); goto leave; } keydb_disable_caching (hd); rc = keydb_search_fpr (hd, fpr2); if (rc ) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); keydb_release (hd); goto leave; } rc = keydb_get_keyblock (hd, &keyblock_orig); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); keydb_release (hd); goto leave; } /* Make sure the original direct key sigs are all sane. */ n_sigs_cleaned = fix_bad_direct_key_sigs (ctrl, keyblock_orig, keyid); if (n_sigs_cleaned) commit_kbnode (&keyblock_orig); /* and try to merge the block */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); n_uids = n_sigs = n_subk = n_uids_cleaned = 0; rc = merge_blocks (ctrl, options, keyblock_orig, keyblock, keyid, curtime, origin, url, &n_uids, &n_sigs, &n_subk ); if (rc ) { keydb_release (hd); goto leave; } if ((options & IMPORT_CLEAN)) clean_key (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet * and thus the address of KEYBLOCK won't change. */ if ( !(options & IMPORT_RESTORE) ) { rc = update_key_origin (keyblock_orig, curtime, origin, url); if (rc) { log_error ("update_key_origin failed: %s\n", gpg_strerror (rc)); keydb_release (hd); goto leave; } } mod_key = 1; /* KEYBLOCK_ORIG has been updated; write */ rc = keydb_update_keyblock (ctrl, hd, keyblock_orig); if (rc) log_error (_("error writing keyring '%s': %s\n"), keydb_get_resource_name (hd), gpg_strerror (rc) ); else if (non_self) revalidation_mark (ctrl); /* We are ready. */ if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2); if (n_uids == 1 ) log_info( _("key %s: \"%s\" 1 new user ID\n"), keystr(keyid),p); else if (n_uids ) log_info( _("key %s: \"%s\" %d new user IDs\n"), keystr(keyid),p,n_uids); if (n_sigs == 1 ) log_info( _("key %s: \"%s\" 1 new signature\n"), keystr(keyid), p); else if (n_sigs ) log_info( _("key %s: \"%s\" %d new signatures\n"), keystr(keyid), p, n_sigs ); if (n_subk == 1 ) log_info( _("key %s: \"%s\" 1 new subkey\n"), keystr(keyid), p); else if (n_subk ) log_info( _("key %s: \"%s\" %d new subkeys\n"), keystr(keyid), p, n_subk ); if (n_sigs_cleaned==1) log_info(_("key %s: \"%s\" %d signature cleaned\n"), keystr(keyid),p,n_sigs_cleaned); else if (n_sigs_cleaned) log_info(_("key %s: \"%s\" %d signatures cleaned\n"), keystr(keyid),p,n_sigs_cleaned); if (n_uids_cleaned==1) log_info(_("key %s: \"%s\" %d user ID cleaned\n"), keystr(keyid),p,n_uids_cleaned); else if (n_uids_cleaned) log_info(_("key %s: \"%s\" %d user IDs cleaned\n"), keystr(keyid),p,n_uids_cleaned); xfree(p); } stats->n_uids +=n_uids; stats->n_sigs +=n_sigs; stats->n_subk +=n_subk; stats->n_sigs_cleaned +=n_sigs_cleaned; stats->n_uids_cleaned +=n_uids_cleaned; if (is_status_enabled () && !silent) print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } else { /* Fixme: we do not track the time we last checked a key for * updates. To do this we would need to rewrite even the * keys which have no changes. */ same_key = 1; if (is_status_enabled ()) print_import_ok (pk, 0); if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); xfree(p); } stats->unchanged++; } keydb_release (hd); hd = NULL; } leave: if (mod_key || new_key || same_key) { /* A little explanation for this: we fill in the fingerprint when importing keys as it can be useful to know the fingerprint in certain keyserver-related cases (a keyserver asked for a particular name, but the key doesn't have that name). However, in cases where we're importing more than one key at a time, we cannot know which key to fingerprint. In these cases, rather than guessing, we do not fingerprinting at all, and we must hope the user ID on the keys are useful. Note that we need to do this for new keys, merged keys and even for unchanged keys. This is required because for example the --auto-key-locate feature may import an already imported key and needs to know the fingerprint of the key in all cases. */ if (fpr) { xfree (*fpr); /* Note that we need to compare against 0 here because COUNT gets only incremented after returning from this function. */ if (!stats->count) *fpr = fingerprint_from_pk (pk, NULL, fpr_len); else *fpr = NULL; } } /* Now that the key is definitely incorporated into the keydb, we need to check if a designated revocation is present or if the prefs are not rational so we can warn the user. */ if (mod_key) { revocation_present (ctrl, keyblock_orig); if (!from_sk && have_secret_key_with_kid (keyid)) check_prefs (ctrl, keyblock_orig); } else if (new_key) { revocation_present (ctrl, keyblock); if (!from_sk && have_secret_key_with_kid (keyid)) check_prefs (ctrl, keyblock); } release_kbnode( keyblock_orig ); free_public_key( pk_orig ); return rc; } /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The function prints diagnostics and returns an error code. If BATCH is true the secret keys are stored by gpg-agent in the transfer format (i.e. no re-protection and aksing for passphrases). */ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; kbnode_t ctx = NULL; kbnode_t node; PKT_public_key *main_pk, *pk; struct seckey_info *ski; int nskey; membuf_t mbuf; int i, j; void *format_args[2*PUBKEY_MAX_NSKEY]; gcry_sexp_t skey, prot, tmpsexp; gcry_sexp_t curve = NULL; unsigned char *transferkey = NULL; size_t transferkeylen; gcry_cipher_hd_t cipherhd = NULL; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; char *cache_nonce = NULL; int stub_key_skipped = 0; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; main_pk = NULL; while ((node = walk_kbnode (sec_keyblock, &ctx, 0))) { if (node->pkt->pkttype != PKT_SECRET_KEY && node->pkt->pkttype != PKT_SECRET_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (!main_pk) main_pk = pk; /* Make sure the keyids are available. */ keyid_from_pk (pk, NULL); if (node->pkt->pkttype == PKT_SECRET_KEY) { pk->main_keyid[0] = pk->keyid[0]; pk->main_keyid[1] = pk->keyid[1]; } else { pk->main_keyid[0] = main_pk->keyid[0]; pk->main_keyid[1] = main_pk->keyid[1]; } ski = pk->seckey_info; if (!ski) BUG (); if (stats) { stats->count++; stats->secret_read++; } /* We ignore stub keys. The way we handle them in other parts of the code is by asking the agent whether any secret key is available for a given keyblock and then concluding that we have a secret key; all secret (sub)keys of the keyblock the agent does not know of are then stub keys. This works also for card stub keys. The learn command or the card-status command may be used to check with the agent whether a card has been inserted and a stub key is in turn generated by the agent. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) { stub_key_skipped = 1; continue; } /* Convert our internal secret key object into an S-expression. */ nskey = pubkey_get_nskey (pk->pubkey_algo); if (!nskey || nskey > PUBKEY_MAX_NSKEY) { err = gpg_error (GPG_ERR_BAD_SECKEY); log_error ("internal error: %s\n", gpg_strerror (err)); goto leave; } init_membuf (&mbuf, 50); put_membuf_str (&mbuf, "(skey"); if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { /* The ECC case. */ char *curvestr = openpgp_oid_to_str (pk->pkey[0]); if (!curvestr) err = gpg_error_from_syserror (); else { const char *curvename = openpgp_oid_to_curve (curvestr, 1); gcry_sexp_release (curve); err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvename?curvename:curvestr); xfree (curvestr); if (!err) { j = 0; /* Append the public key element Q. */ put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + 1; /* Append the secret key element D. For ECDH we skip PKEY[2] because this holds the KEK which is not needed by gpg-agent. */ i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } } else { /* Standard case for the old (non-ECC) algorithms. */ for (i=j=0; i < nskey; i++) { if (!pk->pkey[i]) continue; /* Protected keys only have NPKEY+1 elements. */ if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } put_membuf_str (&mbuf, ")"); put_membuf (&mbuf, "", 1); if (err) xfree (get_membuf (&mbuf, NULL)); else { char *format = get_membuf (&mbuf, NULL); if (!format) err = gpg_error_from_syserror (); else err = gcry_sexp_build_array (&skey, NULL, format, format_args); xfree (format); } if (err) { log_error ("error building skey array: %s\n", gpg_strerror (err)); goto leave; } if (ski->is_protected) { char countbuf[35]; /* Note that the IVLEN may be zero if we are working on a dummy key. We can't express that in an S-expression and thus we send dummy data for the IV. */ snprintf (countbuf, sizeof countbuf, "%lu", (unsigned long)ski->s2k.count); err = gcry_sexp_build (&prot, NULL, " (protection %s %s %b %d %s %b %s)\n", ski->sha1chk? "sha1":"sum", openpgp_cipher_algo_name (ski->algo), ski->ivlen? (int)ski->ivlen:1, ski->ivlen? ski->iv: (const unsigned char*)"X", ski->s2k.mode, openpgp_md_algo_name (ski->s2k.hash_algo), (int)sizeof (ski->s2k.salt), ski->s2k.salt, countbuf); } else err = gcry_sexp_build (&prot, NULL, " (protection none)\n"); tmpsexp = NULL; xfree (transferkey); transferkey = NULL; if (!err) err = gcry_sexp_build (&tmpsexp, NULL, "(openpgp-private-key\n" " (version %d)\n" " (algo %s)\n" " %S%S\n" " (csum %d)\n" " %S)\n", pk->version, openpgp_pk_algo_name (pk->pubkey_algo), curve, skey, (int)(unsigned long)ski->csum, prot); gcry_sexp_release (skey); gcry_sexp_release (prot); if (!err) err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen); gcry_sexp_release (tmpsexp); if (err) { log_error ("error building transfer key: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ wrappedkeylen = transferkeylen + 8; xfree (wrappedkey); wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) err = gpg_error_from_syserror (); else err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, transferkey, transferkeylen); if (err) goto leave; xfree (transferkey); transferkey = NULL; /* Send the wrapped key to the agent. */ { char *desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_IMPORT, 1); err = agent_import_key (ctrl, desc, &cache_nonce, wrappedkey, wrappedkeylen, batch, force, pk->keyid, pk->main_keyid, pk->pubkey_algo); xfree (desc); } if (!err) { if (opt.verbose) log_info (_("key %s: secret key imported\n"), keystr_from_pk_with_sub (main_pk, pk)); if (stats) stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { if (opt.verbose) log_info (_("key %s: secret key already exists\n"), keystr_from_pk_with_sub (main_pk, pk)); err = 0; if (stats) stats->secret_dups++; } else { log_error (_("key %s: error sending to agent: %s\n"), keystr_from_pk_with_sub (main_pk, pk), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; /* Don't try the other subkeys. */ } } if (!err && stub_key_skipped) /* We need to notify user how to migrate stub keys. */ err = gpg_error (GPG_ERR_NOT_PROCESSED); leave: gcry_sexp_release (curve); xfree (cache_nonce); xfree (wrappedkey); xfree (transferkey); gcry_cipher_close (cipherhd); xfree (kek); return err; } /* Walk a secret keyblock and produce a public keyblock out of it. Returns a new node or NULL on error. */ static kbnode_t sec_to_pub_keyblock (kbnode_t sec_keyblock) { kbnode_t pub_keyblock = NULL; kbnode_t ctx = NULL; kbnode_t secnode, pubnode; while ((secnode = walk_kbnode (sec_keyblock, &ctx, 0))) { if (secnode->pkt->pkttype == PKT_SECRET_KEY || secnode->pkt->pkttype == PKT_SECRET_SUBKEY) { /* Make a public key. */ PACKET *pkt; PKT_public_key *pk; pkt = xtrycalloc (1, sizeof *pkt); pk = pkt? copy_public_key (NULL, secnode->pkt->pkt.public_key): NULL; if (!pk) { xfree (pkt); release_kbnode (pub_keyblock); return NULL; } if (secnode->pkt->pkttype == PKT_SECRET_KEY) pkt->pkttype = PKT_PUBLIC_KEY; else pkt->pkttype = PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; pubnode = new_kbnode (pkt); } else { pubnode = clone_kbnode (secnode); } if (!pub_keyblock) pub_keyblock = pubnode; else add_kbnode (pub_keyblock, pubnode); } return pub_keyblock; } /**************** * Ditto for secret keys. Handling is simpler than for public keys. * We allow secret key importing only when allow is true, this is so * that a secret key can not be imported accidentally and thereby tampering * with the trust calculation. */ 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) { PKT_public_key *pk; struct seckey_info *ski; kbnode_t node, uidnode; u32 keyid[2]; int rc = 0; int nr_prev; kbnode_t pub_keyblock; char pkstrbuf[PUBKEY_STRING_SIZE]; /* Get the key and print some info about it */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); uidnode = find_next_kbnode (keyblock, PKT_USER_ID); if (screener && screener (keyblock, screener_arg)) { log_error (_("secret key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); return 0; } if (opt.verbose && !for_migration) { log_info ("sec %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), datestr_from_pk (pk)); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len); log_printf ("\n"); } stats->secret_read++; if ((options & IMPORT_NO_SECKEY)) { if (!for_migration) log_error (_("importing secret keys not allowed\n")); return 0; } if (!uidnode) { if (!for_migration) log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); return 0; } ski = pk->seckey_info; if (!ski) { /* Actually an internal error. */ log_error ("key %s: secret key info missing\n", keystr_from_pk (pk)); return 0; } /* A quick check to not import keys with an invalid protection cipher algorithm (only checks the primary key, though). */ if (ski->algo > 110) { if (!for_migration) log_error (_("key %s: secret key with invalid cipher %d" " - skipped\n"), keystr_from_pk (pk), ski->algo); return 0; } #ifdef ENABLE_SELINUX_HACKS if (1) { /* We don't allow importing secret keys because that may be used to put a secret key into the keyring and the user might later be tricked into signing stuff with that key. */ log_error (_("importing secret keys not allowed\n")); return 0; } #endif clear_kbnode_flags (keyblock); nr_prev = stats->skipped_new_keys; /* Make a public key out of the key. */ pub_keyblock = sec_to_pub_keyblock (keyblock); if (!pub_keyblock) log_error ("key %s: failed to create public key from secret key\n", keystr_from_pk (pk)); else { /* Note that this outputs an IMPORT_OK status message for the public key block, and below we will output another one for the secret keys. FIXME? */ import_one (ctrl, pub_keyblock, stats, NULL, NULL, options, 1, for_migration, screener, screener_arg, 0, NULL); /* Fixme: We should check for an invalid keyblock and cancel the secret key import in this case. */ release_kbnode (pub_keyblock); /* At least we cancel the secret key import when the public key import was skipped due to MERGE_ONLY option and a new key. */ if (stats->skipped_new_keys <= nr_prev) { /* Read the keyblock again to get the effects of a merge. */ /* Fixme: we should do this based on the fingerprint or even better let import_one return the merged keyblock. */ node = get_pubkeyblock (ctrl, keyid); if (!node) log_error ("key %s: failed to re-lookup public key\n", keystr_from_pk (pk)); else { gpg_error_t err; /* transfer_secret_keys collects subkey stats. */ struct import_stats_s subkey_stats = {0}; err = transfer_secret_keys (ctrl, &subkey_stats, keyblock, batch, 0); if (gpg_err_code (err) == GPG_ERR_NOT_PROCESSED) { /* TRANSLATORS: For smartcard, each private key on host has a reference (stub) to a smartcard and actual private key data is stored on the card. A single smartcard can have up to three private key data. Importing private key stub is always skipped in 2.1, and it returns GPG_ERR_NOT_PROCESSED. Instead, user should be suggested to run 'gpg --card-status', then, references to a card will be automatically created again. */ log_info (_("To migrate '%s', with each smartcard, " "run: %s\n"), "secring.gpg", "gpg --card-status"); err = 0; } if (!err) { int status = 16; if (!opt.quiet) log_info (_("key %s: secret key imported\n"), keystr_from_pk (pk)); if (subkey_stats.secret_imported) { status |= 1; stats->secret_imported += 1; } if (subkey_stats.secret_dups) stats->secret_dups += 1; if (is_status_enabled ()) print_import_ok (pk, status); check_prefs (ctrl, node); } release_kbnode (node); } } } return rc; } /**************** * 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) { PKT_public_key *pk = NULL; kbnode_t onode; kbnode_t keyblock = NULL; KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); log_assert (IS_KEY_REV (node->pkt->pkt.signature)); keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; pk = xmalloc_clear( sizeof *pk ); 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)); rc = 0; goto leave; } else if (rc ) { log_error(_("key %s: public key not found: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* Read the original keyblock. */ hd = keydb_new (); if (!hd) { rc = gpg_error_from_syserror (); goto leave; } { byte afp[MAX_FINGERPRINT_LEN]; size_t an; fingerprint_from_pk (pk, afp, &an); while (an < MAX_FINGERPRINT_LEN) afp[an++] = 0; rc = keydb_search_fpr (hd, afp); } if (rc) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (hd, &keyblock ); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* 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. */ rc = check_key_signature (ctrl, keyblock, node, NULL); if (rc ) { log_error( _("key %s: invalid revocation certificate" ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* check whether we already have this */ for(onode=keyblock->next; onode; onode=onode->next ) { if (onode->pkt->pkttype == PKT_USER_ID ) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && !cmp_signatures(node->pkt->pkt.signature, onode->pkt->pkt.signature)) { rc = 0; goto leave; /* yes, we already know about it */ } } /* 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 ) { char *p=get_user_id_native (ctrl, keyid); log_info( _("key %s: \"%s\" revocation certificate imported\n"), keystr(keyid),p); xfree(p); } 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: keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); return rc; } /* Loop over the keyblock and check all self signatures. On return * the following bis 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 * - NODE_DELETION_MARK :: This node shall be deleted * * NON_SELF is set to true if there are any sigs other than self-sigs * in this keyblock. * * Returns 0 on success or -1 (but not an error code) if the keyblock * is invalid. */ static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) { kbnode_t n, knode = NULL; PKT_signature *sig; int rc; u32 bsdate=0, rsdate=0; kbnode_t bsnode = NULL, rsnode = NULL; for (n=keyblock; (n = find_next_kbnode (n, 0)); ) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) { knode = n; bsdate = 0; rsdate = 0; bsnode = NULL; rsnode = NULL; continue; } if ( n->pkt->pkttype != PKT_SIGNATURE ) continue; sig = n->pkt->pkt.signature; if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] ) { *non_self = 1; continue; } /* This just caches the sigs for later use. That way we import a fully-cached key which speeds things up. */ if (!opt.no_sig_cache) check_key_signature (ctrl, keyblock, n, NULL); if ( IS_UID_SIG(sig) || IS_UID_REV(sig) ) { kbnode_t unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); if ( !unode ) { log_error( _("key %s: no user ID for signature\n"), keystr(keyid)); return -1; /* The complete keyblock is invalid. */ } /* If it hasn't been marked valid yet, keep trying. */ if (!(unode->flag & NODE_GOOD_SELFSIG)) { rc = check_key_signature (ctrl, keyblock, n, NULL); if ( rc ) { if ( opt.verbose ) { char *p = utf8_to_native (unode->pkt->pkt.user_id->name, strlen (unode->pkt->pkt.user_id->name),0); log_info (gpg_err_code(rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public key " "algorithm on user ID \"%s\"\n"): _("key %s: invalid self-signature " "on user ID \"%s\"\n"), keystr (keyid),p); xfree (p); } } else unode->flag |= NODE_GOOD_SELFSIG; } } else if (IS_KEY_SIG (sig)) { rc = check_key_signature (ctrl, keyblock, n, NULL); 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 direct key signature\n"), keystr (keyid)); n->flag |= NODE_DELETION_MARK; } } else if ( IS_SUBKEY_SIG (sig) ) { /* Note that this works based solely on the timestamps like the rest of gpg. If the standard gets revocation targets, this may need to be revised. */ if ( !knode ) { if (opt.verbose) log_info (_("key %s: no subkey for key binding\n"), keystr (keyid)); n->flag |= NODE_DELETION_MARK; } else { rc = check_key_signature (ctrl, keyblock, n, NULL); 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)); n->flag |= NODE_DELETION_MARK; } else { /* It's valid, so is it newer? */ if (sig->timestamp >= bsdate) { knode->flag |= NODE_GOOD_SELFSIG; /* Subkey is valid. */ if (bsnode) { /* Delete the last binding sig since this one is newer */ bsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " binding\n"),keystr(keyid)); } bsnode = n; bsdate = sig->timestamp; } else n->flag |= NODE_DELETION_MARK; /* older */ } } } else if ( IS_SUBKEY_REV (sig) ) { /* We don't actually mark the subkey as revoked right now, so just check that the revocation sig is the most recent valid one. Note that we don't care if the binding sig is newer than the revocation sig. See the comment in getkey.c:merge_selfsigs_subkey for more. */ if ( !knode ) { if (opt.verbose) log_info (_("key %s: no subkey for key revocation\n"), keystr(keyid)); n->flag |= NODE_DELETION_MARK; } else { rc = check_key_signature (ctrl, keyblock, n, NULL); 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 revocation\n"), keystr(keyid)); n->flag |= NODE_DELETION_MARK; } else { /* It's valid, so is it newer? */ if (sig->timestamp >= rsdate) { if (rsnode) { /* Delete the last revocation sig since this one is newer. */ rsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " revocation\n"),keystr(keyid)); } rsnode = n; rsdate = sig->timestamp; } else n->flag |= NODE_DELETION_MARK; /* older */ } } } } return 0; } /* Delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implementation; but * consider RSA as valid, because parse/build_packets knows about it. * * Returns: True if at least one valid user-id is left over. */ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, unsigned int options) { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; for (node=keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { uid_seen = 1; if ((node->flag & NODE_BAD_SELFSIG) || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) { char *p=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); log_info( _("key %s: skipped user ID \"%s\"\n"), keystr(keyid),p); xfree(p); } delete_kbnode( node ); /* the user-id */ /* and all following packets up to the next user-id */ while (node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){ delete_kbnode( node->next ); node = node->next; } } else nvalid++; } else if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if ((node->flag & NODE_BAD_SELFSIG) || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) log_info( _("key %s: skipped subkey\n"),keystr(keyid)); delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ while (node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) { delete_kbnode( node->next ); node = node->next; } } else subkey_seen = 1; } else if (node->pkt->pkttype == PKT_SIGNATURE && openpgp_pk_test_algo (node->pkt->pkt.signature->pubkey_algo) && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA ) { delete_kbnode( node ); /* build_packet() can't handle this */ } else if (node->pkt->pkttype == PKT_SIGNATURE && !node->pkt->pkt.signature->flags.exportable && !(options&IMPORT_LOCAL_SIGS) && !have_secret_key_with_kid (node->pkt->pkt.signature->keyid)) { /* here we violate the rfc a bit by still allowing * to import non-exportable signature when we have the * the secret key used to create this signature - it * seems that this makes sense */ if(opt.verbose) log_info( _("key %s: non exportable signature" " (class 0x%02X) - skipped\n"), keystr(keyid), node->pkt->pkt.signature->sig_class ); delete_kbnode( node ); } else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (node->pkt->pkt.signature)) { if (uid_seen ) { if(opt.verbose) log_info( _("key %s: revocation certificate" " at wrong place - skipped\n"),keystr(keyid)); delete_kbnode( node ); } else { /* If the revocation cert is from a different key than the one we're working on don't check it - it's probably from a revocation key and won't be verifiable with this key anyway. */ if(node->pkt->pkt.signature->keyid[0]==keyid[0] && node->pkt->pkt.signature->keyid[1]==keyid[1]) { int rc = check_key_signature (ctrl, keyblock, node, NULL); if (rc ) { if(opt.verbose) log_info( _("key %s: invalid revocation" " certificate: %s - skipped\n"), keystr(keyid), gpg_strerror (rc)); delete_kbnode( node ); } } } } else if (node->pkt->pkttype == PKT_SIGNATURE && (IS_SUBKEY_SIG (node->pkt->pkt.signature) || IS_SUBKEY_REV (node->pkt->pkt.signature)) && !subkey_seen ) { if(opt.verbose) log_info( _("key %s: subkey signature" " in wrong place - skipped\n"), keystr(keyid)); delete_kbnode( node ); } else if (node->pkt->pkttype == PKT_SIGNATURE && !IS_CERT(node->pkt->pkt.signature)) { if(opt.verbose) log_info(_("key %s: unexpected signature class (0x%02X) -" " skipped\n"),keystr(keyid), node->pkt->pkt.signature->sig_class); delete_kbnode(node); } else if ((node->flag & NODE_DELETION_MARK)) delete_kbnode( node ); } /* note: because keyblock is the public key, it is never marked * for deletion and so keyblock cannot change */ commit_kbnode( &keyblock ); return nvalid; } /* This function returns true if any UID is left in the keyring. */ static int any_uid_left (kbnode_t keyblock) { kbnode_t node; for (node=keyblock->next; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) return 1; return 0; } /**************** * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their * sigs into one. * Returns: True if the keyblock has changed. */ int collapse_uids( kbnode_t *keyblock ) { kbnode_t uid1; int any=0; for(uid1=*keyblock;uid1;uid1=uid1->next) { kbnode_t uid2; if(is_deleted_kbnode(uid1)) continue; if(uid1->pkt->pkttype!=PKT_USER_ID) continue; for(uid2=uid1->next;uid2;uid2=uid2->next) { if(is_deleted_kbnode(uid2)) continue; if(uid2->pkt->pkttype!=PKT_USER_ID) continue; if(cmp_user_ids(uid1->pkt->pkt.user_id, uid2->pkt->pkt.user_id)==0) { /* We have a duplicated uid */ kbnode_t sig1,last; any=1; /* Now take uid2's signatures, and attach them to uid1 */ for(last=uid2;last->next;last=last->next) { if(is_deleted_kbnode(last)) continue; if(last->next->pkt->pkttype==PKT_USER_ID || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY || last->next->pkt->pkttype==PKT_SECRET_SUBKEY) break; } /* Snip out uid2 */ (find_prev_kbnode(*keyblock,uid2,0))->next=last->next; /* Now put uid2 in place as part of uid1 */ last->next=uid1->next; uid1->next=uid2; delete_kbnode(uid2); /* Now dedupe uid1 */ for(sig1=uid1->next;sig1;sig1=sig1->next) { kbnode_t sig2; if(is_deleted_kbnode(sig1)) continue; if(sig1->pkt->pkttype==PKT_USER_ID || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig1->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig1->pkt->pkttype!=PKT_SIGNATURE) continue; for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next) { if(is_deleted_kbnode(sig2)) continue; if(sig2->pkt->pkttype==PKT_USER_ID || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig2->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig2->pkt->pkttype!=PKT_SIGNATURE) continue; if(cmp_signatures(sig1->pkt->pkt.signature, sig2->pkt->pkt.signature)==0) { /* We have a match, so delete the second signature */ delete_kbnode(sig2); sig2=last; } } } } } } commit_kbnode(keyblock); if(any && !opt.quiet) { const char *key="???"; if ((uid1 = find_kbnode (*keyblock, PKT_PUBLIC_KEY)) ) key = keystr_from_pk (uid1->pkt->pkt.public_key); else if ((uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY)) ) key = keystr_from_pk (uid1->pkt->pkt.public_key); log_info (_("key %s: duplicated user ID detected - merged\n"), key); } return any; } /* Check for a 0x20 revocation from a revocation key that is not present. This may be called without the benefit of merge_xxxx so you can't rely on pk->revkey and friends. */ static void revocation_present (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t onode, inode; PKT_public_key *pk = keyblock->pkt->pkt.public_key; for(onode=keyblock->next;onode;onode=onode->next) { /* If we reach user IDs, we're done. */ if(onode->pkt->pkttype==PKT_USER_ID) break; if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (onode->pkt->pkt.signature) && onode->pkt->pkt.signature->revkey) { int idx; PKT_signature *sig=onode->pkt->pkt.signature; for(idx=0;idxnumrevkeys;idx++) { u32 keyid[2]; keyid_from_fingerprint (ctrl, sig->revkey[idx].fpr, MAX_FINGERPRINT_LEN, keyid); for(inode=keyblock->next;inode;inode=inode->next) { /* If we reach user IDs, we're done. */ if(inode->pkt->pkttype==PKT_USER_ID) break; if (inode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (inode->pkt->pkt.signature) && inode->pkt->pkt.signature->keyid[0]==keyid[0] && inode->pkt->pkt.signature->keyid[1]==keyid[1]) { /* Okay, we have a revocation key, and a * revocation issued by it. Do we have the key * itself? */ int rc; rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx].fpr, MAX_FINGERPRINT_LEN); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY) { char *tempkeystr=xstrdup(keystr_from_pk(pk)); /* No, so try and get it */ if ((opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (ctrl)) { log_info(_("WARNING: key %s may be revoked:" " fetching revocation key %s\n"), tempkeystr,keystr(keyid)); keyserver_import_fprint (ctrl, sig->revkey[idx].fpr, MAX_FINGERPRINT_LEN, opt.keyserver, 0); /* Do we have it now? */ rc=get_pubkey_byfprint_fast (NULL, sig->revkey[idx].fpr, MAX_FINGERPRINT_LEN); } if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY) log_info(_("WARNING: key %s may be revoked:" " revocation key %s not present.\n"), tempkeystr,keystr(keyid)); xfree(tempkeystr); } } } } } } } /* * compare and merge the blocks * * o compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * o Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * Note: We indicate newly inserted packets with NODE_FLAG_A. */ static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, u32 curtime, int origin, const char *url, int *n_uids, int *n_sigs, int *n_subk ) { kbnode_t onode, node; int rc, found; /* 1st: handle revocation certificates */ for (node=keyblock->next; node; node=node->next ) { if (node->pkt->pkttype == PKT_USER_ID ) break; else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (node->pkt->pkt.signature)) { /* check whether we already have this */ found = 0; for (onode=keyblock_orig->next; onode; onode=onode->next) { if (onode->pkt->pkttype == PKT_USER_ID ) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (onode->pkt->pkt.signature) && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if (!found) { kbnode_t n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= NODE_FLAG_A; ++*n_sigs; if(!opt.quiet) { char *p = get_user_id_native (ctrl, keyid); log_info(_("key %s: \"%s\" revocation" " certificate added\n"), keystr(keyid),p); xfree(p); } } } } /* 2nd: merge in any direct key (0x1F) sigs */ for(node=keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID ) break; else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (node->pkt->pkt.signature)) { /* check whether we already have this */ found = 0; for (onode=keyblock_orig->next; onode; onode=onode->next) { if (onode->pkt->pkttype == PKT_USER_ID) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (onode->pkt->pkt.signature) && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if (!found ) { kbnode_t n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= NODE_FLAG_A; ++*n_sigs; if(!opt.quiet) log_info( _("key %s: direct key signature added\n"), keystr(keyid)); } } } /* 3rd: try to merge new certificates in */ for (onode=keyblock_orig->next; onode; onode=onode->next) { if (!(onode->flag & NODE_FLAG_A) && onode->pkt->pkttype == PKT_USER_ID) { /* find the user id in the imported keyblock */ for (node=keyblock->next; node; node=node->next) if (node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if (node ) /* found: merge */ { rc = merge_sigs (onode, node, n_sigs); if (rc ) return rc; } } } /* 4th: add new user-ids */ for (node=keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID) { /* do we have this in the original keyblock */ for (onode=keyblock_orig->next; onode; onode=onode->next ) if (onode->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if (!onode ) /* this is a new user id: append */ { rc = append_new_uid (options, keyblock_orig, node, curtime, origin, url, n_sigs); if (rc ) return rc; ++*n_uids; } } } /* 5th: add new subkeys */ for (node=keyblock->next; node; node=node->next) { onode = NULL; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next) if (onode->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key)) break; if (!onode ) /* This is a new subkey: append. */ { rc = append_key (keyblock_orig, node, n_sigs); if (rc) return rc; ++*n_subk; } } else if (node->pkt->pkttype == PKT_SECRET_SUBKEY) { /* do we have this in the original keyblock? */ for (onode=keyblock_orig->next; onode; onode=onode->next ) if (onode->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_public_keys (onode->pkt->pkt.public_key, node->pkt->pkt.public_key) ) break; if (!onode ) /* This is a new subkey: append. */ { rc = append_key (keyblock_orig, node, n_sigs); if (rc ) return rc; ++*n_subk; } } } /* 6th: merge subkey certificates */ for (onode=keyblock_orig->next; onode; onode=onode->next) { if (!(onode->flag & NODE_FLAG_A) && (onode->pkt->pkttype == PKT_PUBLIC_SUBKEY || onode->pkt->pkttype == PKT_SECRET_SUBKEY)) { /* find the subkey in the imported keyblock */ for(node=keyblock->next; node; node=node->next) { if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; } if (node) /* Found: merge. */ { rc = merge_keysigs( onode, node, n_sigs); if (rc ) return rc; } } } return 0; } /* Helper function for merge_blocks. * * Append the new userid starting with NODE and all signatures to * KEYBLOCK. ORIGIN and URL conveys the usual key origin info. The * integer at N_SIGS is updated with the number of new signatures. */ static gpg_error_t append_new_uid (unsigned int options, kbnode_t keyblock, kbnode_t node, u32 curtime, int origin, const char *url, int *n_sigs) { gpg_error_t err; kbnode_t n; kbnode_t n_where = NULL; log_assert (node->pkt->pkttype == PKT_USER_ID); /* Find the right position for the new user id and its signatures. */ for (n = keyblock; n; n_where = n, n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY ) break; } if (!n) n_where = NULL; /* and append/insert */ while (node) { /* we add a clone to the original keyblock, because this * one is released first. */ n = clone_kbnode(node); if (n->pkt->pkttype == PKT_USER_ID && !(options & IMPORT_RESTORE) ) { err = insert_key_origin_uid (n->pkt->pkt.user_id, curtime, origin, url); if (err) return err; } if (n_where) { insert_kbnode( n_where, n, 0 ); n_where = n; } else add_kbnode( keyblock, n ); n->flag |= NODE_FLAG_A; node->flag |= NODE_FLAG_A; if (n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if (node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } /* Helper function for merge_blocks * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_USER_ID. * (how should we handle comment packets here?) */ static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs) { kbnode_t n, n2; int found = 0; log_assert (dst->pkt->pkttype == PKT_USER_ID); log_assert (src->pkt->pkttype == PKT_USER_ID); for (n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next) { if (n->pkt->pkttype != PKT_SIGNATURE ) continue; if (IS_SUBKEY_SIG (n->pkt->pkt.signature) || IS_SUBKEY_REV (n->pkt->pkt.signature) ) continue; /* skip signatures which are only valid on subkeys */ found = 0; for (n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next) if (!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature)) { found++; break; } if (!found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= NODE_FLAG_A; n->flag |= NODE_FLAG_A; ++*n_sigs; } } return 0; } /* Helper function for merge_blocks * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY. */ static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs) { kbnode_t n, n2; int found = 0; log_assert (dst->pkt->pkttype == PKT_PUBLIC_SUBKEY || dst->pkt->pkttype == PKT_SECRET_SUBKEY); for (n=src->next; n ; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_PUBLIC_KEY ) break; if (n->pkt->pkttype != PKT_SIGNATURE ) continue; found = 0; for (n2=dst->next; n2; n2 = n2->next) { if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->pkt->pkttype == PKT_PUBLIC_KEY ) break; if (n2->pkt->pkttype == PKT_SIGNATURE && (n->pkt->pkt.signature->keyid[0] == n2->pkt->pkt.signature->keyid[0]) && (n->pkt->pkt.signature->keyid[1] == n2->pkt->pkt.signature->keyid[1]) && (n->pkt->pkt.signature->timestamp <= n2->pkt->pkt.signature->timestamp) && (n->pkt->pkt.signature->sig_class == n2->pkt->pkt.signature->sig_class)) { found++; break; } } if (!found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= NODE_FLAG_A; n->flag |= NODE_FLAG_A; ++*n_sigs; } } return 0; } /* Helper function for merge_blocks. * Append the subkey starting with NODE and all signatures to KEYBLOCK. * Mark all new and copied packets by setting flag bit 0. */ static int append_key (kbnode_t keyblock, kbnode_t node, int *n_sigs) { kbnode_t n; log_assert (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY); while (node) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); add_kbnode( keyblock, n ); n->flag |= NODE_FLAG_A; node->flag |= NODE_FLAG_A; if (n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if (node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } diff --git a/g10/key-check.c b/g10/key-check.c index a22394de8..d32067b99 100644 --- a/g10/key-check.c +++ b/g10/key-check.c @@ -1,640 +1,655 @@ /* 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 * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include "gpg.h" #include "options.h" #include "packet.h" #include "keydb.h" #include "main.h" #include "../common/ttyio.h" #include "../common/i18n.h" #include "keyedit.h" #include "key-check.h" /* Order two signatures. The actual ordering isn't important. Our - goal is to ensure that identical signatures occur together. */ + * goal is to ensure that identical signatures occur together. */ static int sig_comparison (const void *av, const void *bv) { const KBNODE an = *(const KBNODE *) av; const KBNODE bn = *(const KBNODE *) bv; const PKT_signature *a; const PKT_signature *b; int ndataa; int ndatab; int i; log_assert (an->pkt->pkttype == PKT_SIGNATURE); log_assert (bn->pkt->pkttype == PKT_SIGNATURE); a = an->pkt->pkt.signature; b = bn->pkt->pkt.signature; if (a->digest_algo < b->digest_algo) return -1; if (a->digest_algo > b->digest_algo) return 1; ndataa = pubkey_get_nsig (a->pubkey_algo); ndatab = pubkey_get_nsig (b->pubkey_algo); if (ndataa != ndatab) return (ndataa < ndatab)? -1 : 1; for (i = 0; i < ndataa; i ++) { int c = gcry_mpi_cmp (a->data[i], b->data[i]); if (c != 0) return c; } /* Okay, they are equal. */ return 0; } -/* Perform a few sanity checks on a keyblock is okay and possibly - repair some damage. Concretely: - - - Detect duplicate signatures and remove them. - - - Detect out of order signatures and relocate them (e.g., a sig - over user id X located under subkey Y). - Note: this function does not remove signatures that don't belong or - components that are not signed! (Although it would be trivial to - do so.) - - If ONLY_SELFSIGS is true, then this function only reorders self - signatures (it still checks all signatures for duplicates, - however). - - Returns 1 if the keyblock was modified, 0 otherwise. */ +/* Perform a few sanity checks on a keyblock is okay and possibly + * repair some damage. Concretely: + * + * - Detect duplicate signatures and remove them. + * + * - Detect out of order signatures and relocate them (e.g., a sig + * over user id X located under subkey Y). + * + * Note: this function does not remove signatures that don't belong or + * components that are not signed! (Although it would be trivial to + * do so.) + * + * If ONLY_SELFSIGS is true, then this function only reorders self + * signatures (it still checks all signatures for duplicates, + * however). + * + * Allowed values for MODE are: + * -1 - print to the TTY + * 0 - print to stdout + * 1 - use log_info. + * + * Returns true if the keyblock was modified. */ int -key_check_all_keysigs (ctrl_t ctrl, kbnode_t kb, +key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, int only_selected, int only_selfsigs) { gpg_error_t err; + estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; PKT_public_key *pk; KBNODE n, n_next, *n_prevp, n2; char *pending_desc = NULL; PKT_public_key *issuer; KBNODE last_printed_component; KBNODE current_component = NULL; int dups = 0; int missing_issuer = 0; int reordered = 0; int bad_signature = 0; int missing_selfsig = 0; int modified = 0; 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; /* 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. */ issuer = NULL; last_printed_component = NULL; for (n_prevp = &kb, n = kb; n; /* If we moved n, then n_prevp is need valid. */ n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next) { PACKET *p; int processed_current_component; PKT_signature *sig; int rc; int dump_sig_params = 0; n_next = n->next; if (is_deleted_kbnode (n)) continue; p = n->pkt; if (issuer && issuer != pk) { free_public_key (issuer); issuer = NULL; } xfree (pending_desc); pending_desc = NULL; switch (p->pkttype) { case PKT_PUBLIC_KEY: log_assert (p->pkt.public_key == pk); if (only_selected && ! (n->flag & NODFLG_SELKEY)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("public key %s: timestamp: %s (%lld)\n", pk_keyid_str (pk), isotimestamp (pk->timestamp), (long long) pk->timestamp); current_component = n; break; case PKT_PUBLIC_SUBKEY: if (only_selected && ! (n->flag & NODFLG_SELKEY)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("subkey %s: timestamp: %s (%lld)\n", pk_keyid_str (p->pkt.public_key), isotimestamp (p->pkt.public_key->timestamp), (long long) p->pkt.public_key->timestamp); current_component = n; break; case PKT_USER_ID: if (only_selected && ! (n->flag & NODFLG_SELUID)) { current_component = NULL; break; } if (DBG_PACKET) log_debug ("user id: %s\n", p->pkt.user_id->attrib_data ? "[ photo id ]" : p->pkt.user_id->name); current_component = n; break; case PKT_SIGNATURE: if (! current_component) /* The current component is not selected, don't check the sigs under it. */ break; sig = n->pkt->pkt.signature; pending_desc = xasprintf (" sig: class: 0x%x, issuer: %s," " timestamp: %s (%lld), digest: %02x %02x", sig->sig_class, keystr (sig->keyid), isotimestamp (sig->timestamp), (long long) sig->timestamp, sig->digest_start[0], sig->digest_start[1]); if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0) issuer = pk; else /* Issuer is a different key. */ { if (only_selfsigs) continue; issuer = xmalloc (sizeof (*issuer)); err = get_pubkey (ctrl, issuer, sig->keyid); if (err) { xfree (issuer); issuer = NULL; if (DBG_PACKET) { if (pending_desc) log_debug ("%s", pending_desc); log_debug (" Can't check signature allegedly" " issued by %s: %s\n", keystr (sig->keyid), gpg_strerror (err)); } missing_issuer ++; break; } } if ((err = openpgp_pk_test_algo (sig->pubkey_algo))) { if (DBG_PACKET && pending_desc) log_debug ("%s", pending_desc); - tty_printf (_("can't check signature with unsupported" - " public-key algorithm (%d): %s.\n"), + log_info (_("can't check signature with unsupported" + " public-key algorithm (%d): %s.\n"), sig->pubkey_algo, gpg_strerror (err)); break; } if ((err = openpgp_md_test_algo (sig->digest_algo))) { if (DBG_PACKET && pending_desc) log_debug ("%s", pending_desc); - tty_printf (_("can't check signature with unsupported" - " message-digest algorithm %d: %s.\n"), + log_info (_("can't check signature with unsupported" + " message-digest algorithm %d: %s.\n"), sig->digest_algo, gpg_strerror (err)); break; } /* We iterate over the keyblock. Most likely, the matching component is the current component so always try that first. */ processed_current_component = 0; for (n2 = current_component; n2; n2 = (processed_current_component ? n2->next : kb), processed_current_component = 1) if (is_deleted_kbnode (n2)) continue; else if (processed_current_component && n2 == current_component) /* Don't process it twice. */ continue; else { err = check_signature_over_key_or_uid (ctrl, issuer, sig, kb, n2->pkt, NULL, NULL); if (! err) break; } /* n/sig is a signature and n2 is the component (public key, subkey or user id) that it signs, if any. current_component is that component that it appears to apply to (according to the ordering). */ if (current_component == n2) { if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Good signature over last key or uid!\n"); } rc = 0; } else if (n2) { log_assert (n2->pkt->pkttype == PKT_USER_ID || n2->pkt->pkttype == PKT_PUBLIC_KEY || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Good signature out of order!" " (Over %s (%d) '%s')\n", n2->pkt->pkttype == PKT_USER_ID ? "user id" : n2->pkt->pkttype == PKT_PUBLIC_SUBKEY ? "subkey" : "primary key", n2->pkt->pkttype, n2->pkt->pkttype == PKT_USER_ID ? n2->pkt->pkt.user_id->name : pk_keyid_str (n2->pkt->pkt.public_key)); } /* Reorder the packets: move the signature n to be just after n2. */ /* Unlink the signature. */ log_assert (n_prevp); *n_prevp = n->next; /* Insert the sig immediately after the component. */ n->next = n2->next; n2->next = n; reordered ++; modified = 1; rc = 0; } else { if (DBG_PACKET) { log_debug ("%s", pending_desc); log_debug (" Bad signature.\n"); } if (DBG_PACKET) dump_sig_params = 1; bad_signature ++; rc = GPG_ERR_BAD_SIGNATURE; } /* We don't cache the result here, because we haven't completely checked that the signature is legitimate. For instance, if we have a revocation certificate on Alice's key signed by Bob, the signature may be good, but we haven't checked that Bob is a designated revoker. */ /* cache_sig_result (sig, rc); */ { int has_selfsig = 0; if (! rc && issuer == pk) { if (n2->pkt->pkttype == PKT_PUBLIC_KEY && (/* Direct key signature. */ sig->sig_class == 0x1f /* Key revocation signature. */ || sig->sig_class == 0x20)) has_selfsig = 1; if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY && (/* Subkey binding sig. */ sig->sig_class == 0x18 /* Subkey revocation sig. */ || sig->sig_class == 0x28)) has_selfsig = 1; if (n2->pkt->pkttype == PKT_USER_ID && (/* Certification sigs. */ sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13 /* Certification revocation sig. */ || sig->sig_class == 0x30)) has_selfsig = 1; } if ((n2 && n2 != last_printed_component) || (! n2 && last_printed_component != current_component)) { int is_reordered = n2 && n2 != current_component; if (n2) last_printed_component = n2; else last_printed_component = current_component; if (!modified) ; else if (last_printed_component->pkt->pkttype == PKT_USER_ID) { - tty_printf ("uid "); - tty_print_utf8_string (last_printed_component - ->pkt->pkt.user_id->name, - last_printed_component - ->pkt->pkt.user_id->len); + tty_fprintf (fp, "uid "); + tty_print_utf8_string2 (fp, + last_printed_component + ->pkt->pkt.user_id->name, + last_printed_component + ->pkt->pkt.user_id->len, 0); } else if (last_printed_component->pkt->pkttype == PKT_PUBLIC_KEY) - tty_printf ("pub %s", - pk_keyid_str (last_printed_component - ->pkt->pkt.public_key)); + tty_fprintf (fp, "pub %s", + pk_keyid_str (last_printed_component + ->pkt->pkt.public_key)); else - tty_printf ("sub %s", - pk_keyid_str (last_printed_component - ->pkt->pkt.public_key)); + tty_fprintf (fp, "sub %s", + pk_keyid_str (last_printed_component + ->pkt->pkt.public_key)); if (modified) { if (is_reordered) - tty_printf (_(" (reordered signatures follow)")); - tty_printf ("\n"); + tty_fprintf (fp, _(" (reordered signatures follow)")); + if (mode > 0) + log_printf ("\n"); + else + tty_fprintf (fp, "\n"); } } if (modified) - keyedit_print_one_sig (ctrl, rc, kb, n, NULL, NULL, NULL, + keyedit_print_one_sig (ctrl, fp, rc, kb, n, NULL, NULL, NULL, has_selfsig, 0, only_selfsigs); } if (dump_sig_params) { int i; for (i = 0; i < pubkey_get_nsig (sig->pubkey_algo); i ++) { char buffer[1024]; size_t len; char *printable; gcry_mpi_print (GCRYMPI_FMT_USG, buffer, sizeof (buffer), &len, sig->data[i]); printable = bin2hex (buffer, len, NULL); - log_info (" %d: %s\n", i, printable); + log_debug (" %d: %s\n", i, printable); xfree (printable); } } break; default: if (DBG_PACKET) log_debug ("unhandled packet: %d\n", p->pkttype); break; } } xfree (pending_desc); pending_desc = NULL; if (issuer != pk) free_public_key (issuer); issuer = NULL; /* 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) { if (is_deleted_kbnode (n)) continue; p = n->pkt; switch (p->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_USER_ID: if (current_component && ! has_selfsig) missing_selfsig ++; current_component = n; has_selfsig = 0; break; case PKT_SIGNATURE: if (! current_component || has_selfsig) break; sig = n->pkt->pkt.signature; if (! (sig->flags.checked && sig->flags.valid)) break; if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0) /* Different issuer, couldn't be a self-sig. */ break; if (current_component->pkt->pkttype == PKT_PUBLIC_KEY && (/* Direct key signature. */ sig->sig_class == 0x1f /* Key revocation signature. */ || sig->sig_class == 0x20)) has_selfsig = 1; if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY && (/* Subkey binding sig. */ sig->sig_class == 0x18 /* Subkey revocation sig. */ || sig->sig_class == 0x28)) has_selfsig = 1; if (current_component->pkt->pkttype == PKT_USER_ID && (/* Certification sigs. */ sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13 /* Certification revocation sig. */ || sig->sig_class == 0x30)) has_selfsig = 1; break; default: if (current_component && ! has_selfsig) missing_selfsig ++; current_component = NULL; } } } if (dups || missing_issuer || bad_signature || reordered) - tty_printf (_("key %s:\n"), pk_keyid_str (pk)); + tty_fprintf (fp, _("key %s:\n"), pk_keyid_str (pk)); if (dups) - tty_printf (ngettext ("%d duplicate signature removed\n", - "%d duplicate signatures removed\n", dups), dups); + tty_fprintf (fp, + ngettext ("%d duplicate signature removed\n", + "%d duplicate signatures removed\n", dups), dups); if (missing_issuer) - tty_printf (ngettext ("%d signature not checked due to a missing key\n", - "%d signatures not checked due to missing keys\n", - missing_issuer), missing_issuer); + tty_fprintf (fp, + ngettext ("%d signature not checked due to a missing key\n", + "%d signatures not checked due to missing keys\n", + missing_issuer), missing_issuer); if (bad_signature) - tty_printf (ngettext ("%d bad signature\n", - "%d bad signatures\n", - bad_signature), bad_signature); + tty_fprintf (fp, + ngettext ("%d bad signature\n", + "%d bad signatures\n", + bad_signature), bad_signature); if (reordered) - tty_printf (ngettext ("%d signature reordered\n", - "%d signatures reordered\n", - reordered), reordered); + tty_fprintf (fp, + ngettext ("%d signature reordered\n", + "%d signatures reordered\n", + reordered), reordered); if (only_selfsigs && (bad_signature || reordered)) - tty_printf (_("Warning: errors found and only checked self-signatures," - " run '%s' to check all signatures.\n"), "check"); + tty_fprintf (fp, _("Warning: errors found and only checked self-signatures," + " run '%s' to check all signatures.\n"), "check"); return modified; } diff --git a/g10/key-check.h b/g10/key-check.h index 3e4dd1031..9f7886ca0 100644 --- a/g10/key-check.h +++ b/g10/key-check.h @@ -1,28 +1,28 @@ /* key-check.h - Detect and fix various problems with keys * Copyright (C) 2017 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef GNUPG_G10_PACKET_TOOLS_H #define GNUPG_G10_PACKET_TOOLS_H #include "gpg.h" -int key_check_all_keysigs (ctrl_t ctrl, kbnode_t kb, +int key_check_all_keysigs (ctrl_t ctrl, int mode, kbnode_t kb, int only_selected, int only_selfsigs); #endif /* GNUPG_G10_PACKET_TOOLS_H */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 0a90cf5b7..e221b320e 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,6228 +1,6232 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #ifdef HAVE_LIBREADLINE # define GNUPG_LIBREADLINE_H_INCLUDED # include #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "photoid.h" #include "../common/util.h" #include "main.h" #include "trustdb.h" #include "filter.h" #include "../common/ttyio.h" #include "../common/status.h" #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "../common/host2net.h" #include "tofu.h" #include "key-check.h" #include "keyedit.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose); static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs); static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn); static void show_key_and_fingerprint (ctrl_t ctrl, kbnode_t keyblock, int with_subkeys); static void show_key_and_grip (kbnode_t keyblock); static void subkey_expire_warning (kbnode_t keyblock); static int menu_adduid (ctrl_t ctrl, kbnode_t keyblock, int photo, const char *photo_name, const char *uidstr); static void menu_deluid (KBNODE pub_keyblock); static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration); static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock); static int menu_set_notation (ctrl_t ctrl, const char *string, kbnode_t pub_keyblock); static int menu_select_uid (KBNODE keyblock, int idx); static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash); static int menu_select_key (KBNODE keyblock, int idx, char *p); static int count_uids (KBNODE keyblock); static int count_uids_with_flag (KBNODE keyblock, unsigned flag); static int count_keys_with_flag (KBNODE keyblock, unsigned flag); static int count_selected_uids (KBNODE keyblock); static int real_uids_left (KBNODE keyblock); static int count_selected_keys (KBNODE keyblock); static int menu_revsig (ctrl_t ctrl, kbnode_t keyblock); static int menu_revuid (ctrl_t ctrl, kbnode_t keyblock); static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified); static int menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock); #ifndef NO_TRUST_MODELS static int enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable); #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock); static int update_trust = 0; #define CONTROL_D ('D' - 'A' + 1) struct sign_attrib { int non_exportable, non_revocable; struct revocation_reason_info *reason; byte trust_depth, trust_value; char *trust_regexp; }; /* TODO: Fix duplicated code between here and the check-sigs/list-sigs code in keylist.c. */ static int print_and_check_one_sig_colon (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key) { PKT_signature *sig = node->pkt->pkt.signature; int rc, sigrc; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ rc = check_key_signature (ctrl, keyblock, node, is_selfsig); switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { es_printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:", sigrc, sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], (ulong) sig->timestamp, (ulong) sig->expiredate); if (sig->trust_depth || sig->trust_value) es_printf ("%d %d", sig->trust_depth, sig->trust_value); es_printf (":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_printf ("::%02x%c\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (opt.show_subpackets) print_subpackets_colon (sig); } return (sigrc == '!'); } /* * Print information about a signature (rc is its status), check it * and return true if the signature is okay. NODE must be a signature * packet. With EXTENDED set all possible signature list options will * always be printed. */ int -keyedit_print_one_sig (ctrl_t ctrl, int rc, kbnode_t keyblock, kbnode_t node, +keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, + int rc, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; int is_rev = sig->sig_class == 0x30; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { - tty_printf ("%s%c%c %c%c%c%c%c%c %s %s", + tty_fprintf (fp, "%s%c%c %c%c%c%c%c%c %s %s", is_rev ? "rev" : "sig", 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 ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended ) - tty_printf (" %s", expirestr_from_sig (sig)); - tty_printf (" "); + tty_fprintf (fp, " %s", expirestr_from_sig (sig)); + tty_fprintf (fp, " "); if (sigrc == '%') - tty_printf ("[%s] ", gpg_strerror (rc)); + tty_fprintf (fp, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (is_selfsig) { - tty_printf (is_rev ? _("[revocation]") : _("[self-signature]")); + tty_fprintf (fp, is_rev ? _("[revocation]") : _("[self-signature]")); if (extended && sig->flags.chosen_selfsig) - tty_printf ("*"); + tty_fprintf (fp, "*"); } else { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n); - tty_print_utf8_string2 (NULL, p, n, + tty_print_utf8_string2 (fp, p, n, opt.screen_columns - keystrlen () - 26 - ((opt. list_options & LIST_SHOW_SIG_EXPIRE) ? 11 : 0)); xfree (p); } - tty_printf ("\n"); + if (fp == log_get_stream ()) + log_printf ("\n"); + else + tty_fprintf (fp, "\n"); if (sig->flags.policy_url && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended)) - show_policy_url (sig, 3, -1); + show_policy_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0)); if (sig->flags.notation && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended)) - show_notation (sig, 3, -1, + show_notation (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 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) || extended)) - show_keyserver_url (sig, 3, -1); + show_keyserver_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0)); if (extended) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; const unsigned char *s; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (s && *s) - tty_printf (" [primary]\n"); + tty_fprintf (fp, " [primary]\n"); s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (s && buf32_to_u32 (s)) - tty_printf (" [expires: %s]\n", - isotimestamp (pk->timestamp + buf32_to_u32 (s))); + tty_fprintf (fp, " [expires: %s]\n", + isotimestamp (pk->timestamp + buf32_to_u32 (s))); } } return (sigrc == '!'); } static int print_and_check_one_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key, int extended) { int rc; rc = check_key_signature (ctrl, keyblock, node, is_selfsig); - return keyedit_print_one_sig (ctrl, rc, + return keyedit_print_one_sig (ctrl, NULL, rc, keyblock, node, inv_sigs, no_key, oth_err, *is_selfsig, print_without_key, extended); } static int sign_mk_attrib (PKT_signature * sig, void *opaque) { struct sign_attrib *attrib = opaque; byte buf[8]; if (attrib->non_exportable) { buf[0] = 0; /* not exportable */ build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, buf, 1); } if (attrib->non_revocable) { buf[0] = 0; /* not revocable */ build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); } if (attrib->reason) revocation_reason_build_cb (sig, attrib->reason); if (attrib->trust_depth) { /* Not critical. If someone doesn't understand trust sigs, this can still be a valid regular signature. */ buf[0] = attrib->trust_depth; buf[1] = attrib->trust_value; build_sig_subpkt (sig, SIGSUBPKT_TRUST, buf, 2); /* Critical. If someone doesn't understands regexps, this whole sig should be invalid. Note the +1 for the length - regexps are null terminated. */ if (attrib->trust_regexp) build_sig_subpkt (sig, SIGSUBPKT_FLAG_CRITICAL | SIGSUBPKT_REGEXP, attrib->trust_regexp, strlen (attrib->trust_regexp) + 1); } return 0; } static void trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp) { char *p; *trust_value = 0; *trust_depth = 0; *regexp = NULL; /* Same string as pkclist.c:do_edit_ownertrust */ tty_printf (_ ("Please decide how far you trust this user to correctly verify" " other users' keys\n(by looking at passports, checking" " fingerprints from different sources, etc.)\n")); tty_printf ("\n"); tty_printf (_(" %d = I trust marginally\n"), 1); tty_printf (_(" %d = I trust fully\n"), 2); tty_printf ("\n"); while (*trust_value == 0) { p = cpr_get ("trustsig_prompt.trust_value", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); /* 60 and 120 are as per RFC2440 */ if (p[0] == '1' && !p[1]) *trust_value = 60; else if (p[0] == '2' && !p[1]) *trust_value = 120; xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter the depth of this trust signature.\n" "A depth greater than 1 allows the key you are" " signing to make\n" "trust signatures on your behalf.\n")); tty_printf ("\n"); while (*trust_depth == 0) { p = cpr_get ("trustsig_prompt.trust_depth", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); *trust_depth = atoi (p); xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter a domain to restrict this signature, " "or enter for none.\n")); tty_printf ("\n"); p = cpr_get ("trustsig_prompt.trust_regexp", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); if (strlen (p) > 0) { char *q = p; int regexplen = 100, ind; *regexp = xmalloc (regexplen); /* Now mangle the domain the user entered into a regexp. To do this, \-escape everything that isn't alphanumeric, and attach "<[^>]+[@.]" to the front, and ">$" to the end. */ strcpy (*regexp, "<[^>]+[@.]"); ind = strlen (*regexp); while (*q) { if (!((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9'))) (*regexp)[ind++] = '\\'; (*regexp)[ind++] = *q; if ((regexplen - ind) < 3) { regexplen += 100; *regexp = xrealloc (*regexp, regexplen); } q++; } (*regexp)[ind] = '\0'; strcat (*regexp, ">$"); } xfree (p); tty_printf ("\n"); } /* * Loop over all LOCUSR and sign the uids after asking. If no * user id is marked, all user ids will be signed; if some user_ids * are marked only those will be signed. If QUICK is true the * function won't ask the user and use sensible defaults. */ static int sign_uids (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, strlist_t locusr, int *ret_modified, int local, int nonrevocable, int trust, int interactive, int quick) { int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; PKT_public_key *pk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk = NULL; int select_all = !count_selected_uids (keyblock) || interactive; /* Build a list of all signators. * * We use the CERT flag to request the primary which must always * be one which is capable of signing keys. I can't see a reason * why to sign keys using a subkey. Implementation of USAGE_CERT * is just a hack in getkey.c and does not mean that a subkey * marked as certification capable will be used. */ rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); if (rc) goto leave; /* Loop over all signators. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { u32 sk_keyid[2], pk_keyid[2]; char *p, *trust_regexp = NULL; int class = 0, selfsig = 0; u32 duration = 0, timestamp = 0; byte trust_depth = 0, trust_value = 0; pk = sk_rover->pk; keyid_from_pk (pk, sk_keyid); /* Set mark A for all selected user ids. */ for (node = keyblock; node; node = node->next) { if (select_all || (node->flag & NODFLG_SELUID)) node->flag |= NODFLG_MARK_A; else node->flag &= ~NODFLG_MARK_A; } /* Reset mark for uids which are already signed. */ uidnode = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { primary_pk = node->pkt->pkt.public_key; keyid_from_pk (primary_pk, pk_keyid); /* Is this a self-sig? */ if (pk_keyid[0] == sk_keyid[0] && pk_keyid[1] == sk_keyid[1]) selfsig = 1; } else if (node->pkt->pkttype == PKT_USER_ID) { uidnode = (node->flag & NODFLG_MARK_A) ? node : NULL; if (uidnode) { int yesreally = 0; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); if (opt.only_sign_text_ids && uidnode->pkt->pkt.user_id->attribs) { tty_fprintf (fp, _("Skipping user ID \"%s\"," " which is not a text ID.\n"), user); uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (uidnode->pkt->pkt.user_id->flags.revoked) { tty_fprintf (fp, _("User ID \"%s\" is revoked."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.revoke_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (uidnode->pkt->pkt.user_id->flags.expired) { tty_fprintf (fp, _("User ID \"%s\" is expired."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.expire_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (!uidnode->pkt->pkt.user_id->created && !selfsig) { tty_fprintf (fp, _("User ID \"%s\" is not self-signed."), user); if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.nosig_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } if (uidnode && interactive && !yesreally && !quick) { tty_fprintf (fp, _("User ID \"%s\" is signable. "), user); if (!cpr_get_answer_is_yes ("sign_uid.sign_okay", _("Sign it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } } xfree (user); } } else if (uidnode && node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class & ~3) == 0x10) { if (sk_keyid[0] == node->pkt->pkt.signature->keyid[0] && sk_keyid[1] == node->pkt->pkt.signature->keyid[1]) { char buf[50]; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); /* It's a v3 self-sig. Make it into a v4 self-sig? */ if (node->pkt->pkt.signature->version < 4 && selfsig && !quick) { tty_fprintf (fp, _("The self-signature on \"%s\"\n" "is a PGP 2.x-style signature.\n"), user); /* Note that the regular PGP2 warning below still applies if there are no v4 sigs on this key at all. */ if (opt.expert) if (cpr_get_answer_is_yes ("sign_uid.v4_promote_okay", _("Do you want to promote " "it to an OpenPGP self-" "signature? (y/N) "))) { node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Is the current signature expired? */ if (node->pkt->pkt.signature->flags.expired) { tty_fprintf (fp, _("Your current signature on \"%s\"\n" "has expired.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.replace_expired_okay", _("Do you want to issue a " "new signature to replace " "the expired one? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } if (!node->pkt->pkt.signature->flags.exportable && !local) { /* It's a local sig, and we want to make a exportable sig. */ tty_fprintf (fp, _("Your current signature on \"%s\"\n" "is a local signature.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.local_promote_okay", _("Do you want to promote " "it to a full exportable " "signature? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Fixme: see whether there is a revocation in which * case we should allow signing it again. */ if (!node->pkt->pkt.signature->flags.exportable && local) tty_fprintf ( fp, _("\"%s\" was already locally signed by key %s\n"), user, keystr_from_pk (pk)); else tty_fprintf (fp, _("\"%s\" was already signed by key %s\n"), user, keystr_from_pk (pk)); if (opt.expert && !quick && cpr_get_answer_is_yes ("sign_uid.dupe_okay", _("Do you want to sign it " "again anyway? (y/N) "))) { /* Don't delete the old sig here since this is an --expert thing. */ xfree (user); continue; } snprintf (buf, sizeof buf, "%08lX%08lX", (ulong) pk->keyid[0], (ulong) pk->keyid[1]); write_status_text (STATUS_ALREADY_SIGNED, buf); uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */ xfree (user); } } } /* Check whether any uids are left for signing. */ if (!count_uids_with_flag (keyblock, NODFLG_MARK_A)) { tty_fprintf (fp, _("Nothing to sign with key %s\n"), keystr_from_pk (pk)); continue; } /* Ask whether we really should sign these user id(s). */ tty_fprintf (fp, "\n"); show_key_with_all_names (ctrl, fp, keyblock, 1, 0, 1, 0, 0, 0); tty_fprintf (fp, "\n"); if (primary_pk->expiredate && !selfsig) { /* Static analyzer note: A claim that PRIMARY_PK might be NULL is not correct because it set from the public key packet which is always the first packet in a keyblock and parsed in the above loop over the keyblock. In case the keyblock has no packets at all and thus the loop was not entered the above count_uids_with_flag would have detected this case. */ u32 now = make_timestamp (); if (primary_pk->expiredate <= now) { tty_fprintf (fp, _("This key has expired!")); if (opt.expert && !quick) { tty_fprintf (fp, " "); if (!cpr_get_answer_is_yes ("sign_uid.expired_okay", _("Are you sure you still " "want to sign it? (y/N) "))) continue; } else { tty_fprintf (fp, _(" Unable to sign.\n")); continue; } } else { tty_fprintf (fp, _("This key is due to expire on %s.\n"), expirestr_from_pk (primary_pk)); if (opt.ask_cert_expire && !quick) { char *answer = cpr_get ("sign_uid.expire", _("Do you want your signature to " "expire at the same time? (Y/n) ")); if (answer_is_yes_no_default (answer, 1)) { /* This fixes the signature timestamp we're going to make as now. This is so the expiration date is exactly correct, and not a few seconds off (due to the time it takes to answer the questions, enter the passphrase, etc). */ timestamp = now; duration = primary_pk->expiredate - now; } cpr_kill_prompt (); xfree (answer); } } } /* Only ask for duration if we haven't already set it to match the expiration of the pk */ if (!duration && !selfsig) { if (opt.ask_cert_expire && !quick) duration = ask_expire_interval (1, opt.def_cert_expire); else duration = parse_expire_string (opt.def_cert_expire); } if (selfsig) ; else { if (opt.batch || !opt.ask_cert_level || quick) class = 0x10 + opt.def_cert_level; else { char *answer; tty_fprintf (fp, _("How carefully have you verified the key you are " "about to sign actually belongs\nto the person " "named above? If you don't know what to " "answer, enter \"0\".\n")); tty_fprintf (fp, "\n"); tty_fprintf (fp, _(" (0) I will not answer.%s\n"), opt.def_cert_level == 0 ? " (default)" : ""); tty_fprintf (fp, _(" (1) I have not checked at all.%s\n"), opt.def_cert_level == 1 ? " (default)" : ""); tty_fprintf (fp, _(" (2) I have done casual checking.%s\n"), opt.def_cert_level == 2 ? " (default)" : ""); tty_fprintf (fp, _(" (3) I have done very careful checking.%s\n"), opt.def_cert_level == 3 ? " (default)" : ""); tty_fprintf (fp, "\n"); while (class == 0) { answer = cpr_get ("sign_uid.class", _("Your selection? " "(enter '?' for more information): ")); if (answer[0] == '\0') class = 0x10 + opt.def_cert_level; /* Default */ else if (ascii_strcasecmp (answer, "0") == 0) class = 0x10; /* Generic */ else if (ascii_strcasecmp (answer, "1") == 0) class = 0x11; /* Persona */ else if (ascii_strcasecmp (answer, "2") == 0) class = 0x12; /* Casual */ else if (ascii_strcasecmp (answer, "3") == 0) class = 0x13; /* Positive */ else tty_fprintf (fp, _("Invalid selection.\n")); xfree (answer); } } if (trust && !quick) trustsig_prompt (&trust_value, &trust_depth, &trust_regexp); } if (!quick) { p = get_user_id_native (ctrl, sk_keyid); tty_fprintf (fp, _("Are you sure that you want to sign this key with your\n" "key \"%s\" (%s)\n"), p, keystr_from_pk (pk)); xfree (p); } if (selfsig) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("This will be a self-signature.\n")); if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-revocable.\n")); } } else { if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-revocable.\n")); } switch (class) { case 0x11: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have not checked this key at all.\n")); break; case 0x12: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key casually.\n")); break; case 0x13: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key very carefully.\n")); break; } } tty_fprintf (fp, "\n"); if (opt.batch && opt.answer_yes) ; else if (quick) ; else if (!cpr_get_answer_is_yes ("sign_uid.okay", _("Really sign? (y/N) "))) continue; /* Now we can sign the user ids. */ reloop: /* (Must use this, because we are modifying the list.) */ primary_pk = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) primary_pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_MARK_A)) { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; log_assert (primary_pk); memset (&attrib, 0, sizeof attrib); attrib.non_exportable = local; attrib.non_revocable = nonrevocable; attrib.trust_depth = trust_depth; attrib.trust_value = trust_value; attrib.trust_regexp = trust_regexp; node->flag &= ~NODFLG_MARK_A; /* We force creation of a v4 signature for local * signatures, otherwise we would not generate the * subpacket with v3 keys and the signature becomes * exportable. */ if (selfsig) rc = make_keysig_packet (ctrl, &sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, 0x13, 0, 0, 0, keygen_add_std_prefs, primary_pk, NULL); else rc = make_keysig_packet (ctrl, &sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, class, 0, timestamp, duration, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto leave; } *ret_modified = 1; /* We changed the keyblock. */ update_trust = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), PKT_SIGNATURE); goto reloop; } } /* Delete any sigs that got promoted */ for (node = keyblock; node; node = node->next) if (node->flag & NODFLG_DELSIG) delete_kbnode (node); } /* End loop over signators. */ leave: release_sk_list (sk_list); return rc; } /* * Change the passphrase of the primary and all secondary keys. Note * that it is common to use only one passphrase for the primary and * all subkeys. However, this is now (since GnuPG 2.1) all up to the * gpg-agent. Returns 0 on success or an error code. */ static gpg_error_t change_passphrase (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; kbnode_t node; PKT_public_key *pk; int any; u32 keyid[2], subid[2]; char *hexgrip = NULL; char *cache_nonce = NULL; char *passwd_nonce = NULL; node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; public key missing!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); /* Check whether it is likely that we will be able to change the passphrase for any subkey. */ for (any = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *serialno; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); if (!err && serialno) ; /* Key on card. */ else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; /* Maybe stub key. */ else if (!err) any = 1; /* Key is known. */ else log_error ("key %s: error getting keyinfo from agent: %s\n", keystr_with_sub (keyid, subid), gpg_strerror (err)); xfree (serialno); } } err = 0; if (!any) { tty_printf (_("Key has only stub or on-card key items - " "no passphrase to change.\n")); goto leave; } /* Change the passphrase for all keys. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *desc; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_NORMAL, 1); err = agent_passwd (ctrl, hexgrip, desc, 0, &cache_nonce, &passwd_nonce); xfree (desc); if (err) log_log ((gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) ? GPGRT_LOG_INFO : GPGRT_LOG_ERROR, _("key %s: error changing passphrase: %s\n"), keystr_with_sub (keyid, subid), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; } } leave: xfree (hexgrip); xfree (cache_nonce); xfree (passwd_nonce); return err; } /* Fix various problems in the keyblock. Returns true if the keyblock was changed. Note that a pointer to the keyblock must be given and the function may change it (i.e. replacing the first node). */ static int fix_keyblock (ctrl_t ctrl, kbnode_t *keyblockp) { int changed = 0; if (collapse_uids (keyblockp)) changed++; - if (key_check_all_keysigs (ctrl, *keyblockp, 0, 1)) + if (key_check_all_keysigs (ctrl, 1, *keyblockp, 0, 1)) changed++; reorder_keyblock (*keyblockp); /* If we modified the keyblock, make sure the flags are right. */ if (changed) merge_keys_and_selfsig (ctrl, *keyblockp); return changed; } static int parse_sign_type (const char *str, int *localsig, int *nonrevokesig, int *trustsig) { const char *p = str; while (*p) { if (ascii_strncasecmp (p, "l", 1) == 0) { *localsig = 1; p++; } else if (ascii_strncasecmp (p, "nr", 2) == 0) { *nonrevokesig = 1; p += 2; } else if (ascii_strncasecmp (p, "t", 1) == 0) { *trustsig = 1; p++; } else return 0; } return 1; } /* * Menu driven key editor. If seckey_check is true, then a secret key * that matches username will be looked for. If it is false, not all * commands will be available. * * Note: to keep track of certain selections we use node->mark MARKBIT_xxxx. */ /* Need an SK for this command */ #define KEYEDIT_NEED_SK 1 /* Cannot be viewing the SK for this command */ #define KEYEDIT_NOT_SK 2 /* Must be viewing the SK for this command */ #define KEYEDIT_ONLY_SK 4 /* Match the tail of the string */ #define KEYEDIT_TAIL_MATCH 8 enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ cmdSHOWPREF, cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP }; static struct { const char *name; enum cmdids id; int flags; const char *desc; } cmds[] = { { "quit", cmdQUIT, 0, N_("quit this menu")}, { "q", cmdQUIT, 0, NULL}, { "save", cmdSAVE, 0, N_("save and quit")}, { "help", cmdHELP, 0, N_("show this help")}, { "?", cmdHELP, 0, NULL}, { "fpr", cmdFPR, 0, N_("show key fingerprint")}, { "grip", cmdGRIP, 0, N_("show the keygrip")}, { "list", cmdLIST, 0, N_("list key and user IDs")}, { "l", cmdLIST, 0, NULL}, { "uid", cmdSELUID, 0, N_("select user ID N")}, { "key", cmdSELKEY, 0, N_("select subkey N")}, { "check", cmdCHECK, 0, N_("check signatures")}, { "c", cmdCHECK, 0, NULL}, { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH, N_("sign selected user IDs [* see below for related commands]")}, { "s", cmdSIGN, KEYEDIT_NOT_SK, NULL}, /* "lsign" and friends will never match since "sign" comes first and it is a tail match. They are just here so they show up in the help menu. */ { "lsign", cmdNOP, 0, N_("sign selected user IDs locally")}, { "tsign", cmdNOP, 0, N_("sign selected user IDs with a trust signature")}, { "nrsign", cmdNOP, 0, N_("sign selected user IDs with a non-revocable signature")}, { "debug", cmdDEBUG, 0, NULL}, { "adduid", cmdADDUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a user ID")}, { "addphoto", cmdADDPHOTO, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a photo ID")}, { "deluid", cmdDELUID, KEYEDIT_NOT_SK, N_("delete selected user IDs")}, /* delphoto is really deluid in disguise */ { "delphoto", cmdDELUID, KEYEDIT_NOT_SK, NULL}, { "addkey", cmdADDKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a subkey")}, #ifdef ENABLE_CARD_SUPPORT { "addcardkey", cmdADDCARDKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a key to a smartcard")}, { "keytocard", cmdKEYTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, N_("move a key to a smartcard")}, { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, N_("move a backup key to a smartcard")}, #endif /*ENABLE_CARD_SUPPORT */ { "delkey", cmdDELKEY, KEYEDIT_NOT_SK, N_("delete selected subkeys")}, { "addrevoker", cmdADDREVOKER, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("add a revocation key")}, { "delsig", cmdDELSIG, KEYEDIT_NOT_SK, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("change the expiration date for the key or selected subkeys")}, { "primary", cmdPRIMARY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("flag the selected user ID as primary")}, { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, /* Dummy command. */ { "t", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, { "pref", cmdPREF, KEYEDIT_NOT_SK, N_("list preferences (expert)")}, { "showpref", cmdSHOWPREF, KEYEDIT_NOT_SK, N_("list preferences (verbose)")}, { "setpref", cmdSETPREF, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set preference list for the selected user IDs")}, { "updpref", cmdSETPREF, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "keyserver", cmdPREFKS, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set the preferred keyserver URL for the selected user IDs")}, { "notation", cmdNOTATION, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("set a notation for the selected user IDs")}, { "passwd", cmdPASSWD, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("change the passphrase")}, { "password", cmdPASSWD, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, #ifndef NO_TRUST_MODELS { "trust", cmdTRUST, KEYEDIT_NOT_SK, N_("change the ownertrust")}, #endif /*!NO_TRUST_MODELS*/ { "revsig", cmdREVSIG, KEYEDIT_NOT_SK, N_("revoke signatures on the selected user IDs")}, { "revuid", cmdREVUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("revoke selected user IDs")}, { "revphoto", cmdREVUID, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL}, { "revkey", cmdREVKEY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, N_("revoke key or selected subkeys")}, #ifndef NO_TRUST_MODELS { "enable", cmdENABLEKEY, KEYEDIT_NOT_SK, N_("enable key")}, { "disable", cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key")}, #endif /*!NO_TRUST_MODELS*/ { "showphoto", cmdSHOWPHOTO, 0, N_("show selected photo IDs")}, { "clean", cmdCLEAN, KEYEDIT_NOT_SK, N_("compact unusable user IDs and remove unusable signatures from key")}, { "minimize", cmdMINIMIZE, KEYEDIT_NOT_SK, N_("compact unusable user IDs and remove all signatures from key")}, { NULL, cmdNONE, 0, NULL} }; #ifdef HAVE_LIBREADLINE /* These two functions are used by readline for command completion. */ static char * command_generator (const char *text, int state) { static int list_index, len; const char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next partial match */ while ((name = cmds[list_index].name)) { /* Only complete commands that have help text */ if (cmds[list_index++].desc && strncmp (name, text, len) == 0) return strdup (name); } return NULL; } static char ** keyedit_completion (const char *text, int start, int end) { /* If we are at the start of a line, we try and command-complete. If not, just do nothing for now. */ (void) end; if (start == 0) return rl_completion_matches (text, command_generator); rl_attempted_completion_over = 1; return NULL; } #endif /* HAVE_LIBREADLINE */ /* Main function of the menu driven key editor. */ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check) { enum cmdids cmd = 0; gpg_error_t err = 0; KBNODE keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int have_seckey = 0; char *answer = NULL; int redisplay = 1; int modified = 0; int sec_shadowing = 0; int run_subkey_warnings = 0; int have_commands = !!commands; if (opt.command_fd != -1) ; else if (opt.batch && !have_commands) { log_error (_("can't do this in batch mode\n")); goto leave; } #ifdef HAVE_W32_SYSTEM /* Due to Windows peculiarities we need to make sure that the trustdb stale check is done before we open another file (i.e. by searching for a key). In theory we could make sure that the files are closed after use but the open/close caches inhibits that and flushing the cache right before the stale check is not easy to implement. Thus we take the easy way out and run the stale check as early as possible. Note, that for non- W32 platforms it is run indirectly trough a call to get_validity (). */ check_trustdb_stale (ctrl); #endif /* Get the public key */ err = get_pubkey_byname (ctrl, NULL, NULL, username, &keyblock, &kdbhd, 1, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } if (fix_keyblock (ctrl, &keyblock)) modified++; /* See whether we have a matching secret key. */ if (seckey_check) { have_seckey = !agent_probe_any_secret_key (ctrl, keyblock); if (have_seckey && !quiet) tty_printf (_("Secret key is available.\n")); } /* Main command loop. */ for (;;) { int i, arg_number, photo; const char *arg_string = ""; char *p; PKT_public_key *pk = keyblock->pkt->pkt.public_key; tty_printf ("\n"); if (redisplay && !quiet) { /* Show using flags: with_revoker, with_subkeys. */ show_key_with_all_names (ctrl, NULL, keyblock, 0, 1, 0, 1, 0, 0); tty_printf ("\n"); redisplay = 0; } if (run_subkey_warnings) { run_subkey_warnings = 0; if (!count_selected_keys (keyblock)) subkey_expire_warning (keyblock); } do { xfree (answer); if (have_commands) { if (commands) { answer = xstrdup (commands->d); commands = commands->next; } else if (opt.batch) { answer = xstrdup ("quit"); } else have_commands = 0; } if (!have_commands) { #ifdef HAVE_LIBREADLINE tty_enable_completion (keyedit_completion); #endif answer = cpr_get_no_help ("keyedit.prompt", GPG_NAME "> "); cpr_kill_prompt (); tty_disable_completion (); } trim_spaces (answer); } while (*answer == '#'); arg_number = 0; /* Here is the init which egcc complains about. */ photo = 0; /* Same here. */ if (!*answer) cmd = cmdLIST; else if (*answer == CONTROL_D) cmd = cmdQUIT; else if (digitp (answer)) { cmd = cmdSELUID; arg_number = atoi (answer); } else { if ((p = strchr (answer, ' '))) { *p++ = 0; trim_spaces (answer); trim_spaces (p); arg_number = atoi (p); arg_string = p; } for (i = 0; cmds[i].name; i++) { if (cmds[i].flags & KEYEDIT_TAIL_MATCH) { size_t l = strlen (cmds[i].name); size_t a = strlen (answer); if (a >= l) { if (!ascii_strcasecmp (&answer[a - l], cmds[i].name)) { answer[a - l] = '\0'; break; } } } else if (!ascii_strcasecmp (answer, cmds[i].name)) break; } if ((cmds[i].flags & KEYEDIT_NEED_SK) && !have_seckey) { tty_printf (_("Need the secret key to do this.\n")); cmd = cmdNOP; } else cmd = cmds[i].id; } /* Dispatch the command. */ switch (cmd) { case cmdHELP: for (i = 0; cmds[i].name; i++) { if ((cmds[i].flags & KEYEDIT_NEED_SK) && !have_seckey) ; /* Skip those item if we do not have the secret key. */ else if (cmds[i].desc) tty_printf ("%-11s %s\n", cmds[i].name, _(cmds[i].desc)); } tty_printf ("\n"); tty_printf (_("* The 'sign' command may be prefixed with an 'l' for local " "signatures (lsign),\n" " a 't' for trust signatures (tsign), an 'nr' for " "non-revocable signatures\n" " (nrsign), or any combination thereof (ltsign, " "tnrsign, etc.).\n")); break; case cmdLIST: redisplay = 1; break; case cmdFPR: show_key_and_fingerprint (ctrl, keyblock, (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1)))); break; case cmdGRIP: show_key_and_grip (keyblock); break; case cmdSELUID: if (strlen (arg_string) == NAMEHASH_LEN * 2) redisplay = menu_select_uid_namehash (keyblock, arg_string); else { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ redisplay = menu_select_uid (keyblock, arg_number); } break; case cmdSELKEY: { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ if (menu_select_key (keyblock, arg_number, p)) redisplay = 1; } break; case cmdCHECK: - if (key_check_all_keysigs (ctrl, keyblock, + if (key_check_all_keysigs (ctrl, -1, keyblock, count_selected_uids (keyblock), !strcmp (arg_string, "selfsig"))) modified = 1; break; case cmdSIGN: { int localsig = 0, nonrevokesig = 0, trustsig = 0, interactive = 0; if (pk->flags.revoked) { tty_printf (_("Key is revoked.")); if (opt.expert) { tty_printf (" "); if (!cpr_get_answer_is_yes ("keyedit.sign_revoked.okay", _("Are you sure you still want to sign it? (y/N) "))) break; } else { tty_printf (_(" Unable to sign.\n")); break; } } if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock)) { int result; if (opt.only_sign_text_ids) result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all user IDs? (y/N) ")); else result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all text user IDs? (y/N) ")); if (! result) { if (opt.interactive) interactive = 1; else { tty_printf (_("Hint: Select the user IDs to sign\n")); have_commands = 0; break; } } } /* What sort of signing are we doing? */ if (!parse_sign_type (answer, &localsig, &nonrevokesig, &trustsig)) { tty_printf (_("Unknown signature type '%s'\n"), answer); break; } sign_uids (ctrl, NULL, keyblock, locusr, &modified, localsig, nonrevokesig, trustsig, interactive, 0); } break; case cmdDEBUG: dump_kbnode (keyblock); break; case cmdTOGGLE: /* The toggle command is a leftover from old gpg versions where we worked with a secret and a public keyring. It is not necessary anymore but we keep this command for the sake of scripts using it. */ redisplay = 1; break; case cmdADDPHOTO: if (RFC2440) { tty_printf (_("This command is not allowed while in %s mode.\n"), gnupg_compliance_option_string (opt.compliance)); break; } photo = 1; /* fall through */ case cmdADDUID: if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL)) { update_trust = 1; redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdDELUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (real_uids_left (keyblock) < 1) tty_printf (_("You can't delete the last user ID!\n")); else if (cpr_get_answer_is_yes ("keyedit.remove.uid.okay", n1 > 1 ? _("Really remove all selected user IDs? (y/N) ") : _("Really remove this user ID? (y/N) "))) { menu_deluid (keyblock); redisplay = 1; modified = 1; } } break; case cmdDELSIG: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (menu_delsig (ctrl, keyblock)) { /* No redisplay here, because it may scroll away some * of the status output of this command. */ modified = 1; } } break; case cmdADDKEY: if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; #ifdef ENABLE_CARD_SUPPORT case cmdADDCARDKEY: if (!card_generate_subkey (ctrl, keyblock)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdKEYTOCARD: { KBNODE node = NULL; switch (count_selected_keys (keyblock)) { case 0: if (cpr_get_answer_is_yes ("keyedit.keytocard.use_primary", /* TRANSLATORS: Please take care: This is about moving the key and not about removing it. */ _("Really move the primary key? (y/N) "))) node = keyblock; break; case 1: for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && node->flag & NODFLG_SELKEY) break; } break; default: tty_printf (_("You must select exactly one key.\n")); break; } if (node) { PKT_public_key *xxpk = node->pkt->pkt.public_key; if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0)) { redisplay = 1; sec_shadowing = 1; } } } break; case cmdBKUPTOCARD: { /* Ask for a filename, check whether this is really a backup key as generated by the card generation, parse that key and store it on card. */ KBNODE node; char *fname; PACKET *pkt; IOBUF a; struct parse_packet_ctx_s parsectx; if (!*arg_string) { tty_printf (_("Command expects a filename argument\n")); break; } if (*arg_string == DIRSEP_C) fname = xstrdup (arg_string); else if (*arg_string == '~') fname = make_filename (arg_string, NULL); else fname = make_filename (gnupg_homedir (), arg_string, NULL); /* Open that file. */ a = iobuf_open (fname); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if (!a) { tty_printf (_("Can't open '%s': %s\n"), fname, strerror (errno)); xfree (fname); break; } /* Parse and check that file. */ pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); err = parse_packet (&parsectx, pkt); deinit_parse_packet (&parsectx); iobuf_close (a); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname); if (!err && pkt->pkttype != PKT_SECRET_KEY && pkt->pkttype != PKT_SECRET_SUBKEY) err = GPG_ERR_NO_SECKEY; if (err) { tty_printf (_("Error reading backup key from '%s': %s\n"), fname, gpg_strerror (err)); xfree (fname); free_packet (pkt, NULL); xfree (pkt); break; } xfree (fname); node = new_kbnode (pkt); /* Transfer it to gpg-agent which handles secret keys. */ err = transfer_secret_keys (ctrl, NULL, node, 1, 1); /* Treat the pkt as a public key. */ pkt->pkttype = PKT_PUBLIC_KEY; /* Ask gpg-agent to store the secret key to card. */ if (card_store_subkey (node, 0)) { redisplay = 1; sec_shadowing = 1; } release_kbnode (node); } break; #endif /* ENABLE_CARD_SUPPORT */ case cmdDELKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { tty_printf (_("You must select at least one key.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "key"); } else if (!cpr_get_answer_is_yes ("keyedit.remove.subkey.okay", n1 > 1 ? _("Do you really want to delete the " "selected keys? (y/N) ") : _("Do you really want to delete this key? (y/N) "))) ; else { menu_delkey (keyblock); redisplay = 1; modified = 1; } } break; case cmdADDREVOKER: { int sensitive = 0; if (ascii_strcasecmp (arg_string, "sensitive") == 0) sensitive = 1; if (menu_addrevoker (ctrl, keyblock, sensitive)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } } break; case cmdREVUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (cpr_get_answer_is_yes ("keyedit.revoke.uid.okay", n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ") : _("Really revoke this user ID? (y/N) "))) { if (menu_revuid (ctrl, keyblock)) { modified = 1; redisplay = 1; } } } break; case cmdREVKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", _("Do you really want to revoke" " the entire key? (y/N) "))) { if (menu_revkey (ctrl, keyblock)) modified = 1; redisplay = 1; } } else if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", n1 > 1 ? _("Do you really want to revoke" " the selected subkeys? (y/N) ") : _("Do you really want to revoke" " this subkey? (y/N) "))) { if (menu_revsubkey (ctrl, keyblock)) modified = 1; redisplay = 1; } if (modified) merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdEXPIRE: if (gpg_err_code (menu_expire (ctrl, keyblock, 0, 0)) == GPG_ERR_TRUE) { merge_keys_and_selfsig (ctrl, keyblock); run_subkey_warnings = 1; modified = 1; redisplay = 1; } break; case cmdCHANGEUSAGE: if (menu_changeusage (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdBACKSIGN: if (menu_backsign (ctrl, keyblock)) { modified = 1; redisplay = 1; } break; case cmdPRIMARY: if (menu_set_primary_uid (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdPASSWD: change_passphrase (ctrl, keyblock); break; #ifndef NO_TRUST_MODELS case cmdTRUST: if (opt.trust_model == TM_EXTERNAL) { tty_printf (_("Owner trust may not be set while " "using a user provided trust database\n")); break; } show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0); tty_printf ("\n"); if (edit_ownertrust (ctrl, find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt. public_key, 1)) { redisplay = 1; /* No real need to set update_trust here as edit_ownertrust() calls revalidation_mark() anyway. */ update_trust = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 1); } break; case cmdSHOWPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 2); } break; case cmdSETPREF: { PKT_user_id *tempuid; keygen_set_std_prefs (!*arg_string ? "default" : arg_string, 0); tempuid = keygen_get_std_prefs (); tty_printf (_("Set preference list to:\n")); show_prefs (tempuid, NULL, 1); free_user_id (tempuid); if (cpr_get_answer_is_yes ("keyedit.setpref.okay", count_selected_uids (keyblock) ? _("Really update the preferences" " for the selected user IDs? (y/N) ") : _("Really update the preferences? (y/N) "))) { if (menu_set_preferences (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } } } break; case cmdPREFKS: if (menu_set_keyserver_url (ctrl, *arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdNOTATION: if (menu_set_notation (ctrl, *arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdNOP: break; case cmdREVSIG: if (menu_revsig (ctrl, keyblock)) { redisplay = 1; modified = 1; } break; #ifndef NO_TRUST_MODELS case cmdENABLEKEY: case cmdDISABLEKEY: if (enable_disable_key (ctrl, keyblock, cmd == cmdDISABLEKEY)) { redisplay = 1; modified = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdSHOWPHOTO: menu_showphoto (ctrl, keyblock); break; case cmdCLEAN: if (menu_clean (ctrl, keyblock, 0)) redisplay = modified = 1; break; case cmdMINIMIZE: if (menu_clean (ctrl, keyblock, 1)) redisplay = modified = 1; break; case cmdQUIT: if (have_commands) goto leave; if (!modified && !sec_shadowing) goto leave; if (!cpr_get_answer_is_yes ("keyedit.save.okay", _("Save changes? (y/N) "))) { if (cpr_enabled () || cpr_get_answer_is_yes ("keyedit.cancel.okay", _("Quit without saving? (y/N) "))) goto leave; break; } /* fall through */ case cmdSAVE: if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (sec_shadowing) { err = agent_scd_learn (NULL, 1); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (!modified && !sec_shadowing) tty_printf (_("Key not changed so no update needed.\n")); if (update_trust) { revalidation_mark (ctrl); update_trust = 0; } goto leave; case cmdINVCMD: default: tty_printf ("\n"); tty_printf (_("Invalid command (try \"help\")\n")); break; } } /* End of the main command loop. */ leave: release_kbnode (keyblock); keydb_release (kdbhd); xfree (answer); } /* Change the passphrase of the secret key identified by USERNAME. */ void keyedit_passwd (ctrl_t ctrl, const char *username) { gpg_error_t err; PKT_public_key *pk; kbnode_t keyblock = NULL; pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); goto leave; } err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock); if (err) goto leave; err = change_passphrase (ctrl, keyblock); leave: release_kbnode (keyblock); free_public_key (pk); if (err) { log_info ("error changing the passphrase for '%s': %s\n", username, gpg_strerror (err)); write_status_error ("keyedit.passwd", err); } else write_status_text (STATUS_SUCCESS, "keyedit.passwd"); } /* Helper for quick commands to find the keyblock for USERNAME. * Returns on success the key database handle at R_KDBHD and the * keyblock at R_KEYBLOCK. */ static gpg_error_t quick_find_keyblock (ctrl_t ctrl, const char *username, KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; KEYDB_SEARCH_DESC desc; kbnode_t node; *r_kdbhd = NULL; *r_keyblock = NULL; /* Search the key; we don't want the whole getkey stuff here. */ kdbhd = keydb_new (); if (!kdbhd) { /* Note that keydb_new has already used log_error. */ err = gpg_error_from_syserror (); goto leave; } err = classify_user_id (username, &desc, 1); if (!err) err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) { err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } /* Now with the keyblock retrieved, search again to detect an ambiguous specification. We need to save the found state so that we can do an update later. */ keydb_push_found_state (kdbhd); err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; keydb_pop_found_state (kdbhd); if (!err) { /* We require the secret primary key to set the primary UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); log_assert (node); err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_NO_PUBKEY); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } fix_keyblock (ctrl, &keyblock); merge_keys_and_selfsig (ctrl, keyblock); *r_keyblock = keyblock; keyblock = NULL; *r_kdbhd = kdbhd; kdbhd = NULL; leave: release_kbnode (keyblock); keydb_release (kdbhd); return err; } /* Unattended adding of a new keyid. USERNAME specifies the key. NEWUID is the new user id to add to the key. */ void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; char *uidstring = NULL; uidstring = xstrdup (newuid); trim_spaces (uidstring); if (!*uidstring) { log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring)) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (ctrl); } leave: xfree (uidstring); release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended revocation of a keyid. USERNAME specifies the key. UIDTOREV is the user id revoke from the key. */ void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; kbnode_t node; int modified = 0; size_t revlen; size_t valid_uids; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; /* Too make sure that we do not revoke the last valid UID, we first count how many valid UIDs there are. */ valid_uids = 0; for (node = keyblock; node; node = node->next) valid_uids += (node->pkt->pkttype == PKT_USER_ID && !node->pkt->pkt.user_id->flags.revoked && !node->pkt->pkt.user_id->flags.expired); /* Find the right UID. */ revlen = strlen (uidtorev); for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && revlen == node->pkt->pkt.user_id->len && !memcmp (node->pkt->pkt.user_id->name, uidtorev, revlen)) { struct revocation_reason_info *reason; /* Make sure that we do not revoke the last valid UID. */ if (valid_uids == 1 && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired) { log_error (_("cannot revoke the last valid user ID.\n")); err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } reason = get_default_uid_revocation_reason (); err = core_revuid (ctrl, keyblock, node, reason, &modified); release_revocation_reason_info (reason); if (err) goto leave; err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } revalidation_mark (ctrl); goto leave; } } err = gpg_error (GPG_ERR_NO_USER_ID); leave: if (err) log_error (_("revoking the user ID failed: %s\n"), gpg_strerror (err)); release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended setting of the primary uid. USERNAME specifies the key. PRIMARYUID is the user id which shall be primary. */ void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; kbnode_t node; size_t primaryuidlen; int any; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; /* Find and mark the UID - we mark only the first valid one. */ primaryuidlen = strlen (primaryuid); any = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && !any && !node->pkt->pkt.user_id->flags.revoked && !node->pkt->pkt.user_id->flags.expired && primaryuidlen == node->pkt->pkt.user_id->len && !memcmp (node->pkt->pkt.user_id->name, primaryuid, primaryuidlen)) { node->flag |= NODFLG_SELUID; any = 1; } else node->flag &= ~NODFLG_SELUID; } if (!any) err = gpg_error (GPG_ERR_NO_USER_ID); else if (menu_set_primary_uid (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } revalidation_mark (ctrl); } else err = gpg_error (GPG_ERR_GENERAL); if (err) log_error (_("setting the primary user ID failed: %s\n"), gpg_strerror (err)); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Find a keyblock by fingerprint because only this uniquely * identifies a key and may thus be used to select a key for * unattended subkey creation os key signing. */ static gpg_error_t find_by_primary_fpr (ctrl_t ctrl, const char *fpr, kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; KEYDB_SEARCH_DESC desc; byte fprbin[MAX_FINGERPRINT_LEN]; size_t fprlen; *r_keyblock = NULL; *r_kdbhd = NULL; if (classify_user_id (fpr, &desc, 1) || !(desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_FPR16 || desc.mode == KEYDB_SEARCH_MODE_FPR20)) { log_error (_("\"%s\" is not a fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); goto leave; } /* Check that the primary fingerprint has been given. */ fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen); if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16 && !memcmp (fprbin, desc.u.fpr, 16)) ; else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR && !memcmp (fprbin, desc.u.fpr, 16) && !desc.u.fpr[16] && !desc.u.fpr[17] && !desc.u.fpr[18] && !desc.u.fpr[19]) ; else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20 || desc.mode == KEYDB_SEARCH_MODE_FPR) && !memcmp (fprbin, desc.u.fpr, 20)) ; else { log_error (_("\"%s\" is not the primary fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } *r_keyblock = keyblock; keyblock = NULL; *r_kdbhd = kdbhd; kdbhd = NULL; err = 0; leave: release_kbnode (keyblock); keydb_release (kdbhd); return err; } /* Unattended key signing function. If the key specifified by FPR is available and FPR is the primary fingerprint all user ids of the key are signed using the default signing key. If UIDS is an empty list all usable UIDs are signed, if it is not empty, only those user ids matching one of the entries of the list are signed. With LOCAL being true the signatures are marked as non-exportable. */ void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int modified = 0; PKT_public_key *pk; kbnode_t node; strlist_t sl; int any; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a key and may thus be used to select a key for unattended key signing. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; /* Give some info in verbose. */ if (opt.verbose) { show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 1/*with_revoker*/, 1/*with_fingerprint*/, 0, 0, 1); es_fflush (es_stdout); } pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), _(" Unable to sign.\n")); goto leave; } /* Set the flags according to the UIDS list. Fixme: We may want to use classify_user_id along with dedicated compare functions so that we match the same way as in the key lookup. */ any = 0; menu_select_uid (keyblock, 0); /* Better clear the flags first. */ for (sl=uids; sl; sl = sl->next) { const char *name = sl->d; int count = 0; sl->flags &= ~(1|2); /* Clear flags used for error reporting. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->attrib_data) ; else if (*name == '=' && strlen (name+1) == uid->len && !memcmp (uid->name, name + 1, uid->len)) { /* Exact match - we don't do a check for ambiguity * in this case. */ node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } } else if (ascii_memistr (uid->name, uid->len, *name == '*'? name+1:name)) { node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } count++; } } } if (count > 1) { any = -1; /* Force failure at end. */ sl->flags |= 2; /* Report as ambiguous. */ } } /* Check whether all given user ids were found. */ for (sl=uids; sl; sl = sl->next) if (!(sl->flags & 1)) any = -1; /* That user id was not found. */ /* Print an error if there was a problem with the user ids. */ if (uids && any < 1) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); es_fflush (es_stdout); for (sl=uids; sl; sl = sl->next) { if ((sl->flags & 2)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_AMBIGUOUS_NAME)); else if (!(sl->flags & 1)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_NOT_FOUND)); } log_error ("%s %s", _("No matching user IDs."), _("Nothing to sign.\n")); goto leave; } /* Sign. */ sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1); es_fflush (es_stdout); if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); if (update_trust) revalidation_mark (ctrl); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended subkey creation function. * */ void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err; kbnode_t keyblock; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a * key and may thus be used to select a key for unattended subkey * creation. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), "\n"); goto leave; } /* Create the subkey. Note that the called function already prints * an error message. */ if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr)) modified = 1; es_fflush (es_stdout); /* Store. */ if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended expiration setting function for the main key. If * SUBKEYFPRS is not NULL and SUBKEYSFPRS[0] is neither NULL, it is * expected to be an array of fingerprints for subkeys to change. It * may also be an array which just one item "*" to indicate that all * keys shall be set to that expiration date. */ void keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr, char **subkeyfprs) { gpg_error_t err; kbnode_t keyblock, node; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; u32 expire; int primary_only = 0; int idx; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a * key and may thus be used to select a key for unattended * expiration setting. */ err = find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd); if (err) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), "\n"); err = gpg_error (GPG_ERR_CERT_REVOKED); goto leave; } expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { log_error (_("'%s' is not a valid expiration time\n"), expirestr); err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } if (expire) expire += make_timestamp (); /* Check whether a subkey's expiration time shall be changed or the * expiration time of all keys. */ if (!subkeyfprs || !subkeyfprs[0]) primary_only = 1; else if ( !strcmp (subkeyfprs[0], "*") && !subkeyfprs[1]) { /* Change all subkeys keys which have not been revoked and are * not yet expired. */ merge_keys_and_selfsig (ctrl, keyblock); for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (pk = node->pkt->pkt.public_key) && !pk->flags.revoked && !pk->has_expired) node->flag |= NODFLG_SELKEY; } } else { /* Change specified subkeys. */ KEYDB_SEARCH_DESC desc; byte fprbin[MAX_FINGERPRINT_LEN]; size_t fprlen; err = 0; merge_keys_and_selfsig (ctrl, keyblock); for (idx=0; subkeyfprs[idx]; idx++) { int any = 0; /* Parse the fingerprint. */ if (classify_user_id (subkeyfprs[idx], &desc, 1) || !(desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_FPR20)) { log_error (_("\"%s\" is not a proper fingerprint\n"), subkeyfprs[idx] ); if (!err) err = gpg_error (GPG_ERR_INV_NAME); continue; } /* Set the flag for the matching non revoked subkey. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (pk = node->pkt->pkt.public_key) && !pk->flags.revoked ) { fingerprint_from_pk (pk, fprbin, &fprlen); if (fprlen == 20 && !memcmp (fprbin, desc.u.fpr, 20)) { node->flag |= NODFLG_SELKEY; any = 1; } } } if (!any) { log_error (_("subkey \"%s\" not found\n"), subkeyfprs[idx]); if (!err) err = gpg_error (GPG_ERR_NOT_FOUND); } } if (err) goto leave; } /* Set the new expiration date. */ err = menu_expire (ctrl, keyblock, primary_only? 1 : 2, expire); if (gpg_err_code (err) == GPG_ERR_TRUE) modified = 1; else if (err) goto leave; es_fflush (es_stdout); /* Store. */ if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (ctrl); } else log_info (_("Key not changed so no update needed.\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); if (err) write_status_error ("set_expire", err); } static void tty_print_notations (int indent, PKT_signature * sig) { int first = 1; struct notation *notation, *nd; if (indent < 0) { first = 0; indent = -indent; } notation = sig_to_notation (sig); for (nd = notation; nd; nd = nd->next) { if (!first) tty_printf ("%*s", indent, ""); else first = 0; tty_print_utf8_string (nd->name, strlen (nd->name)); tty_printf ("="); tty_print_utf8_string (nd->value, strlen (nd->value)); tty_printf ("\n"); } free_notation (notation); } /* * Show preferences of a public keyblock. */ static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) { const prefitem_t fake = { 0, 0 }; const prefitem_t *prefs; int i; if (!uid) return; if (uid->prefs) prefs = uid->prefs; else if (verbose) prefs = &fake; else return; if (verbose) { int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; tty_printf (" "); tty_printf (_("Cipher: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_SYM) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!openpgp_cipher_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", openpgp_cipher_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == CIPHER_ALGO_3DES) des_seen = 1; } } if (!des_seen) { if (any) tty_printf (", "); tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); } tty_printf ("\n "); tty_printf (_("Digest: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_HASH) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", gcry_md_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == DIGEST_ALGO_SHA1) sha1_seen = 1; } } if (!sha1_seen) { if (any) tty_printf (", "); tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); } tty_printf ("\n "); tty_printf (_("Compression: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_ZIP) { const char *s = compress_algo_to_string (prefs[i].value); if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (s && prefs[i].value < 100) tty_printf ("%s", s); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == COMPRESS_ALGO_NONE) uncomp_seen = 1; } } if (!uncomp_seen) { if (any) tty_printf (", "); else { tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_ZIP)); tty_printf (", "); } tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); } if (uid->flags.mdc || !uid->flags.ks_modify) { tty_printf ("\n "); tty_printf (_("Features: ")); any = 0; if (uid->flags.mdc) { tty_printf ("MDC"); any = 1; } if (!uid->flags.ks_modify) { if (any) tty_printf (", "); tty_printf (_("Keyserver no-modify")); } } tty_printf ("\n"); if (selfsig) { const byte *pref_ks; size_t pref_ks_len; pref_ks = parse_sig_subpkt (selfsig->hashed, SIGSUBPKT_PREF_KS, &pref_ks_len); if (pref_ks && pref_ks_len) { tty_printf (" "); tty_printf (_("Preferred keyserver: ")); tty_print_utf8_string (pref_ks, pref_ks_len); tty_printf ("\n"); } if (selfsig->flags.notation) { tty_printf (" "); tty_printf (_("Notations: ")); tty_print_notations (5 + strlen (_("Notations: ")), selfsig); } } } else { tty_printf (" "); for (i = 0; prefs[i].type; i++) { tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : prefs[i].type == PREFTYPE_HASH ? 'H' : prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[i].value); } if (uid->flags.mdc) tty_printf (" [mdc]"); if (!uid->flags.ks_modify) tty_printf (" [no-ks-modify]"); tty_printf ("\n"); } } /* This is the version of show_key_with_all_names used when opt.with_colons is used. It prints all available data in a easy to parse format and does not translate utf8 */ static void show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) { KBNODE node; int i, j, ulti_hack = 0; byte pk_version = 0; PKT_public_key *primary = NULL; int have_seckey; if (!fp) fp = es_stdout; /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) { PKT_public_key *pk = node->pkt->pkt.public_key; u32 keyid[2]; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk_version = pk->version; primary = pk; } keyid_from_pk (pk, keyid); have_seckey = !agent_probe_secret_key (ctrl, pk); if (node->pkt->pkttype == PKT_PUBLIC_KEY) es_fputs (have_seckey? "sec:" : "pub:", fp); else es_fputs (have_seckey? "ssb:" : "sub:", fp); if (!pk->flags.valid) es_putc ('i', fp); else if (pk->flags.revoked) es_putc ('r', fp); else if (pk->has_expired) es_putc ('e', fp); else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks)) { int trust = get_validity_info (ctrl, keyblock, pk, NULL); if (trust == 'u') ulti_hack = 1; es_putc (trust, fp); } es_fprintf (fp, ":%u:%d:%08lX%08lX:%lu:%lu::", nbits_from_pk (pk), pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], (ulong) pk->timestamp, (ulong) pk->expiredate); if (node->pkt->pkttype == PKT_PUBLIC_KEY && !(opt.fast_list_mode || opt.no_expensive_trust_checks)) es_putc (get_ownertrust_info (ctrl, pk, 0), fp); es_putc (':', fp); es_putc (':', fp); es_putc (':', fp); /* Print capabilities. */ if ((pk->pubkey_usage & PUBKEY_USAGE_ENC)) es_putc ('e', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_SIG)) es_putc ('s', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_CERT)) es_putc ('c', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) es_putc ('a', fp); es_putc ('\n', fp); print_fingerprint (ctrl, fp, pk, 0); print_revokers (fp, pk); } } /* the user ids */ i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (uid->attrib_data) es_fputs ("uat:", fp); else es_fputs ("uid:", fp); if (uid->flags.revoked) es_fputs ("r::::::::", fp); else if (uid->flags.expired) es_fputs ("e::::::::", fp); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) es_fputs ("::::::::", fp); else { int uid_validity; if (primary && !ulti_hack) uid_validity = get_validity_info (ctrl, keyblock, primary, uid); else uid_validity = 'u'; es_fprintf (fp, "%c::::::::", uid_validity); } if (uid->attrib_data) es_fprintf (fp, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (fp, uid->name, uid->len, ":", NULL); es_putc (':', fp); /* signature class */ es_putc (':', fp); /* capabilities */ es_putc (':', fp); /* preferences */ if (pk_version > 3 || uid->selfsigversion > 3) { const prefitem_t *prefs = uid->prefs; for (j = 0; prefs && prefs[j].type; j++) { if (j) es_putc (' ', fp); es_fprintf (fp, "%c%d", prefs[j].type == PREFTYPE_SYM ? 'S' : prefs[j].type == PREFTYPE_HASH ? 'H' : prefs[j].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[j].value); } if (uid->flags.mdc) es_fputs (",mdc", fp); if (!uid->flags.ks_modify) es_fputs (",no-ks-modify", fp); } es_putc (':', fp); /* flags */ es_fprintf (fp, "%d,", i); if (uid->flags.primary) es_putc ('p', fp); if (uid->flags.revoked) es_putc ('r', fp); if (uid->flags.expired) es_putc ('e', fp); if ((node->flag & NODFLG_SELUID)) es_putc ('s', fp); if ((node->flag & NODFLG_MARK_A)) es_putc ('m', fp); es_putc (':', fp); if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { #ifdef USE_TOFU enum tofu_policy policy; if (! tofu_get_policy (ctrl, primary, uid, &policy) && policy != TOFU_POLICY_NONE) es_fprintf (fp, "%s", tofu_policy_str (policy)); #endif /*USE_TOFU*/ } es_putc (':', fp); es_putc ('\n', fp); } } } static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node)) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (!flag || (flag && (node->flag & flag))) { if (!(flag & NODFLG_MARK_A) && pk) tty_fprintf (fp, "%s ", uid_trust_string_fixed (ctrl, pk, uid)); if (flag & NODFLG_MARK_A) tty_fprintf (fp, " "); else if (node->flag & NODFLG_SELUID) tty_fprintf (fp, "(%d)* ", i); else if (uid->flags.primary) tty_fprintf (fp, "(%d). ", i); else tty_fprintf (fp, "(%d) ", i); tty_print_utf8_string2 (fp, uid->name, uid->len, 0); tty_fprintf (fp, "\n"); if (with_prefs && pk) { if (pk->version > 3 || uid->selfsigversion > 3) { PKT_signature *selfsig = NULL; KBNODE signode; for (signode = node->next; signode && signode->pkt->pkttype == PKT_SIGNATURE; signode = signode->next) { if (signode->pkt->pkt.signature-> flags.chosen_selfsig) { selfsig = signode->pkt->pkt.signature; break; } } show_prefs (uid, selfsig, with_prefs == 2); } else tty_fprintf (fp, _("There are no preferences on a" " PGP 2.x-style user ID.\n")); } } } } } /* * Display the key a the user ids, if only_marked is true, do only so * for user ids with mark A flag set and do not display the index * number. If FP is not NULL print to the given stream and not to the * tty (ignored in with-colons mode). */ static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn) { gpg_error_t err; kbnode_t node; int i; int do_warn = 0; int have_seckey = 0; char *serialno = NULL; PKT_public_key *primary = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; if (opt.with_colons) { show_key_with_all_names_colon (ctrl, fp, keyblock); return; } /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY && !is_deleted_kbnode (node))) { PKT_public_key *pk = node->pkt->pkt.public_key; const char *otrust = "err"; const char *trust = "err"; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { /* do it here, so that debug messages don't clutter the * output */ static int did_warn = 0; trust = get_validity_string (ctrl, pk, NULL); otrust = get_ownertrust_string (ctrl, pk, 0); /* Show a warning once */ if (!did_warn && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0) & TRUST_FLAG_PENDING_CHECK)) { did_warn = 1; do_warn = 1; } primary = pk; } if (pk->flags.revoked) { char *user = get_user_id_string_native (ctrl, pk->revoked.keyid); tty_fprintf (fp, _("The following key was revoked on" " %s by %s key %s\n"), revokestr_from_pk (pk), gcry_pk_algo_name (pk->revoked.algo), user); xfree (user); } if (with_revoker) { if (!pk->revkey && pk->numrevkeys) BUG (); else for (i = 0; i < pk->numrevkeys; i++) { u32 r_keyid[2]; char *user; const char *algo; algo = gcry_pk_algo_name (pk->revkey[i].algid); keyid_from_fingerprint (ctrl, pk->revkey[i].fpr, MAX_FINGERPRINT_LEN, r_keyid); user = get_user_id_string_native (ctrl, r_keyid); tty_fprintf (fp, _("This key may be revoked by %s key %s"), algo ? algo : "?", user); if (pk->revkey[i].class & 0x40) { tty_fprintf (fp, " "); tty_fprintf (fp, _("(sensitive)")); } tty_fprintf (fp, "\n"); xfree (user); } } keyid_from_pk (pk, NULL); xfree (serialno); serialno = NULL; { char *hexgrip; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("error computing a keygrip: %s\n", gpg_strerror (err)); have_seckey = 0; } else have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); xfree (hexgrip); } tty_fprintf (fp, "%s%c %s/%s", node->pkt->pkttype == PKT_PUBLIC_KEY && have_seckey? "sec" : node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" : have_seckey ? "ssb" : "sub", (node->flag & NODFLG_SELKEY) ? '*' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (pk->keyid)); if (opt.legacy_list_mode) tty_fprintf (fp, " "); else tty_fprintf (fp, "\n "); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); if (pk->flags.revoked) tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); else if (pk->has_expired) tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); else tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("usage: %s"), usagestr_from_pk (pk, 1)); tty_fprintf (fp, "\n"); if (serialno) { /* The agent told us that a secret key is available and that it has been stored on a card. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s\n", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s\n", serialno); } else if (pk->seckey_info && pk->seckey_info->is_protected && pk->seckey_info->s2k.mode == 1002) { /* FIXME: Check whether this code path is still used. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (pk->seckey_info->ivlen == 16 && !memcmp (pk->seckey_info->iv, "\xD2\x76\x00\x01\x24\x01", 6)) { /* This is an OpenPGP card. */ for (i = 8; i < 14; i++) { if (i == 10) tty_fprintf (fp, " "); tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } } else { /* Unknown card: Print all. */ for (i = 0; i < pk->seckey_info->ivlen; i++) tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { if (opt.trust_model != TM_ALWAYS) { tty_fprintf (fp, "%*s", opt.legacy_list_mode? ((int) keystrlen () + 13):5, ""); /* Ownertrust is only meaningful for the PGP or classic trust models, or PGP combined with TOFU */ if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { int width = 14 - strlen (otrust); if (width <= 0) width = 1; tty_fprintf (fp, _("trust: %s"), otrust); tty_fprintf (fp, "%*s", width, ""); } tty_fprintf (fp, _("validity: %s"), trust); tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY && (get_ownertrust (ctrl, pk) & TRUST_FLAG_DISABLED)) { tty_fprintf (fp, "*** "); tty_fprintf (fp, _("This key has been disabled")); tty_fprintf (fp, "\n"); } } if ((node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) && with_fpr) { print_fingerprint (ctrl, fp, pk, 2); tty_fprintf (fp, "\n"); } } } show_names (ctrl, fp, keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs); if (do_warn && !nowarn) tty_fprintf (fp, _("Please note that the shown key validity" " is not necessarily correct\n" "unless you restart the program.\n")); xfree (serialno); } /* Display basic key information. This function is suitable to show information on the key without any dependencies on the trustdb or any other internal GnuPG stuff. KEYBLOCK may either be a public or a secret key. This function may be called with KEYBLOCK containing secret keys and thus the printing of "pub" vs. "sec" does only depend on the packet type and not by checking with gpg-agent. */ void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock) { KBNODE node; int i; char pkstrbuf[PUBKEY_STRING_SIZE]; /* The primary key */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { PKT_public_key *pk = node->pkt->pkt.public_key; /* Note, we use the same format string as in other show functions to make the translation job easier. */ tty_printf ("%s %s/%s ", node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" : node->pkt->pkttype == PKT_PUBLIC_SUBKEY ? "sub" : node->pkt->pkttype == PKT_SECRET_KEY ? "sec" :"ssb", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk)); tty_printf (_("created: %s"), datestr_from_pk (pk)); tty_printf (" "); tty_printf (_("expires: %s"), expirestr_from_pk (pk)); tty_printf ("\n"); print_fingerprint (ctrl, NULL, pk, 3); tty_printf ("\n"); } } /* The user IDs. */ for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; tty_printf (" "); if (uid->flags.revoked) tty_printf ("[%s] ", _("revoked")); else if (uid->flags.expired) tty_printf ("[%s] ", _("expired")); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } } } static void show_key_and_fingerprint (ctrl_t ctrl, kbnode_t keyblock, int with_subkeys) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk = node->pkt->pkt.public_key; tty_printf ("pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk)); } else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_print_utf8_string (uid->name, uid->len); break; } } tty_printf ("\n"); if (pk) print_fingerprint (ctrl, NULL, pk, 2); if (with_subkeys) { for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("sub %s/%s %s [%s]\n", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); print_fingerprint (ctrl, NULL, pk, 4); } } } } /* Show a listing of the primary and its subkeys along with their keygrips. */ static void show_key_and_grip (kbnode_t keyblock) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; char *hexgrip; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("%s %s/%s %s [%s]\n", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); if (!hexkeygrip_from_pk (pk, &hexgrip)) { tty_printf (" Keygrip: %s\n", hexgrip); xfree (hexgrip); } } } } /* Show a warning if no uids on the key have the primary uid flag set. */ static void no_primary_warning (KBNODE keyblock) { KBNODE node; int have_primary = 0, uid_count = 0; /* TODO: if we ever start behaving differently with a primary or non-primary attribute ID, we will need to check for attributes here as well. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data == NULL) { uid_count++; if (node->pkt->pkt.user_id->flags.primary == 2) { have_primary = 1; break; } } } if (uid_count > 1 && !have_primary) log_info (_ ("WARNING: no user ID has been marked as primary. This command" " may\n cause a different user ID to become" " the assumed primary.\n")); } /* Print a warning if the latest encryption subkey expires soon. This function is called after the expire data of the primary key has been changed. */ static void subkey_expire_warning (kbnode_t keyblock) { u32 curtime = make_timestamp (); kbnode_t node; PKT_public_key *pk; /* u32 mainexpire = 0; */ u32 subexpire = 0; u32 latest_date = 0; for (node = keyblock; node; node = node->next) { /* if (node->pkt->pkttype == PKT_PUBLIC_KEY) */ /* { */ /* pk = node->pkt->pkt.public_key; */ /* mainexpire = pk->expiredate; */ /* } */ if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (!pk->flags.valid) continue; if (pk->flags.revoked) continue; if (pk->timestamp > curtime) continue; /* Ignore future keys. */ if (!(pk->pubkey_usage & PUBKEY_USAGE_ENC)) continue; /* Not an encryption key. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; subexpire = pk->expiredate; } } if (!subexpire) return; /* No valid subkey with an expiration time. */ if (curtime + (10*86400) > subexpire) { log_info (_("WARNING: Your encryption subkey expires soon.\n")); log_info (_("You may want to change its expiration date too.\n")); } } /* * Ask for a new user id, add the self-signature, and update the * keyblock. If UIDSTRING is not NULL the user ID is generated * unattended using that string. UIDSTRING is expected to be utf-8 * encoded and white space trimmed. Returns true if there is a new * user id. */ static int menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock, int photo, const char *photo_name, const char *uidstring) { PKT_user_id *uid; PKT_public_key *pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; KBNODE node; KBNODE pub_where = NULL; gpg_error_t err; if (photo && uidstring) return 0; /* Not allowed. */ for (node = pub_keyblock; node; pub_where = node, node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; } if (!node) /* No subkey. */ pub_where = NULL; log_assert (pk); if (photo) { int hasattrib = 0; for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data != NULL) { hasattrib = 1; break; } /* It is legal but bad for compatibility to add a photo ID to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a photo on a v3 key. Don't bother to ask this if the key already has a photo - any damage has already been done at that point. -dms */ if (pk->version == 3 && !hasattrib) { if (opt.expert) { tty_printf (_("WARNING: This is a PGP2-style key. " "Adding a photo ID may cause some versions\n" " of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_photo.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a photo ID to " "a PGP2-style key.\n")); return 0; } } uid = generate_photo_id (ctrl, pk, photo_name); } else uid = generate_user_id (pub_keyblock, uidstring); if (!uid) { if (uidstring) { write_status_error ("adduid", gpg_error (304)); log_error ("%s", _("Such a user ID already exists on this key!\n")); } return 0; } err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x13, 0, 0, 0, keygen_add_std_prefs, pk, NULL); if (err) { write_status_error ("keysig", err); log_error ("signing failed: %s\n", gpg_strerror (err)); free_user_id (uid); return 0; } /* Insert/append to public keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode (pkt); if (pub_where) insert_kbnode (pub_where, node, 0); else add_kbnode (pub_keyblock, node); pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; if (pub_where) insert_kbnode (node, new_kbnode (pkt), 0); else add_kbnode (pub_keyblock, new_kbnode (pkt)); return 1; } /* * Remove all selected userids from the keyring */ static void menu_deluid (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { selected = node->flag & NODFLG_SELUID; if (selected) { /* Only cause a trust update if we delete a non-revoked user id */ if (!node->pkt->pkt.user_id->flags.revoked) update_trust = 1; delete_kbnode (node); } } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) selected = 0; } commit_kbnode (&pub_keyblock); } static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock) { KBNODE node; PKT_user_id *uid = NULL; int changed = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { uid = (node->flag & NODFLG_SELUID) ? node->pkt->pkt.user_id : NULL; } else if (uid && node->pkt->pkttype == PKT_SIGNATURE) { int okay, valid, selfsig, inv_sig, no_key, other_err; tty_printf ("uid "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); okay = inv_sig = no_key = other_err = 0; if (opt.with_colons) valid = print_and_check_one_sig_colon (ctrl, pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1); else valid = print_and_check_one_sig (ctrl, pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1, 0); if (valid) { okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.valid", _("Delete this good signature? (y/N/q)")); /* Only update trust if we delete a good signature. The other two cases do not affect trust. */ if (okay) update_trust = 1; } else if (inv_sig || other_err) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.invalid", _("Delete this invalid signature? (y/N/q)")); else if (no_key) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.unknown", _("Delete this unknown signature? (y/N/q)")); if (okay == -1) break; if (okay && selfsig && !cpr_get_answer_is_yes ("keyedit.delsig.selfsig", _("Really delete this self-signature? (y/N)"))) okay = 0; if (okay) { delete_kbnode (node); changed++; } } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) uid = NULL; } if (changed) { commit_kbnode (&pub_keyblock); tty_printf (ngettext("Deleted %d signature.\n", "Deleted %d signatures.\n", changed), changed); } else tty_printf (_("Nothing deleted.\n")); return changed; } static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only) { KBNODE uidnode; int modified = 0, select_all = !count_selected_uids (keyblock); for (uidnode = keyblock->next; uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY; uidnode = uidnode->next) { if (uidnode->pkt->pkttype == PKT_USER_ID && (uidnode->flag & NODFLG_SELUID || select_all)) { int uids = 0, sigs = 0; char *user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); clean_one_uid (ctrl, keyblock, uidnode, opt.verbose, self_only, &uids, &sigs); if (uids) { const char *reason; if (uidnode->pkt->pkt.user_id->flags.revoked) reason = _("revoked"); else if (uidnode->pkt->pkt.user_id->flags.expired) reason = _("expired"); else reason = _("invalid"); tty_printf (_("User ID \"%s\" compacted: %s\n"), user, reason); modified = 1; } else if (sigs) { tty_printf (ngettext("User ID \"%s\": %d signature removed\n", "User ID \"%s\": %d signatures removed\n", sigs), user, sigs); modified = 1; } else { tty_printf (self_only == 1 ? _("User ID \"%s\": already minimized\n") : _("User ID \"%s\": already clean\n"), user); } xfree (user); } } return modified; } /* * Remove some of the secondary keys */ static void menu_delkey (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { selected = node->flag & NODFLG_SELKEY; if (selected) delete_kbnode (node); } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else selected = 0; } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys are no longer used to certify other keys, so there is no change in trust when revoking/removing them. */ } /* * Ask for a new revoker, create the self-signature and put it into * the keyblock. Returns true if there is a new revoker. */ static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) { PKT_public_key *pk = NULL; PKT_public_key *revoker_pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; struct revocation_key revkey; size_t fprlen; int rc; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = pub_keyblock->pkt->pkt.public_key; if (pk->numrevkeys == 0 && pk->version == 3) { /* It is legal but bad for compatibility to add a revoker to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a revoker on a v3 key. Don't bother to ask this if the key already has a revoker - any damage has already been done at that point. -dms */ if (opt.expert) { tty_printf (_("WARNING: This is a PGP 2.x-style key. " "Adding a designated revoker may cause\n" " some versions of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_revoker.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a designated revoker to " "a PGP 2.x-style key.\n")); return 0; } } for (;;) { char *answer; free_public_key (revoker_pk); revoker_pk = xmalloc_clear (sizeof (*revoker_pk)); tty_printf ("\n"); answer = cpr_get_utf8 ("keyedit.add_revoker", _("Enter the user ID of the designated revoker: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); goto fail; } /* Note that I'm requesting CERT here, which usually implies primary keys only, but some casual testing shows that PGP and GnuPG both can handle a designated revocation from a subkey. */ revoker_pk->req_usage = PUBKEY_USAGE_CERT; rc = get_pubkey_byname (ctrl, NULL, revoker_pk, answer, NULL, NULL, 1, 1); if (rc) { log_error (_("key \"%s\" not found: %s\n"), answer, gpg_strerror (rc)); xfree (answer); continue; } xfree (answer); fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); if (fprlen != 20) { log_error (_("cannot appoint a PGP 2.x style key as a " "designated revoker\n")); continue; } revkey.class = 0x80; if (sensitive) revkey.class |= 0x40; revkey.algid = revoker_pk->pubkey_algo; if (cmp_public_keys (revoker_pk, pk) == 0) { /* This actually causes no harm (after all, a key that designates itself as a revoker is the same as a regular key), but it's easy enough to check. */ log_error (_("you cannot appoint a key as its own " "designated revoker\n")); continue; } keyid_from_pk (pk, NULL); /* Does this revkey already exist? */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i; for (i = 0; i < pk->numrevkeys; i++) { if (memcmp (&pk->revkey[i], &revkey, sizeof (struct revocation_key)) == 0) { char buf[50]; log_error (_("this key has already been designated " "as a revoker\n")); format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf)); write_status_text (STATUS_ALREADY_SIGNED, buf); break; } } if (i < pk->numrevkeys) continue; } print_pubkey_info (ctrl, NULL, revoker_pk); print_fingerprint (ctrl, NULL, revoker_pk, 2); tty_printf ("\n"); tty_printf (_("WARNING: appointing a key as a designated revoker " "cannot be undone!\n")); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.add_revoker.okay", _("Are you sure you want to appoint this " "key as a designated revoker? (y/N) "))) continue; free_public_key (revoker_pk); revoker_pk = NULL; break; } rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x1F, 0, 0, 0, keygen_add_revkey, &revkey, NULL); if (rc) { write_status_error ("keysig", rc); log_error ("signing failed: %s\n", gpg_strerror (rc)); goto fail; } /* Insert into public keyblock. */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), PKT_SIGNATURE); return 1; fail: if (sig) free_seckey_enc (sig); free_public_key (revoker_pk); return 0; } /* With FORCE_MAINKEY cleared this function handles the interactive * menu option "expire". With UNATTENDED set to 1 this function only * sets the expiration date of the primary key to NEWEXPIRATION and * avoid all interactivity; with a value of 2 only the flagged subkeys * are set to NEWEXPIRATION. Returns 0 if nothing was done, * GPG_ERR_TRUE if the key was modified, or any other error code. */ static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration) { int signumber, rc; u32 expiredate; int only_mainkey; /* Set if only the mainkey is to be updated. */ PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; if (unattended) { only_mainkey = (unattended == 1); expiredate = newexpiration; } else { int n1; only_mainkey = 0; n1 = count_selected_keys (pub_keyblock); if (n1 > 1) { if (!cpr_get_answer_is_yes ("keyedit.expire_multiple_subkeys.okay", _("Are you sure you want to change the" " expiration time for multiple subkeys? (y/N) "))) return gpg_error (GPG_ERR_CANCELED);; } else if (n1) tty_printf (_("Changing expiration time for a subkey.\n")); else { tty_printf (_("Changing expiration time for the primary key.\n")); only_mainkey = 1; no_primary_warning (pub_keyblock); } expiredate = ask_expiredate (); } /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; signumber = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); main_pk->expiredate = expiredate; } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if ((node->flag & NODFLG_SELKEY) && unattended != 1) { /* The flag is set and we do not want to set the * expiration date only for the main key. */ sub_pk = node->pkt->pkt.public_key; sub_pk->expiredate = expiredate; } else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (only_mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((only_mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!only_mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is a self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; signumber++; if ((only_mainkey && main_pk->version < 4) || (!only_mainkey && sub_pk->version < 4)) { log_info (_("You can't change the expiration date of a v3 key\n")); return gpg_error (GPG_ERR_LEGACY_KEY); } if (only_mainkey) rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_expire, main_pk); else rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_expire, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_TRUE) rc = GPG_ERR_GENERAL; return rc; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; } } } update_trust = 1; return gpg_error (GPG_ERR_TRUE); } /* Change the capability of a selected key. This command should only * be used to rectify badly created keys and as such is not suggested * for general use. */ static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock) { int n1, rc; int mainkey = 0; PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; n1 = count_selected_keys (keyblock); if (n1 > 1) { tty_printf (_("You must select exactly one key.\n")); return 0; } else if (n1) tty_printf ("Changing usage of a subkey.\n"); else { tty_printf ("Changing usage of the primary key.\n"); mainkey = 1; } /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->flag & NODFLG_SELKEY) sub_pk = node->pkt->pkt.public_key; else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is the self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; if ((mainkey && main_pk->version < 4) || (!mainkey && sub_pk->version < 4)) { log_info ("You can't change the capabilities of a v3 key\n"); return 0; } if (mainkey) main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0, main_pk->pubkey_usage); else sub_pk->pubkey_usage = ask_key_flags (sub_pk->pubkey_algo, 1, sub_pk->pubkey_usage); if (mainkey) rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_flags, main_pk); else rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_flags, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; break; } } } return 1; } static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock) { int rc, modified = 0; PKT_public_key *main_pk; KBNODE node; u32 timestamp; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); merge_keys_and_selfsig (ctrl, pub_keyblock); main_pk = pub_keyblock->pkt->pkt.public_key; keyid_from_pk (main_pk, NULL); /* We use the same timestamp for all backsigs so that we don't reveal information about the used machine. */ timestamp = make_timestamp (); for (node = pub_keyblock; node; node = node->next) { PKT_public_key *sub_pk = NULL; KBNODE node2, sig_pk = NULL /*,sig_sk = NULL*/; /* char *passphrase; */ /* Find a signing subkey with no backsig */ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->pkt->pkt.public_key->pubkey_usage & PUBKEY_USAGE_SIG) { if (node->pkt->pkt.public_key->flags.backsig) tty_printf (_ ("signing subkey %s is already cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); else sub_pk = node->pkt->pkt.public_key; } else tty_printf (_("subkey %s does not sign and so does" " not need to be cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); } if (!sub_pk) continue; /* Find the selected selfsig on this subkey */ for (node2 = node->next; node2 && node2->pkt->pkttype == PKT_SIGNATURE; node2 = node2->next) if (node2->pkt->pkt.signature->version >= 4 && node2->pkt->pkt.signature->flags.chosen_selfsig) { sig_pk = node2; break; } if (!sig_pk) continue; /* Find the secret subkey that matches the public subkey */ log_debug ("FIXME: Check whether a secret subkey is available.\n"); /* if (!sub_sk) */ /* { */ /* tty_printf (_("no secret subkey for public subkey %s - ignoring\n"), */ /* keystr_from_pk (sub_pk)); */ /* continue; */ /* } */ /* Now we can get to work. */ rc = make_backsig (ctrl, sig_pk->pkt->pkt.signature, main_pk, sub_pk, sub_pk, timestamp, NULL); if (!rc) { PKT_signature *newsig; PACKET *newpkt; rc = update_keysig_packet (ctrl, &newsig, sig_pk->pkt->pkt.signature, main_pk, NULL, sub_pk, main_pk, NULL, NULL); if (!rc) { /* Put the new sig into place on the pubkey */ newpkt = xmalloc_clear (sizeof (*newpkt)); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (sig_pk->pkt, NULL); xfree (sig_pk->pkt); sig_pk->pkt = newpkt; modified = 1; } else { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); break; } } else { log_error ("make_backsig failed: %s\n", gpg_strerror (rc)); break; } } return modified; } static int change_primary_uid_cb (PKT_signature * sig, void *opaque) { byte buf[1]; /* first clear all primary uid flags so that we are sure none are * lingering around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID); /* if opaque is set,we want to set the primary id */ if (opaque) { buf[0] = 1; build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, buf, 1); } return 0; } /* * Set the primary uid flag for the selected UID. We will also reset * all other primary uid flags. For this to work we have to update * all the signature timestamps. If we would do this with the current * time, we lose quite a lot of information, so we use a kludge to * do this: Just increment the timestamp by one second which is * sufficient to updated a signature during import. */ static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected; int attribute = 0; int modified = 0; if (count_selected_uids (pub_keyblock) != 1) { tty_printf (_("Please select exactly one user ID.\n")); return 0; } main_pk = NULL; uid = NULL; selected = 0; /* Is our selected uid an attribute packet? */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->flag & NODFLG_SELUID) attribute = (node->pkt->pkt.user_id->attrib_data != NULL); for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = node->flag & NODFLG_SELUID; } else if (main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && attribute == (uid->attrib_data != NULL) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced. We can just ignore v3 signatures because they are not able to carry the primary ID flag. We also ignore self-sigs on user IDs that are not of the same type that we are making primary. That is, if we are making a user ID primary, we alter user IDs. If we are making an attribute packet primary, we alter attribute packets. */ /* FIXME: We must make sure that we only have one self-signature per user ID here (not counting revocations) */ PKT_signature *newsig; PACKET *newpkt; const byte *p; int action; /* See whether this signature has the primary UID flag. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (!p) p = parse_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) /* yes */ action = selected ? 0 : -1; else /* no */ action = selected ? 1 : 0; if (action) { int rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, change_primary_uid_cb, action > 0 ? "x" : NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } } return modified; } /* * Set preferences to new values for the selected user IDs */ static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; no_primary_warning (pub_keyblock); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user-ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the preferences. */ PKT_signature *newsig; PACKET *newpkt; int rc; rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_upd_std_prefs, NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } return modified; } static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer, *uri; no_primary_warning (pub_keyblock); if (url) answer = xstrdup (url); else { answer = cpr_get_utf8 ("keyedit.add_keyserver", _("Enter your preferred keyserver URL: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (ascii_strcasecmp (answer, "none") == 0) uri = NULL; else { struct keyserver_spec *keyserver = NULL; /* Sanity check the format */ keyserver = parse_keyserver_uri (answer, 1); xfree (answer); if (!keyserver) { log_info (_("could not parse keyserver URL\n")); return 0; } uri = xstrdup (keyserver->uri); free_keyserver_spec (keyserver); } select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the subpacket. */ PKT_signature *newsig; PACKET *newpkt; int rc; const byte *p; size_t plen; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &plen); if (p && plen) { tty_printf ("Current preferred keyserver for user" " ID \"%s\": ", user); tty_print_utf8_string (p, plen); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.confirm_keyserver", uri ? _("Are you sure you want to replace it? (y/N) ") : _("Are you sure you want to delete it? (y/N) "))) continue; } else if (uri == NULL) { /* There is no current keyserver URL, so there is no point in trying to un-set it. */ continue; } rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_keyserver_url, uri); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); xfree (uri); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } xfree (user); } } } xfree (uri); return modified; } static int menu_set_notation (ctrl_t ctrl, const char *string, KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer; struct notation *notation; no_primary_warning (pub_keyblock); if (string) answer = xstrdup (string); else { answer = cpr_get_utf8 ("keyedit.add_notation", _("Enter the notation: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (!ascii_strcasecmp (answer, "none") || !ascii_strcasecmp (answer, "-")) notation = NULL; /* Delete them all. */ else { notation = string_to_notation (answer, 0); if (!notation) { xfree (answer); return 0; } } xfree (answer); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { PKT_signature *newsig; PACKET *newpkt; int rc, skip = 0, addonly = 1; if (sig->flags.notation) { tty_printf ("Current notations for user ID \"%s\":\n", user); tty_print_notations (-9, sig); } else { tty_printf ("No notations on user ID \"%s\"\n", user); if (notation == NULL) { /* There are no current notations, so there is no point in trying to un-set them. */ continue; } } if (notation) { struct notation *n; int deleting = 0; notation->next = sig_to_notation (sig); for (n = notation->next; n; n = n->next) if (strcmp (n->name, notation->name) == 0) { if (notation->value) { if (strcmp (n->value, notation->value) == 0) { if (notation->flags.ignore) { /* Value match with a delete flag. */ n->flags.ignore = 1; deleting = 1; } else { /* Adding the same notation twice, so don't add it at all. */ skip = 1; tty_printf ("Skipping notation:" " %s=%s\n", notation->name, notation->value); break; } } } else { /* No value, so it means delete. */ n->flags.ignore = 1; deleting = 1; } if (n->flags.ignore) { tty_printf ("Removing notation: %s=%s\n", n->name, n->value); addonly = 0; } } if (!notation->flags.ignore && !skip) tty_printf ("Adding notation: %s=%s\n", notation->name, notation->value); /* We tried to delete, but had no matches. */ if (notation->flags.ignore && !deleting) continue; } else { tty_printf ("Removing all notations\n"); addonly = 0; } if (skip || (!addonly && !cpr_get_answer_is_yes ("keyedit.confirm_notation", _("Proceed? (y/N) ")))) continue; rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_notations, notation); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); free_notation (notation); xfree (user); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; if (notation) { /* Snip off the notation list from the sig */ free_notation (notation->next); notation->next = NULL; } xfree (user); } } } } free_notation (notation); return modified; } /* * Select one user id or remove all selection if IDX is 0 or select * all if IDX is -1. Returns: True if the selection changed. */ static int menu_select_uid (KBNODE keyblock, int idx) { KBNODE node; int i; if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag |= NODFLG_SELUID; return 1; } else if (idx) /* Toggle. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) if (++i == idx) break; } if (!node) { tty_printf (_("No user ID with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { if (++i == idx) { if ((node->flag & NODFLG_SELUID)) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; } } } } else /* Unselect all */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag &= ~NODFLG_SELUID; } return 1; } /* Search in the keyblock for a uid that matches namehash */ static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash) { byte hash[NAMEHASH_LEN]; KBNODE node; int i; log_assert (strlen (namehash) == NAMEHASH_LEN * 2); for (i = 0; i < NAMEHASH_LEN; i++) hash[i] = hextobyte (&namehash[i * 2]); for (node = keyblock->next; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { namehash_from_uid (node->pkt->pkt.user_id); if (memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN) == 0) { if (node->flag & NODFLG_SELUID) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; break; } } } if (!node) { tty_printf (_("No user ID with hash %s\n"), namehash); return 0; } return 1; } /* * Select secondary keys * Returns: True if the selection changed. */ static int menu_select_key (KBNODE keyblock, int idx, char *p) { KBNODE node; int i, j; int is_hex_digits; is_hex_digits = p && strlen (p) >= 8; if (is_hex_digits) { /* Skip initial spaces. */ while (spacep (p)) p ++; /* If the id starts with 0x accept and ignore it. */ if (p[0] == '0' && p[1] == 'x') p += 2; for (i = 0, j = 0; p[i]; i ++) if (hexdigitp (&p[i])) { p[j] = toupper (p[i]); j ++; } else if (spacep (&p[i])) /* Skip spaces. */ { } else { is_hex_digits = 0; break; } if (is_hex_digits) /* In case we skipped some spaces, add a new NUL terminator. */ { p[j] = 0; /* If we skipped some spaces, make sure that we still have at least 8 characters. */ is_hex_digits = (/* Short keyid. */ strlen (p) == 8 /* Long keyid. */ || strlen (p) == 16 /* Fingerprints are (currently) 32 or 40 characters. */ || strlen (p) >= 32); } } if (is_hex_digits) { int found_one = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { int match = 0; if (strlen (p) == 8 || strlen (p) == 16) { u32 kid[2]; char kid_str[17]; keyid_from_pk (node->pkt->pkt.public_key, kid); format_keyid (kid, strlen (p) == 8 ? KF_SHORT : KF_LONG, kid_str, sizeof (kid_str)); if (strcmp (p, kid_str) == 0) match = 1; } else { char fp[2*MAX_FINGERPRINT_LEN + 1]; hexfingerprint (node->pkt->pkt.public_key, fp, sizeof (fp)); if (strcmp (fp, p) == 0) match = 1; } if (match) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; found_one = 1; } } if (found_one) return 1; tty_printf (_("No subkey with key ID '%s'.\n"), p); return 0; } if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag |= NODFLG_SELKEY; } else if (idx) /* Toggle selection. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) break; } if (!node) { tty_printf (_("No subkey with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; } } } else /* Unselect all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag &= ~NODFLG_SELKEY; } return 1; } static int count_uids_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & flag)) i++; return i; } static int count_keys_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && (node->flag & flag)) i++; return i; } static int count_uids (KBNODE keyblock) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) i++; return i; } /* * Returns true if there is at least one selected user id */ static int count_selected_uids (KBNODE keyblock) { return count_uids_with_flag (keyblock, NODFLG_SELUID); } static int count_selected_keys (KBNODE keyblock) { return count_keys_with_flag (keyblock, NODFLG_SELKEY); } /* Returns how many real (i.e. not attribute) uids are unmarked. */ static int real_uids_left (KBNODE keyblock) { KBNODE node; int real = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODFLG_SELUID) && !node->pkt->pkt.user_id->attrib_data) real++; return real; } /* * Ask whether the signature should be revoked. If the user commits this, * flag bit MARK_A is set on the signature and the user ID. */ static void ask_revoke_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node) { int doit = 0; PKT_user_id *uid; PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); if (!unode) { log_error ("Oops: no user ID for signature\n"); return; } uid = unode->pkt->pkt.user_id; if (opt.with_colons) { if (uid->attrib_data) printf ("uat:::::::::%u %lu", uid->numattribs, uid->attrib_len); else { es_printf ("uid:::::::::"); es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); } es_printf ("\n"); print_and_check_one_sig_colon (ctrl, keyblock, node, NULL, NULL, NULL, NULL, 1); } else { char *p = utf8_to_native (unode->pkt->pkt.user_id->name, unode->pkt->pkt.user_id->len, 0); tty_printf (_("user ID: \"%s\"\n"), p); xfree (p); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), ""); } if (sig->flags.expired) { tty_printf (_("This signature expired on %s.\n"), expirestr_from_sig (sig)); /* Use a different question so we can have different help text */ doit = cpr_get_answer_is_yes ("ask_revoke_sig.expired", _("Are you sure you still want to revoke it? (y/N) ")); } else doit = cpr_get_answer_is_yes ("ask_revoke_sig.one", _("Create a revocation certificate for this signature? (y/N) ")); if (doit) { node->flag |= NODFLG_MARK_A; unode->flag |= NODFLG_MARK_A; } } /* * Display all user ids of the current public key together with signatures * done by one of our keys. Then walk over all this sigs and ask the user * whether he wants to revoke this signature. * Return: True when the keyblock has changed. */ static int menu_revsig (ctrl_t ctrl, kbnode_t keyblock) { PKT_signature *sig; PKT_public_key *primary_pk; KBNODE node; int changed = 0; int rc, any, skip = 1, all = !count_selected_uids (keyblock); struct revocation_reason_info *reason = NULL; log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* First check whether we have any signatures at all. */ any = 0; for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) skip = 0; else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), have_secret_key_with_kid (sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { any = 1; break; } } } if (!any) { tty_printf (_("Not signed by you.\n")); return 0; } /* FIXME: detect duplicates here */ tty_printf (_("You have signed these user IDs on key %s:\n"), keystr_from_pk (keyblock->pkt->pkt.public_key)); for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) { PKT_user_id *uid = node->pkt->pkt.user_id; /* Hmmm: Should we show only UIDs with a signature? */ tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); skip = 0; } else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), have_secret_key_with_kid (sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), sig->flags.revocable ? "" : _(" (non-revocable)")); if (sig->flags.revocable) node->flag |= NODFLG_SELSIG; } else if (sig->sig_class == 0x30) { tty_printf (" "); tty_printf (_("revoked by your key %s on %s\n"), keystr (sig->keyid), datestr_from_sig (sig)); } } } tty_printf ("\n"); /* ask */ for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_SELSIG)) continue; ask_revoke_sig (ctrl, keyblock, node); } /* present selected */ any = 0; for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_MARK_A)) continue; if (!any) { any = 1; tty_printf (_("You are about to revoke these signatures:\n")); } if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } else if (node->pkt->pkttype == PKT_SIGNATURE) { sig = node->pkt->pkt.signature; tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), "", sig->flags.exportable ? "" : _(" (non-exportable)")); } } if (!any) return 0; /* none selected */ if (!cpr_get_answer_is_yes ("ask_revoke_sig.okay", _("Really create the revocation certificates? (y/N) "))) return 0; /* forget it */ reason = ask_revocation_reason (0, 1, 0); if (!reason) { /* user decided to cancel */ return 0; } /* now we can sign the user ids */ reloop: /* (must use this, because we are modifying the list) */ primary_pk = keyblock->pkt->pkt.public_key; for (node = keyblock; node; node = node->next) { KBNODE unode; PACKET *pkt; struct sign_attrib attrib; PKT_public_key *signerkey; if (!(node->flag & NODFLG_MARK_A) || node->pkt->pkttype != PKT_SIGNATURE) continue; unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); log_assert (unode); /* we already checked this */ memset (&attrib, 0, sizeof attrib); attrib.reason = reason; attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable; node->flag &= ~NODFLG_MARK_A; signerkey = xmalloc_secure_clear (sizeof *signerkey); if (get_seckey (ctrl, signerkey, node->pkt->pkt.signature->keyid)) { log_info (_("no secret key\n")); free_public_key (signerkey); continue; } rc = make_keysig_packet (ctrl, &sig, primary_pk, unode->pkt->pkt.user_id, NULL, signerkey, 0x30, 0, 0, 0, sign_mk_attrib, &attrib, NULL); free_public_key (signerkey); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ update_trust = 1; /* Are we revoking our own uid? */ if (primary_pk->keyid[0] == sig->keyid[0] && primary_pk->keyid[1] == sig->keyid[1]) unode->pkt->pkt.user_id->flags.revoked = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (unode, new_kbnode (pkt), 0); goto reloop; } release_revocation_reason_info (reason); return changed; } /* return 0 if revocation of NODE (which must be a User ID) was successful, non-zero if there was an error. *modified will be set to 1 if a change was made. */ static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; gpg_error_t rc; if (node->pkt->pkttype != PKT_USER_ID) { rc = gpg_error (GPG_ERR_NO_USER_ID); write_status_error ("keysig", rc); log_error (_("tried to revoke a non-user ID: %s\n"), gpg_strerror (rc)); return 1; } else { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->flags.revoked) { char *user = utf8_to_native (uid->name, uid->len, 0); log_info (_("user ID \"%s\" is already revoked\n"), user); xfree (user); } else { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; u32 timestamp = make_timestamp (); if (uid->created >= timestamp) { /* Okay, this is a problem. The user ID selfsig was created in the future, so we need to warn the user and set our revocation timestamp one second after that so everything comes out clean. */ log_info (_("WARNING: a user ID signature is dated %d" " seconds in the future\n"), uid->created - timestamp); timestamp = uid->created + 1; } memset (&attrib, 0, sizeof attrib); /* should not need to cast away const here; but revocation_reason_build_cb needs to take a non-const void* in order to meet the function signtuare for the mksubpkt argument to make_keysig_packet */ attrib.reason = (struct revocation_reason_info *)reason; rc = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x30, 0, timestamp, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); return 1; } else { pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); #ifndef NO_TRUST_MODELS /* If the trustdb has an entry for this key+uid then the trustdb needs an update. */ if (!update_trust && ((get_validity (ctrl, keyblock, pk, uid, NULL, 0) & TRUST_MASK) >= TRUST_UNDEFINED)) update_trust = 1; #endif /*!NO_TRUST_MODELS*/ node->pkt->pkt.user_id->flags.revoked = 1; if (modified) *modified = 1; } } return 0; } } /* Revoke a user ID (i.e. revoke a user ID selfsig). Return true if keyblock changed. */ static int menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; size_t valid_uids; /* Note that this is correct as per the RFCs, but nevertheless somewhat meaningless in the real world. 1991 did define the 0x30 sig class, but PGP 2.x did not actually implement it, so it would probably be safe to use v4 revocations everywhere. -ds */ for (node = pub_keyblock; node; node = node->next) if (pk->version > 3 || (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->selfsigversion > 3)) { if ((reason = ask_revocation_reason (0, 1, 4))) break; else goto leave; } /* Too make sure that we do not revoke the last valid UID, we first count how many valid UIDs there are. */ valid_uids = 0; for (node = pub_keyblock; node; node = node->next) valid_uids += node->pkt->pkttype == PKT_USER_ID && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired; reloop: /* (better this way because we are modifying the keyring) */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID)) { int modified = 0; /* Make sure that we do not revoke the last valid UID. */ if (valid_uids == 1 && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired) { log_error (_("Cannot revoke the last valid user ID.\n")); goto leave; } rc = core_revuid (ctrl, pub_keyblock, node, reason, &modified); if (rc) goto leave; if (modified) { node->flag &= ~NODFLG_SELUID; changed = 1; goto reloop; } } if (changed) commit_kbnode (&pub_keyblock); leave: release_revocation_reason_info (reason); return changed; } /* * Revoke the whole key. */ static int menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; int rc, changed = 0; struct revocation_reason_info *reason; PACKET *pkt; PKT_signature *sig; if (pk->flags.revoked) { tty_printf (_("Key %s is already revoked.\n"), keystr_from_pk (pk)); return 0; } reason = ask_revocation_reason (1, 0, 0); /* user decided to cancel */ if (!reason) return 0; rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x20, 0, 0, 0, revocation_reason_build_cb, reason, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto scram; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), 0); commit_kbnode (&pub_keyblock); update_trust = 1; scram: release_revocation_reason_info (reason); return changed; } static int menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *mainpk; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; reason = ask_revocation_reason (1, 0, 0); if (!reason) return 0; /* User decided to cancel. */ reloop: /* (better this way because we are modifying the keyring) */ mainpk = pub_keyblock->pkt->pkt.public_key; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (node->flag & NODFLG_SELKEY)) { PACKET *pkt; PKT_signature *sig; PKT_public_key *subpk = node->pkt->pkt.public_key; struct sign_attrib attrib; if (subpk->flags.revoked) { tty_printf (_("Subkey %s is already revoked.\n"), keystr_from_pk (subpk)); continue; } memset (&attrib, 0, sizeof attrib); attrib.reason = reason; node->flag &= ~NODFLG_SELKEY; rc = make_keysig_packet (ctrl, &sig, mainpk, NULL, subpk, mainpk, 0x28, 0, 0, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); goto reloop; } } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys no longer are used to certify other keys, so there is no change in trust when revoking/removing them */ release_revocation_reason_info (reason); return changed; } /* Note that update_ownertrust is going to mark the trustdb dirty when enabling or disabling a key. This is arguably sub-optimal as disabled keys are still counted in the web of trust, but perhaps not worth adding extra complexity to change. -ds */ #ifndef NO_TRUST_MODELS static int enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable) { PKT_public_key *pk = find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt.public_key; unsigned int trust, newtrust; trust = newtrust = get_ownertrust (ctrl, pk); newtrust &= ~TRUST_FLAG_DISABLED; if (disable) newtrust |= TRUST_FLAG_DISABLED; if (trust == newtrust) return 0; /* already in that state */ update_ownertrust (ctrl, pk, newtrust); return 0; } #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock) { KBNODE node; int select_all = !count_selected_uids (keyblock); int count = 0; PKT_public_key *pk = NULL; /* Look for the public key first. We have to be really, really, explicit as to which photo this is, and what key it is a UID on since people may want to sign it. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; count++; if ((select_all || (node->flag & NODFLG_SELUID)) && uid->attribs != NULL) { int i; for (i = 0; i < uid->numattribs; i++) { byte type; u32 size; if (uid->attribs[i].type == ATTRIB_IMAGE && parse_image_header (&uid->attribs[i], &type, &size)) { tty_printf (_("Displaying %s photo ID of size %ld for " "key %s (uid %d)\n"), image_type_to_string (type, 1), (ulong) size, keystr_from_pk (pk), count); show_photos (ctrl, &uid->attribs[i], 1, pk, uid); } } } } } } diff --git a/g10/keyedit.h b/g10/keyedit.h index 23a126bc3..d1f453a6f 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -1,59 +1,60 @@ /* keyedit.h - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch * Copyright (C) 2015-2017 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef GNUPG_G10_KEYEDIT_H #define GNUPG_G10_KEYEDIT_H #define NODFLG_BADSIG (1<<0) /* Bad signature. */ #define NODFLG_NOKEY (1<<1) /* No public key. */ #define NODFLG_SIGERR (1<<2) /* Other sig error. */ #define NODFLG_MARK_A (1<<4) /* Temporary mark. */ #define NODFLG_DELSIG (1<<5) /* To be deleted. */ #define NODFLG_SELUID (1<<8) /* Indicate the selected userid. */ #define NODFLG_SELKEY (1<<9) /* Indicate the selected key. */ #define NODFLG_SELSIG (1<<10) /* Indicate a selected signature. */ /*-- keyedit.c --*/ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check ); void keyedit_passwd (ctrl_t ctrl, const char *username); void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid); void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr); void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local); void keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr, char **subkeyfprs); void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock); -int keyedit_print_one_sig (ctrl_t ctrl, int rc, kbnode_t keyblock, +int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, + int rc, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended); #endif /* GNUPG_G10_KEYEDIT_H */ diff --git a/g10/keylist.c b/g10/keylist.c index 7203d160b..86d1c564f 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,2011 +1,2011 @@ /* keylist.c - Print information about OpenPGP keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2008, 2010, 2012 Free Software Foundation, Inc. * Copyright (C) 2013, 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM # include /* for setmode() */ #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "photoid.h" #include "../common/util.h" #include "../common/ttyio.h" #include "trustdb.h" #include "main.h" #include "../common/i18n.h" #include "../common/status.h" #include "call-agent.h" #include "../common/mbox-util.h" #include "../common/zb32.h" #include "tofu.h" #include "../common/compliance.h" static void list_all (ctrl_t, int, int); static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret); static void locate_one (ctrl_t ctrl, strlist_t names); static void print_card_serialno (const char *serialno); struct keylist_context { int check_sigs; /* If set signatures shall be verified. */ int good_sigs; /* Counter used if CHECK_SIGS is set. */ int inv_sigs; /* Counter used if CHECK_SIGS is set. */ int no_key; /* Counter used if CHECK_SIGS is set. */ int oth_err; /* Counter used if CHECK_SIGS is set. */ int no_validity; /* Do not show validity. */ }; static void list_keyblock (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx); /* The stream used to write attribute packets to. */ static estream_t attrib_fp; /* Release resources from a keylist context. */ static void keylist_context_release (struct keylist_context *listctx) { (void)listctx; /* Nothing to release. */ } /* List the keys. If list is NULL, all available keys are listed. With LOCATE_MODE set the locate algorithm is used to find a key. */ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode) { #ifndef NO_TRUST_MODELS if (opt.with_colons) { byte trust_model, marginals, completes, cert_depth, min_cert_level; ulong created, nextcheck; read_trust_options (ctrl, &trust_model, &created, &nextcheck, &marginals, &completes, &cert_depth, &min_cert_level); es_fprintf (es_stdout, "tru:"); if (nextcheck && nextcheck <= make_timestamp ()) es_fprintf (es_stdout, "o"); if (trust_model != opt.trust_model) es_fprintf (es_stdout, "t"); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { if (marginals != opt.marginals_needed) es_fprintf (es_stdout, "m"); if (completes != opt.completes_needed) es_fprintf (es_stdout, "c"); if (cert_depth != opt.max_cert_depth) es_fprintf (es_stdout, "d"); if (min_cert_level != opt.min_cert_level) es_fprintf (es_stdout, "l"); } es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck); /* Only show marginals, completes, and cert_depth in the classic or PGP trust models since they are not meaningful otherwise. */ if (trust_model == TM_PGP || trust_model == TM_CLASSIC) es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth); es_fprintf (es_stdout, "\n"); } #endif /*!NO_TRUST_MODELS*/ /* We need to do the stale check right here because it might need to update the keyring while we already have the keyring open. This is very bad for W32 because of a sharing violation. For real OSes it might lead to false results if we are later listing a keyring which is associated with the inode of a deleted file. */ check_trustdb_stale (ctrl); #ifdef USE_TOFU tofu_begin_batch_update (ctrl); #endif if (locate_mode) locate_one (ctrl, list); else if (!list) list_all (ctrl, 0, opt.with_secret); else list_one (ctrl, list, 0, opt.with_secret); #ifdef USE_TOFU tofu_end_batch_update (ctrl); #endif } void secret_key_list (ctrl_t ctrl, strlist_t list) { (void)ctrl; check_trustdb_stale (ctrl); if (!list) list_all (ctrl, 1, 0); else /* List by user id */ list_one (ctrl, list, 1, 0); } char * format_seckey_info (ctrl_t ctrl, PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; char *info; keyid_from_pk (pk, keyid); p = get_user_id_native (ctrl, keyid); info = xtryasprintf ("sec %s/%s %s %s", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); return info; } void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk) { char *p = format_seckey_info (ctrl, pk); tty_printf ("\n%s\n", p); xfree (p); } /* Print information about the public key. With FP passed as NULL, - the tty output interface is used, otherwise output is directted to + the tty output interface is used, otherwise output is directed to the given stream. */ void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; keyid_from_pk (pk, keyid); /* If the pk was chosen by a particular user ID, that is the one to print. */ if (pk->user_id) p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); else p = get_user_id_native (ctrl, keyid); if (fp) tty_printf ("\n"); tty_fprintf (fp, "%s %s/%s %s %s\n", pk->flags.primary? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); } /* Print basic information of a secret key including the card serial number information. */ #ifdef ENABLE_CARD_SUPPORT void print_card_key_info (estream_t fp, kbnode_t keyblock) { kbnode_t node; char *hexgrip; char *serialno; int s2k_char; char pkstrbuf[PUBKEY_STRING_SIZE]; int indent; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { int rc; PKT_public_key *pk = node->pkt->pkt.public_key; serialno = NULL; rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) { log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); s2k_char = '?'; } else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) s2k_char = serialno? '>':' '; else s2k_char = '#'; /* Key not found. */ tty_fprintf (fp, "%s%c %s/%s %n", node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb", s2k_char, pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), &indent); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); if (serialno) { tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s", serialno); } tty_fprintf (fp, "\n"); xfree (hexgrip); xfree (serialno); } } } #endif /*ENABLE_CARD_SUPPORT*/ /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { char status[40]; /* Don't print these. */ if (len > 256) return; snprintf (status, sizeof status, "%d %u %u ", type, flags, (unsigned int) len); write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0); } /* Print a policy URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_policy_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical signature policy: "); else str = _("Signature policy: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) write_status_buffer (STATUS_POLICY_URL, p, len, 0); } } /* Print a keyserver URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_keyserver_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical preferred keyserver: "); else str = _("Preferred keyserver: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) status_one_subpacket (SIGSUBPKT_PREF_KS, len, (crit ? 0x02 : 0) | 0x01, p); } } /* Print notation data. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. * * Defined bits in WHICH: * 1 - standard notations * 2 - user notations */ void show_notation (PKT_signature * sig, int indent, int mode, int which) { estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; notation_t nd, notations; if (which == 0) which = 3; notations = sig_to_notation (sig); /* There may be multiple notations in the same sig. */ for (nd = notations; nd; nd = nd->next) { if (mode != 2) { int has_at = !!strchr (nd->name, '@'); if ((which & 1 && !has_at) || (which & 2 && has_at)) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (nd->flags.critical) str = _("Critical signature notation: "); else str = _("Signature notation: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); /* This is all UTF8 */ tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0); tty_fprintf (fp, "="); tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0); /* (We need to use log_printf so that the next call to a log function does not insert an extra LF.) */ if (mode > 0) log_printf ("\n"); else tty_fprintf (fp, "\n"); } } if (mode > 0) { write_status_buffer (STATUS_NOTATION_NAME, nd->name, strlen (nd->name), 0); if (nd->flags.critical || nd->flags.human) write_status_text (STATUS_NOTATION_FLAGS, nd->flags.critical && nd->flags.human? "1 1" : nd->flags.critical? "1 0" : "0 1"); write_status_buffer (STATUS_NOTATION_DATA, nd->value, strlen (nd->value), 50); } } free_notation (notations); } static void print_signature_stats (struct keylist_context *s) { if (!s->check_sigs) return; /* Signature checking was not requested. */ /* Better flush stdout so that the stats are always printed after * the output. */ es_fflush (es_stdout); if (s->good_sigs) log_info (ngettext("%d good signature\n", "%d good signatures\n", s->good_sigs), s->good_sigs); if (s->inv_sigs) log_info (ngettext("%d bad signature\n", "%d bad signatures\n", s->inv_sigs), s->inv_sigs); if (s->no_key) log_info (ngettext("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", s->no_key), s->no_key); if (s->oth_err) log_info (ngettext("%d signature not checked due to an error\n", "%d signatures not checked due to errors\n", s->oth_err), s->oth_err); } /* List all keys. If SECRET is true only secret keys are listed. If MARK_SECRET is true secret keys are indicated in a public key listing. */ static void list_all (ctrl_t ctrl, int secret, int mark_secret) { KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc = 0; int any_secret; const char *lastresname, *resname; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; hd = keydb_new (); if (!hd) rc = gpg_error_from_syserror (); else rc = keydb_search_first (hd); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); goto leave; } lastresname = NULL; do { rc = keydb_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if (secret || mark_secret) any_secret = !agent_probe_any_secret_key (NULL, keyblock); else any_secret = 0; if (secret && !any_secret) ; /* Secret key listing requested but this isn't one. */ else { if (!opt.with_colons) { resname = keydb_get_resource_name (hd); if (lastresname != resname) { int i; es_fprintf (es_stdout, "%s\n", resname); for (i = strlen (resname); i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); lastresname = resname; } } merge_keys_and_selfsig (ctrl, keyblock); list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); if (keydb_get_skipped_counter (hd)) log_info (ngettext("Warning: %lu key skipped due to its large size\n", "Warning: %lu keys skipped due to their large sizes\n", keydb_get_skipped_counter (hd)), keydb_get_skipped_counter (hd)); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); leave: keylist_context_release (&listctx); release_kbnode (keyblock); keydb_release (hd); } static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) { int rc = 0; KBNODE keyblock = NULL; GETKEY_CTX ctx; const char *resname; const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) listctx.check_sigs = 1; /* fixme: using the bynames function has the disadvantage that we * don't know whether one of the names given was not found. OTOH, * this function has the advantage to list the names in the * sequence as defined by the keyDB and does not duplicate * outputs. A solution could be do test whether all given have * been listed (this needs a way to use the keyDB search * functions) or to have the search function return indicators for * found names. Yet another way is to use the keydb search * facilities directly. */ rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock); if (rc) { log_error ("error reading key: %s\n", gpg_strerror (rc)); getkey_end (ctrl, ctx); return; } do { if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons) { resname = keydb_get_resource_name (get_ctx_handle (ctx)); es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname); for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } list_keyblock (ctrl, keyblock, secret, mark_secret, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (!getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void locate_one (ctrl_t ctrl, strlist_t names) { int rc = 0; strlist_t sl; GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; for (sl = names; sl; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY) log_error ("error reading key: %s\n", gpg_strerror (rc)); else if (opt.verbose) log_info (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (rc)); } else { do { list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); ctx = NULL; } } if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void print_key_data (PKT_public_key * pk) { int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0; int i; for (i = 0; i < n; i++) { es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i])); mpi_print (es_stdout, pk->pkey[i], 1); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } static void print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock) { unsigned int use = pk->pubkey_usage; int c_printed = 0; if (use & PUBKEY_USAGE_ENC) es_putc ('e', es_stdout); if (use & PUBKEY_USAGE_SIG) { es_putc ('s', es_stdout); if (pk->flags.primary) { es_putc ('c', es_stdout); /* The PUBKEY_USAGE_CERT flag was introduced later and we used to always print 'c' for a primary key. To avoid any regression here we better track whether we printed 'c' already. */ c_printed = 1; } } if ((use & PUBKEY_USAGE_CERT) && !c_printed) es_putc ('c', es_stdout); if ((use & PUBKEY_USAGE_AUTH)) es_putc ('a', es_stdout); if ((use & PUBKEY_USAGE_UNKNOWN)) es_putc ('?', es_stdout); if (keyblock) { /* Figure out the usable capabilities. */ KBNODE k; int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = k->pkt->pkt.public_key; if (pk->flags.primary) disabled = pk_is_disabled (pk); if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired) { if (pk->pubkey_usage & PUBKEY_USAGE_ENC) enc = 1; if (pk->pubkey_usage & PUBKEY_USAGE_SIG) { sign = 1; if (pk->flags.primary) cert = 1; } if (pk->pubkey_usage & PUBKEY_USAGE_CERT) cert = 1; if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) auth = 1; } } } if (enc) es_putc ('E', es_stdout); if (sign) es_putc ('S', es_stdout); if (cert) es_putc ('C', es_stdout); if (auth) es_putc ('A', es_stdout); if (disabled) es_putc ('D', es_stdout); } es_putc (':', es_stdout); } /* FLAGS: 0x01 hashed 0x02 critical */ static void print_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { size_t i; es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len); for (i = 0; i < len; i++) { /* printable ascii other than : and % */ if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%') es_fprintf (es_stdout, "%c", buf[i]); else es_fprintf (es_stdout, "%%%02X", buf[i]); } es_fprintf (es_stdout, "\n"); } void print_subpackets_colon (PKT_signature * sig) { byte *i; log_assert (opt.show_subpackets); for (i = opt.show_subpackets; *i; i++) { const byte *p; size_t len; int seq, crit; seq = 0; while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p); seq = 0; while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p); } } void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) { int i; if (!attrib_fp) return; for (i = 0; i < uid->numattribs; i++) { if (is_status_enabled ()) { byte array[MAX_FINGERPRINT_LEN], *p; char buf[(MAX_FINGERPRINT_LEN * 2) + 90]; size_t j, n; if (!pk) BUG (); fingerprint_from_pk (pk, array, &n); p = array; for (j = 0; j < n; j++, p++) sprintf (buf + 2 * j, "%02X", *p); sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u", (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1, uid->numattribs, (ulong) uid->created, (ulong) uid->expiredate, ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) | (uid->flags.expired ? 0x04 : 0))); write_status_text (STATUS_ATTRIBUTE, buf); } es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp); es_fflush (attrib_fp); } } static void list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, struct keylist_context *listctx) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; int skip_sigs = 0; char *hexgrip = NULL; char *serialno = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { /* Encode some info about the secret key in SECRET. */ if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } if (!listctx->no_validity) check_trustdb_stale (ctrl); /* Print the "pub" line and in KF_NONE mode the fingerprint. */ print_key_line (ctrl, es_stdout, pk, secret); if (fpr) print_fingerprint (ctrl, NULL, pk, 0); if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (serialno) print_card_serialno (serialno); if (opt.with_key_data) print_key_data (pk); if (opt.with_key_origin && (pk->keyorg || pk->keyupdate || pk->updateurl)) { char updatestr[MK_DATESTR_SIZE]; es_fprintf (es_stdout, " origin=%s last=%s %s", key_origin_string (pk->keyorg), mk_datestr (updatestr, sizeof updatestr, pk->keyupdate), pk->updateurl? "url=":""); if (pk->updateurl) print_utf8_string (es_stdout, pk->updateurl); es_putc ('\n', es_stdout); } for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int indent; int kl = opt.keyid_format == KF_NONE? 10 : keystrlen (); if ((uid->flags.expired || uid->flags.revoked) && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS)) { skip_sigs = 1; continue; } else skip_sigs = 0; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if ((uid->flags.revoked || uid->flags.expired) || ((opt.list_options & LIST_SHOW_UID_VALIDITY) && !listctx->no_validity)) { const char *validity; validity = uid_trust_string_fixed (ctrl, pk, uid); indent = ((kl + (opt.legacy_list_mode? 9:11)) - atoi (uid_trust_string_fixed (ctrl, NULL, NULL))); if (indent < 0 || indent > 40) indent = 0; es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity); } else { indent = kl + (opt.legacy_list_mode? 10:12); es_fprintf (es_stdout, "uid%*s", indent, ""); } print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); if (opt.with_wkd_hash) { char *mbox, *hash, *p; char hashbuf[32]; mbox = mailbox_from_userid (uid->name); if (mbox && (p = strchr (mbox, '@'))) { *p++ = 0; gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); hash = zb32_encode (hashbuf, 8*20); if (hash) { es_fprintf (es_stdout, " %*s%s@%s\n", indent, "", hash, p); xfree (hash); } } xfree (mbox); } if (opt.with_key_origin && (uid->keyorg || uid->keyupdate || uid->updateurl)) { char updatestr[MK_DATESTR_SIZE]; es_fprintf (es_stdout, " %*sorigin=%s last=%s %s", indent, "", key_origin_string (uid->keyorg), mk_datestr (updatestr, sizeof updatestr, uid->keyupdate), uid->updateurl? "url=":""); if (uid->updateurl) print_utf8_string (es_stdout, uid->updateurl); es_putc ('\n', es_stdout); } if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL) show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk2 = node->pkt->pkt.public_key; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { skip_sigs = 1; continue; } else skip_sigs = 0; xfree (serialno); serialno = NULL; xfree (hexgrip); hexgrip = NULL; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk2, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } /* Print the "sub" line. */ print_key_line (ctrl, es_stdout, pk2, secret); if (fpr > 1 || opt.with_subkey_fingerprint) { print_fingerprint (ctrl, NULL, pk2, 0); if (serialno) print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; if (listctx->check_sigs) { rc = check_key_signature (ctrl, keyblock, node, NULL); switch (gpg_err_code (rc)) { case 0: listctx->good_sigs++; sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: listctx->inv_sigs++; sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: listctx->no_key++; continue; default: listctx->oth_err++; sigrc = '%'; break; } /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keyedit.c:print_and_check_one_sig */ } else { rc = 0; sigrc = ' '; } if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig " "[unexpected signature class 0x%02x]\n", sig->sig_class); continue; } es_fputs (sigstr, 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 (opt.list_options & LIST_SHOW_SIG_EXPIRE) es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); es_fprintf (es_stdout, " "); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (!opt.fast_list_mode) { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n); print_utf8_buffer (es_stdout, p, n); xfree (p); } 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); /* fixme: check or list other sigs here */ } } es_putc ('\n', es_stdout); xfree (serialno); xfree (hexgrip); } void print_revokers (estream_t fp, PKT_public_key * pk) { /* print the revoker record */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i, j; for (i = 0; i < pk->numrevkeys; i++) { byte *p; es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid); p = pk->revkey[i].fpr; for (j = 0; j < 20; j++, p++) es_fprintf (fp, "%02X", *p); es_fprintf (fp, ":%02x%s:\n", pk->revkey[i].class, (pk->revkey[i].class & 0x40) ? "s" : ""); } } } /* Print the compliance flags to field 18. PK is the public key. * KEYLENGTH is the length of the key in bits and CURVENAME is either * NULL or the name of the curve. The latter two args are here * merely because the caller has already computed them. */ static void print_compliance_flags (PKT_public_key *pk, unsigned int keylength, const char *curvename) { int any = 0; if (!keylength) keylength = nbits_from_pk (pk); if (pk->version == 5) { es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout); any++; } if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey, keylength, curvename)) { es_fprintf (es_stdout, any ? " %s" : "%s", gnupg_status_compliance_flag (CO_DE_VS)); any++; } } /* List a key in colon mode. If SECRET is true this is a secret key record (i.e. requested via --list-secret-key). If HAS_SECRET a secret key is available even if SECRET is not set. */ static void list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; u32 keyid[2]; int trustletter = 0; int trustletter_print; int ownertrust_print; int ulti_hack = 0; int i; char *hexgrip_buffer = NULL; const char *hexgrip = NULL; char *serialno = NULL; int stubkey; unsigned int keylength; char *curve = NULL; const char *curvename = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); /* In the error case we print an empty string so that we have a * "grp" record for each and subkey - even if it is empty. This * may help to prevent sync problems. */ hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret || has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk, keyid); if (!pk->flags.valid) trustletter_print = 'i'; else if (pk->flags.revoked) trustletter_print = 'r'; else if (pk->has_expired) trustletter_print = 'e'; else if (opt.fast_list_mode || opt.no_expensive_trust_checks) trustletter_print = 0; else { trustletter = get_validity_info (ctrl, keyblock, pk, NULL); if (trustletter == 'u') ulti_hack = 1; trustletter_print = trustletter; } if (!opt.fast_list_mode && !opt.no_expensive_trust_checks) ownertrust_print = get_ownertrust_info (ctrl, pk, 0); else ownertrust_print = 0; keylength = nbits_from_pk (pk); es_fputs (secret? "sec:":"pub:", es_stdout); if (trustletter_print) es_putc (trustletter_print, es_stdout); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::", keylength, pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], colon_datestr_from_pk (pk), colon_strtime (pk->expiredate)); if (ownertrust_print) es_putc (ownertrust_print, es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); print_capabilities (ctrl, pk, keyblock); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { curve = openpgp_oid_to_str (pk->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk, keylength, curvename); es_putc (':', es_stdout); /* End of field 18 (compliance). */ if (pk->keyupdate) es_fputs (colon_strtime (pk->keyupdate), es_stdout); es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":""); if (pk->updateurl) es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl), ":", NULL); es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); print_revokers (es_stdout, pk); print_fingerprint (ctrl, NULL, pk, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int uid_validity; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if (uid->flags.revoked) uid_validity = 'r'; else if (uid->flags.expired) uid_validity = 'e'; else if (opt.no_expensive_trust_checks) uid_validity = 0; else if (ulti_hack) uid_validity = 'u'; else uid_validity = get_validity_info (ctrl, keyblock, pk, uid); es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout); if (uid_validity) es_putc (uid_validity, es_stdout); es_fputs ("::::", es_stdout); es_fprintf (es_stdout, "%s:", colon_strtime (uid->created)); es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate)); namehash_from_uid (uid); for (i = 0; i < 20; i++) es_fprintf (es_stdout, "%02X", uid->namehash[i]); es_fprintf (es_stdout, "::"); if (uid->attrib_data) es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); es_fputs (":::::::::", es_stdout); if (uid->keyupdate) es_fputs (colon_strtime (uid->keyupdate), es_stdout); es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":""); if (uid->updateurl) es_write_sanitized (es_stdout, uid->updateurl, strlen (uid->updateurl), ":", NULL); es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); #ifdef USE_TOFU if (!uid->attrib_data && opt.with_tofu_info && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)) { /* Print a "tfs" record. */ tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name); } #endif /*USE_TOFU*/ } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 keyid2[2]; PKT_public_key *pk2; int need_hexgrip = !!hexgrip; pk2 = node->pkt->pkt.public_key; xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL; xfree (serialno); serialno = NULL; if (need_hexgrip || secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk2, keyid2); es_fputs (secret? "ssb:":"sub:", es_stdout); if (!pk2->flags.valid) es_putc ('i', es_stdout); else if (pk2->flags.revoked) es_putc ('r', es_stdout); else if (pk2->has_expired) es_putc ('e', es_stdout); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) ; else { /* TRUSTLETTER should always be defined here. */ if (trustletter) es_fprintf (es_stdout, "%c", trustletter); } keylength = nbits_from_pk (pk2); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::", keylength, pk2->pubkey_algo, (ulong) keyid2[0], (ulong) keyid2[1], colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)); print_capabilities (ctrl, pk2, NULL); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA || pk2->pubkey_algo == PUBKEY_ALGO_ECDH) { xfree (curve); curve = openpgp_oid_to_str (pk2->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk2, keylength, curvename); es_putc (':', es_stdout); /* End of field 18. */ es_putc ('\n', es_stdout); print_fingerprint (ctrl, NULL, pk2, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc, fprokay = 0; char *sigstr; size_t fplen; byte fparray[MAX_FINGERPRINT_LEN]; char *siguid; size_t siguidlen; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig::::::::::%02x%c:\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); continue; } if (opt.check_sigs) { PKT_public_key *signer_pk = NULL; es_fflush (es_stdout); if (opt.no_sig_cache) signer_pk = xmalloc_clear (sizeof (PKT_public_key)); rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk, NULL, NULL, NULL); switch (gpg_err_code (rc)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if (opt.no_sig_cache) { if (!rc) { fingerprint_from_pk (signer_pk, fparray, &fplen); fprokay = 1; } free_public_key (signer_pk); } } else { rc = 0; sigrc = ' '; } if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) siguid = get_user_id (ctrl, sig->keyid, &siguidlen); else { siguid = NULL; siguidlen = 0; } es_fputs (sigstr, es_stdout); es_putc (':', 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 (sig->trust_depth || sig->trust_value) es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value); es_fprintf (es_stdout, ":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_fprintf (es_stdout, ":"); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else 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 (opt.no_sig_cache && opt.check_sigs && fprokay) { for (i = 0; i < fplen; i++) es_fprintf (es_stdout, "%02X", fparray[i]); } es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ xfree (siguid); } } xfree (curve); xfree (hexgrip_buffer); xfree (serialno); } /* * Reorder the keyblock so that the primary user ID (and not attribute * packet) comes first. Fixme: Replace this by a generic sort * function. */ static void do_reorder_keyblock (KBNODE keyblock, int attr) { KBNODE primary = NULL, primary0 = NULL, primary2 = NULL; KBNODE last, node; for (node = keyblock; node; primary0 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && ((attr && node->pkt->pkt.user_id->attrib_data) || (!attr && !node->pkt->pkt.user_id->attrib_data)) && node->pkt->pkt.user_id->flags.primary) { primary = primary2 = node; for (node = node->next; node; primary2 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { break; } } break; } } if (!primary) return; /* No primary key flag found (should not happen). */ for (last = NULL, node = keyblock; node; last = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; } log_assert (node); log_assert (last); /* The user ID is never the first packet. */ log_assert (primary0); /* Ditto (this is the node before primary). */ if (node == primary) return; /* Already the first one. */ last->next = primary; primary0->next = primary2->next; primary2->next = node; } void reorder_keyblock (KBNODE keyblock) { do_reorder_keyblock (keyblock, 1); do_reorder_keyblock (keyblock, 0); } static void list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { reorder_keyblock (keyblock); if (opt.with_colons) list_keyblock_colon (ctrl, keyblock, secret, has_secret); else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); if (secret) es_fflush (es_stdout); } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); } /* Print an hex digit in ICAO spelling. */ static void print_icao_hexdigit (estream_t fp, int c) { static const char *list[16] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot" }; tty_fprintf (fp, "%s", list[c&15]); } /* * Function to print the finperprint. * mode 0: as used in key listings, opt.with_colons is honored * 1: print using log_info () * 2: direct use of tty * 3: direct use of tty but only primary key. * 4: direct use of tty but only subkey. * 10: Same as 0 but with_colons etc is ignored. * 20: Same as 0 but using a compact format. * * Modes 1 and 2 will try and print both subkey and primary key * fingerprints. A MODE with bit 7 set is used internally. If * OVERRIDE_FP is not NULL that stream will be used in 0 instead * of es_stdout or instead of the TTY in modes 2 and 3. */ void print_fingerprint (ctrl_t ctrl, estream_t override_fp, PKT_public_key *pk, int mode) { char hexfpr[2*MAX_FINGERPRINT_LEN+1]; char *p; size_t i; estream_t fp; const char *text; int primary = 0; int with_colons = opt.with_colons; int with_icao = opt.with_icao_spelling; int compact = 0; if (mode == 10) { mode = 0; with_colons = 0; with_icao = 0; } else if (mode == 20) { mode = 0; with_colons = 0; compact = 1; } if (!opt.fingerprint && !opt.with_fingerprint && opt.with_subkey_fingerprint) compact = 1; if (pk->main_keyid[0] == pk->keyid[0] && pk->main_keyid[1] == pk->keyid[1]) primary = 1; /* Just to be safe */ if ((mode & 0x80) && !primary) { log_error ("primary key is not really primary!\n"); return; } mode &= ~0x80; if (!primary && (mode == 1 || mode == 2)) { PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk)); get_pubkey (ctrl, primary_pk, pk->main_keyid); print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80)); free_public_key (primary_pk); } if (mode == 1) { fp = log_get_stream (); if (primary) text = _("Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 2) { fp = override_fp; /* Use tty or given stream. */ if (primary) /* TRANSLATORS: this should fit into 24 bytes so that the * fingerprint data is properly aligned with the user ID */ text = _(" Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 3) { fp = override_fp; /* Use tty or given stream. */ text = _(" Key fingerprint ="); } else if (mode == 4) { fp = override_fp; /* Use tty or given stream. */ text = _(" Subkey fingerprint:"); } else { fp = override_fp? override_fp : es_stdout; if (opt.keyid_format == KF_NONE) { text = " "; /* To indent ICAO spelling. */ compact = 1; } else text = _(" Key fingerprint ="); } hexfingerprint (pk, hexfpr, sizeof hexfpr); if (with_colons && !mode) { es_fprintf (fp, "fpr:::::::::%s:", hexfpr); } else if (compact && !opt.fingerprint && !opt.with_fingerprint) { tty_fprintf (fp, "%*s%s", 6, "", hexfpr); } else { char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1]; format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr); if (compact) tty_fprintf (fp, "%*s%s", 6, "", fmtfpr); else tty_fprintf (fp, "%s %s", text, fmtfpr); } tty_fprintf (fp, "\n"); if (!with_colons && with_icao) { ; tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, ""); for (i = 0, p = hexfpr; *p; i++, p++) { if (!i) ; else if (!(i%8)) tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, ""); else if (!(i%4)) tty_fprintf (fp, " "); else tty_fprintf (fp, " "); print_icao_hexdigit (fp, xtoi_1 (p)); } tty_fprintf (fp, "\"\n"); } } /* Print the serial number of an OpenPGP card if available. */ static void print_card_serialno (const char *serialno) { if (!serialno) return; if (opt.with_colons) return; /* Handled elsewhere. */ es_fputs (_(" Card serial no. ="), es_stdout); es_putc (' ', es_stdout); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else es_fputs (serialno, es_stdout); es_putc ('\n', es_stdout); } /* Print a public or secret (sub)key line. Example: * * pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B367 * * Some global options may result in a different output format. If * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and * depending on the value a flag character is shown: * * 1 := ' ' Regular secret key * 2 := '#' Stub secret key * 3 := '>' Secret key is on a token. */ void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret) { char pkstrbuf[PUBKEY_STRING_SIZE]; tty_fprintf (fp, "%s%c %s", pk->flags.primary? (secret? "sec":"pub") /**/ : (secret? "ssb":"sub"), secret == 2? '#' : secret == 3? '>' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf)); if (opt.keyid_format != KF_NONE) tty_fprintf (fp, "/%s", keystr_from_pk (pk)); tty_fprintf (fp, " %s", datestr_from_pk (pk)); if ((opt.list_options & LIST_SHOW_USAGE)) { tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0)); } if (pk->flags.revoked) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->has_expired) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->expiredate) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } #if 0 /* I need to think about this some more. It's easy enough to include, but it looks sort of confusing in the listing... */ if (opt.list_options & LIST_SHOW_VALIDITY) { int validity = get_validity (ctrl, pk, NULL, NULL, 0); tty_fprintf (fp, " [%s]", trust_value_to_string (validity)); } #endif if (pk->pubkey_algo >= 100) tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo); tty_fprintf (fp, "\n"); /* if the user hasn't explicitly asked for human-readable fingerprints, show compact fpr of primary key: */ if (pk->flags.primary && !opt.fingerprint && !opt.with_fingerprint) print_fingerprint (ctrl, fp, pk, 20); } void set_attrib_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; /* Fixme: Do we need to check for the log stream here? */ if (attrib_fp && attrib_fp != log_get_stream ()) es_fclose (attrib_fp); attrib_fp = NULL; if (fd == -1) return; if (! gnupg_fd_valid (fd)) log_fatal ("attribute-fd is invalid: %s\n", strerror (errno)); #ifdef HAVE_DOSISH_SYSTEM setmode (fd, O_BINARY); #endif if (fd == 1) attrib_fp = es_stdout; else if (fd == 2) attrib_fp = es_stderr; else attrib_fp = es_fdopen (fd, "wb"); if (!attrib_fp) { log_fatal ("can't open fd %d for attribute output: %s\n", fd, strerror (errno)); } last_fd = fd; }