diff options
author | Werner Koch <wk@gnupg.org> | 2020-10-28 17:06:27 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2020-10-28 17:06:27 +0100 |
commit | 243f9176e799b2328f2e5bed93099bfc474fdc5a (patch) | |
tree | 3e2d0ca22cba6bd17e0e15d12f041ff17450ea3a /g10/keyedit.c | |
parent | gpg: Sort the signatures in standard key listings. (diff) | |
download | gnupg2-243f9176e799b2328f2e5bed93099bfc474fdc5a.tar.xz gnupg2-243f9176e799b2328f2e5bed93099bfc474fdc5a.zip |
gpg: New command --quick-revoke-sig
* g10/gpg.c (enum cmd_and_opt_values): Add aQuickRevSig.
(opts): Add --quick-revoke-sig.
(main): Implement.
* g10/keyedit.c (quick_find_keyblock): Add arg 'want_secret' and
adjust all callers.
(keyedit_quick_revsig): new.
* g10/revoke.c (get_default_sig_revocation_reason): New.
* g10/keylist.c (cmp_signodes): Make global.
--
GnuPG-bug-id: 5093
Diffstat (limited to 'g10/keyedit.c')
-rw-r--r-- | g10/keyedit.c | 246 |
1 files changed, 240 insertions, 6 deletions
diff --git a/g10/keyedit.c b/g10/keyedit.c index 19fd0a1fc..9f4aad24a 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -2278,7 +2278,7 @@ leave: * 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, +quick_find_keyblock (ctrl_t ctrl, const char *username, int want_secret, KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock) { gpg_error_t err; @@ -2321,7 +2321,7 @@ quick_find_keyblock (ctrl_t ctrl, const char *username, err = 0; keydb_pop_found_state (kdbhd); - if (!err) + if (!err && want_secret) { /* We require the secret primary key to set the primary UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); @@ -2379,7 +2379,7 @@ keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) #endif /* Search the key; we don't want the whole getkey stuff here. */ - err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); + err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock); if (err) goto leave; @@ -2422,7 +2422,7 @@ keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) #endif /* Search the key; we don't want the whole getkey stuff here. */ - err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); + err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock); if (err) goto leave; @@ -2502,7 +2502,7 @@ keyedit_quick_set_primary (ctrl_t ctrl, const char *username, check_trustdb_stale (ctrl); #endif - err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); + err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock); if (err) goto leave; @@ -2763,6 +2763,240 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, } +/* Unattended revocation of a key signatures. USERNAME specifies the + * key; this should best be a fingerprint. SIGTOREV is the user-id of + * the key for which the key signature shall be removed. Only + * non-self-signatures can be removed with this functions. If + * AFFECTED_UIDS is not NULL only the key signatures on these user-ids + * are revoked. */ +void +keyedit_quick_revsig (ctrl_t ctrl, const char *username, const char *sigtorev, + strlist_t affected_uids) +{ + gpg_error_t err; + int no_signing_key = 0; + KEYDB_HANDLE kdbhd = NULL; + kbnode_t keyblock = NULL; + PKT_public_key *primarypk; /* Points into KEYBLOCK. */ + u32 *primarykid; + PKT_public_key *pksigtorev = NULL; + u32 *pksigtorevkid; + kbnode_t node, n; + int skip_remaining; + int consider_sig; + strlist_t sl; + struct sign_attrib attrib = { 0 }; + +#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. Noet + * that we are looking for the public key here. */ + err = quick_find_keyblock (ctrl, username, 0, &kdbhd, &keyblock); + if (err) + goto leave; + log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY + || keyblock->pkt->pkttype == PKT_SECRET_KEY); + primarypk = keyblock->pkt->pkt.public_key; + primarykid = pk_keyid (primarypk); + + /* Get the signing key we want to revoke. This must be one of our + * signing keys. We will compare only the keyid because we don't + * assume that we have duplicated keyids on our own secret keys. If + * a there is a duplicated one we will notice this when creating the + * revocation. */ + pksigtorev = xtrycalloc (1, sizeof *pksigtorev); + if (!pksigtorev) + { + err = gpg_error_from_syserror (); + goto leave; + } + pksigtorev->req_usage = PUBKEY_USAGE_CERT; + err = getkey_byname (ctrl, NULL, pksigtorev, sigtorev, 1, NULL); + if (err) + { + no_signing_key = 1; + goto leave; + } + pksigtorevkid = pk_keyid (pksigtorev); + + /* Find the signatures we want to revoke and set a mark. */ + skip_remaining = consider_sig = 0; + for (node = keyblock; node; node = node->next) + { + node->flag &= ~NODFLG_MARK_A; + if (skip_remaining) + ; + else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + skip_remaining = 1; + else if (node->pkt->pkttype == PKT_USER_ID) + { + PKT_user_id *uid = node->pkt->pkt.user_id; + + consider_sig = !affected_uids; + for (sl = affected_uids; !consider_sig && sl; sl = sl->next) + { + const char *name = sl->d; + + if (uid->attrib_data) + ; + else if (*name == '=' + && strlen (name+1) == uid->len + && !memcmp (uid->name, name + 1, uid->len)) + { /* Exact match. */ + consider_sig = 1; + } + else if (ascii_memistr (uid->name, uid->len, + *name == '*'? name+1:name)) + { /* Case-insensitive substring match. */ + consider_sig = 1; + } + } + } + else if (node->pkt->pkttype == PKT_SIGNATURE) + { + /* We need to sort the signatures so that we can figure out + * whether the key signature has been revoked or the + * revocation has been superseded by a new key + * signature. */ + PKT_signature *sig; + unsigned int sigcount = 0; + kbnode_t *sigarray; + + /* Allocate an array large enogh for all signatures. */ + for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + sigcount++; + sigarray = xtrycalloc (sigcount, sizeof *sigarray); + if (!sigarray) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Now fill the array with signatures we are interested in. + * We also move NODE forward to the end. */ + sigcount = 0; + for (n=node; n && n->pkt->pkttype == PKT_SIGNATURE; node=n, n=n->next) + { + sig = node->pkt->pkt.signature; + if (!keyid_cmp (primarykid, sig->keyid)) + continue; /* Ignore self-signatures. */ + + if (keyid_cmp (pksigtorevkid, sig->keyid)) + continue; /* Ignore non-matching signatures. */ + + n->flag &= ~NODFLG_MARK_B; /* Clear flag used by cm_signode. */ + sigarray[sigcount++] = n; + } + + if (sigcount) + { + qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes); + + /* log_debug ("Sorted signatures:\n"); */ + /* for (idx=0; idx < sigcount; idx++) */ + /* { */ + /* sig = sigarray[idx]->pkt->pkt.signature; */ + /* log_debug ("%s 0x%02x %s\n", keystr (sig->keyid), */ + /* sig->sig_class, datestr_from_sig (sig)); */ + /* } */ + sig = sigarray[sigcount-1]->pkt->pkt.signature; + if ((consider_sig || !affected_uids) && IS_UID_REV (sig)) + { + if (!opt.quiet) + log_info ("sig by %s already revoked at %s\n", + keystr (sig->keyid), datestr_from_sig (sig)); + } + else if ((consider_sig && IS_UID_SIG (sig)) + || (!affected_uids && IS_KEY_SIG (sig))) + node->flag |= NODFLG_MARK_A; /* Select signature. */ + } + + xfree (sigarray); + } + } + + /* Check whether any signatures were done by the given key. We do + * not return an error if none were found. */ + for (node = keyblock; node; node = node->next) + if ((node->flag & NODFLG_MARK_A)) + break; + if (!node) + { + if (opt.verbose) + log_info (_("Not signed by you.\n")); + err = 0; + goto leave; + } + + /* Revoke all marked signatures. */ + attrib.reason = get_default_sig_revocation_reason (); + + reloop: /* (we must repeat because we are modifying the list) */ + for (node = keyblock; node; node = node->next) + { + kbnode_t unode; + PKT_signature *sig; + PACKET *pkt; + + if (!(node->flag & NODFLG_MARK_A)) + continue; + node->flag &= ~NODFLG_MARK_A; + + if (IS_KEY_SIG (node->pkt->pkt.signature)) + unode = NULL; + else + { + unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); + log_assert (unode); + } + + attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable; + + err = make_keysig_packet (ctrl, &sig, primarypk, + unode? unode->pkt->pkt.user_id : NULL, + NULL, pksigtorev, 0x30, 0, 0, + sign_mk_attrib, &attrib, NULL); + if (err) + { + log_error ("signing failed: %s\n", gpg_strerror (err)); + goto leave; + } + + pkt = xmalloc_clear (sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + if (unode) + insert_kbnode (unode, new_kbnode (pkt), 0); + goto reloop; + } + + err = keydb_update_keyblock (ctrl, kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + revalidation_mark (ctrl); + + leave: + if (err) + { + log_error (_("revoking the key signature failed: %s\n"), + gpg_strerror (err)); + if (no_signing_key) + print_further_info ("error getting key used to make the key signature"); + write_status_error ("keyedit.revoke.sig", err); + } + release_revocation_reason_info (attrib.reason); + free_public_key (pksigtorev); + release_kbnode (keyblock); + keydb_release (kdbhd); +} + + /* Unattended subkey creation function. * */ @@ -5984,7 +6218,7 @@ core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, 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 signutare for the + void* in order to meet the function signature for the mksubpkt argument to make_keysig_packet */ attrib.reason = (struct revocation_reason_info *)reason; |