aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-10-28 17:06:27 +0100
committerWerner Koch <wk@gnupg.org>2020-10-28 17:06:27 +0100
commit243f9176e799b2328f2e5bed93099bfc474fdc5a (patch)
tree3e2d0ca22cba6bd17e0e15d12f041ff17450ea3a
parent742e2729f4bcadfeb93260107462f4faa108d3b2 (diff)
downloadgnupg-243f9176e799b2328f2e5bed93099bfc474fdc5a.tar.gz
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
-rw-r--r--doc/gpg.texi11
-rw-r--r--g10/gpg.c20
-rw-r--r--g10/keyedit.c246
-rw-r--r--g10/keyedit.h2
-rw-r--r--g10/keylist.c2
-rw-r--r--g10/main.h6
-rw-r--r--g10/revoke.c10
7 files changed, 288 insertions, 9 deletions
diff --git a/doc/gpg.texi b/doc/gpg.texi
index ccb0689f6..48e52e98f 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -1147,6 +1147,17 @@ you want to specify a different revocation reason, or to supply
supplementary revocation text, you should use the interactive
sub-command @code{revuid} of @option{--edit-key}.
+@item --quick-revoke-sig @var{fpr} @var{signing-fpr} [@var{names}]
+@opindex quick-revoke-sig
+This command revokes the key signatures made by @var{signing-fpr} from
+the key specified by the fingerprint @var{fpr}. With @var{names}
+given only the signatures on user ids of the key matching any of the
+given names are affected (see @option{--quick-sign-key}). If a
+revocation already exists a notice is printed instead of creating a
+new revocation; no error is returned in this case. Note that key
+signature revocations may be superseded by a newer key signature and
+in turn again revoked.
+
@item --quick-set-primary-uid @var{user-id} @var{primary-user-id}
@opindex quick-set-primary-uid
This command sets or updates the primary user ID flag on an existing
diff --git a/g10/gpg.c b/g10/gpg.c
index 910977a6c..ae890ca9e 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -129,6 +129,7 @@ enum cmd_and_opt_values
aLSignKey,
aQuickSignKey,
aQuickLSignKey,
+ aQuickRevSig,
aQuickAddUid,
aQuickAddKey,
aQuickRevUid,
@@ -499,6 +500,8 @@ static gpgrt_opt_t opts[] = {
N_("quickly sign a key")),
ARGPARSE_c (aQuickLSignKey, "quick-lsign-key",
N_("quickly sign a key locally")),
+ ARGPARSE_c (aQuickRevSig, "quick-revoke-sig" ,
+ N_("quickly revoke a key signature")),
ARGPARSE_c (aSignKey, "sign-key" ,N_("sign a key")),
ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")),
ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")),
@@ -2631,6 +2634,7 @@ main (int argc, char **argv)
case aSign:
case aQuickSignKey:
case aQuickLSignKey:
+ case aQuickRevSig:
case aSignKey:
case aLSignKey:
case aStore:
@@ -4446,6 +4450,22 @@ main (int argc, char **argv)
}
break;
+ case aQuickRevSig:
+ {
+ const char *userid, *siguserid;
+
+ if (argc < 2)
+ wrong_args ("--quick-revoke-sig USER-ID SIG-USER-ID [userids]");
+ userid = *argv++; argc--;
+ siguserid = *argv++; argc--;
+ sl = NULL;
+ for( ; argc; argc--, argv++)
+ append_to_strlist2 (&sl, *argv, utf8_strings);
+ keyedit_quick_revsig (ctrl, userid, siguserid, sl);
+ free_strlist (sl);
+ }
+ break;
+
case aSignKey:
if( argc != 1 )
wrong_args("--sign-key user-id");
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;
diff --git a/g10/keyedit.h b/g10/keyedit.h
index 89c408b63..b6e5b58b9 100644
--- a/g10/keyedit.h
+++ b/g10/keyedit.h
@@ -48,6 +48,8 @@ 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_revsig (ctrl_t ctrl, const char *username,
+ const char *sigtorev, strlist_t affected_uids);
void keyedit_quick_set_expire (ctrl_t ctrl,
const char *fpr, const char *expirestr,
char **subkeyfprs);
diff --git a/g10/keylist.c b/g10/keylist.c
index cb3b61e03..e222259ac 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -940,7 +940,7 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
/* Order two signatures. We first order by keyid and then by creation
* time. */
-static int
+int
cmp_signodes (const void *av, const void *bv)
{
const kbnode_t an = *(const kbnode_t *)av;
diff --git a/g10/main.h b/g10/main.h
index 7b5754648..7f3752aac 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -457,8 +457,9 @@ int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr);
int revocation_reason_build_cb( PKT_signature *sig, void *opaque );
struct revocation_reason_info *
ask_revocation_reason( int key_rev, int cert_rev, int hint );
-struct revocation_reason_info * get_default_uid_revocation_reason(void);
-void release_revocation_reason_info( struct revocation_reason_info *reason );
+struct revocation_reason_info * get_default_uid_revocation_reason (void);
+struct revocation_reason_info * get_default_sig_revocation_reason (void);
+void release_revocation_reason_info (struct revocation_reason_info *reason);
/*-- keylist.c --*/
void public_key_list (ctrl_t ctrl, strlist_t list,
@@ -468,6 +469,7 @@ void print_subpackets_colon(PKT_signature *sig);
void reorder_keyblock (KBNODE keyblock);
void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret,
int has_secret, int fpr, int no_validity);
+int cmp_signodes (const void *av, const void *bv);
void print_fingerprint (ctrl_t ctrl, estream_t fp,
PKT_public_key *pk, int mode);
void print_revokers (estream_t fp, PKT_public_key *pk);
diff --git a/g10/revoke.c b/g10/revoke.c
index 5262c17bf..c0a003b6f 100644
--- a/g10/revoke.c
+++ b/g10/revoke.c
@@ -889,6 +889,16 @@ get_default_uid_revocation_reason(void)
return reason;
}
+struct revocation_reason_info *
+get_default_sig_revocation_reason(void)
+{
+ struct revocation_reason_info *reason;
+ reason = xmalloc( sizeof *reason );
+ reason->code = 0; /* No specific reason given. */
+ reason->desc = strdup(""); /* no text */
+ return reason;
+}
+
void
release_revocation_reason_info( struct revocation_reason_info *reason )
{