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
This commit is contained in:
Werner Koch 2020-10-28 17:06:27 +01:00
parent 742e2729f4
commit 243f9176e7
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 288 additions and 9 deletions

View File

@ -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

View File

@ -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");

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 )
{