gpg: Allow deletion of subkeys with --delete-[secret-]key.

* common/userids.c (classify_user_id): Do not set the EXACT flag in
the default case.
* g10/export.c (exact_subkey_match_p): Make static,
* g10/delkey.c (do_delete_key): Implement subkey only deleting.
--

GnuPG-bug-id: 4457
This commit is contained in:
Werner Koch 2019-05-27 10:40:38 +02:00
parent 9ccdd59e4e
commit d9b31d3a20
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 113 additions and 13 deletions

View File

@ -351,8 +351,10 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
} }
else if (!hexprefix) else if (!hexprefix)
{ {
/* The fingerprint in an X.509 listing is often delimited by /* The fingerprint of an X.509 listing is often delimited by
colons, so we try to single this case out. */ * colons, so we try to single this case out. Note that the
* OpenPGP bang suffix is not supported here. */
desc->exact = 0;
mode = 0; mode = 0;
hexlength = strspn (s, ":0123456789abcdefABCDEF"); hexlength = strspn (s, ":0123456789abcdefABCDEF");
if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
@ -414,7 +416,6 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
} }
if (!mode) /* Default to substring search. */ if (!mode) /* Default to substring search. */
{ {
desc->exact = 0;
desc->u.name = s; desc->u.name = s;
mode = KEYDB_SEARCH_MODE_SUBSTR; mode = KEYDB_SEARCH_MODE_SUBSTR;
} }

View File

@ -404,7 +404,10 @@ functionality is also available as the subcommand "passwd" with the
@opindex delete-keys @opindex delete-keys
Remove key from the public keyring. In batch mode either @option{--yes} is Remove key from the public keyring. In batch mode either @option{--yes} is
required or the key must be specified by fingerprint. This is a required or the key must be specified by fingerprint. This is a
safeguard against accidental deletion of multiple keys. safeguard against accidental deletion of multiple keys. If the
exclamation mark syntax is used with the fingerprint of a subkey only
that subkey is deleted; if the exclamation mark is used with the
fingerprint of the primary key the entire public key is deleted.
@item --delete-secret-keys @var{name} @item --delete-secret-keys @var{name}
@opindex delete-secret-keys @opindex delete-secret-keys
@ -413,7 +416,10 @@ specified by fingerprint. The option @option{--yes} can be used to
advice gpg-agent not to request a confirmation. This extra advice gpg-agent not to request a confirmation. This extra
pre-caution is done because @command{@gpgname} can't be sure that the pre-caution is done because @command{@gpgname} can't be sure that the
secret key (as controlled by gpg-agent) is only used for the given secret key (as controlled by gpg-agent) is only used for the given
OpenPGP public key. OpenPGP public key. If the exclamation mark syntax is used with the
fingerprint of a subkey only the secret part of that subkey is
deleted; if the exclamation mark is used with the fingerprint of the
primary key only the secret part of the primary key is deleted.
@item --delete-secret-and-public-key @var{name} @item --delete-secret-and-public-key @var{name}

View File

@ -1,7 +1,7 @@
/* delkey.c - delete keys /* delkey.c - delete keys
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004,
* 2005, 2006 Free Software Foundation, Inc. * 2005, 2006 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch * Copyright (C) 2014, 2019 Werner Koch
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -53,13 +53,15 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
gpg_error_t err; gpg_error_t err;
kbnode_t keyblock = NULL; kbnode_t keyblock = NULL;
kbnode_t node, kbctx; kbnode_t node, kbctx;
kbnode_t targetnode;
KEYDB_HANDLE hd; KEYDB_HANDLE hd;
PKT_public_key *pk = NULL; PKT_public_key *pk = NULL;
u32 keyid[2]; u32 keyid[2];
int okay=0; int okay=0;
int yes; int yes;
KEYDB_SEARCH_DESC desc; KEYDB_SEARCH_DESC desc;
int exactmatch; int exactmatch; /* True if key was found by fingerprint. */
int thiskeyonly; /* 0 = false, 1 = is primary key, 2 = is a subkey. */
*r_sec_avail = 0; *r_sec_avail = 0;
@ -72,6 +74,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR
|| desc.mode == KEYDB_SEARCH_MODE_FPR16 || desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20); || desc.mode == KEYDB_SEARCH_MODE_FPR20);
thiskeyonly = desc.exact;
if (!err) if (!err)
err = keydb_search (hd, &desc, 1, NULL); err = keydb_search (hd, &desc, 1, NULL);
if (err) if (err)
@ -97,7 +100,35 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
goto leave; goto leave;
} }
pk = node->pkt->pkt.public_key;
/* If an operation only on a subkey is requested, find that subkey
* now. */
if (thiskeyonly)
{
kbnode_t tmpnode;
for (kbctx=NULL; (tmpnode = walk_kbnode (keyblock, &kbctx, 0)); )
{
if (!(tmpnode->pkt->pkttype == PKT_PUBLIC_KEY
|| tmpnode->pkt->pkttype == PKT_PUBLIC_SUBKEY))
continue;
if (exact_subkey_match_p (&desc, tmpnode))
break;
}
if (!tmpnode)
{
log_error ("Oops; requested subkey not found anymore!\n");
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
/* Set NODE to this specific subkey or primary key. */
thiskeyonly = node == tmpnode? 1 : 2;
targetnode = tmpnode;
}
else
targetnode = node;
pk = targetnode->pkt->pkt.public_key;
keyid_from_pk (pk, keyid); keyid_from_pk (pk, keyid);
if (!secret && !force) if (!secret && !force)
@ -143,6 +174,32 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
print_pubkey_info (ctrl, NULL, pk ); print_pubkey_info (ctrl, NULL, pk );
tty_printf( "\n" ); tty_printf( "\n" );
if (thiskeyonly == 1 && !secret)
{
/* We need to delete the entire public key despite the use
* of the thiskeyonly request. */
tty_printf (_("Note: The public primary key and all its subkeys"
" will be deleted.\n"));
}
else if (thiskeyonly == 2 && !secret)
{
tty_printf (_("Note: Only the shown public subkey"
" will be deleted.\n"));
}
if (thiskeyonly == 1 && secret)
{
tty_printf (_("Note: Only the secret part of the shown primary"
" key will be deleted.\n"));
}
else if (thiskeyonly == 2 && secret)
{
tty_printf (_("Note: Only the secret part of the shown subkey"
" will be deleted.\n"));
}
if (thiskeyonly)
tty_printf ("\n");
yes = cpr_get_answer_is_yes yes = cpr_get_answer_is_yes
(secret? "delete_key.secret.okay": "delete_key.okay", (secret? "delete_key.secret.okay": "delete_key.okay",
_("Delete this key from the keyring? (y/N) ")); _("Delete this key from the keyring? (y/N) "));
@ -178,6 +235,9 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) || node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
continue; continue;
if (thiskeyonly && targetnode != node)
continue;
if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key))
continue; /* No secret key for that public (sub)key. */ continue; /* No secret key for that public (sub)key. */
@ -219,9 +279,38 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
if (firsterr) if (firsterr)
goto leave; goto leave;
} }
else if (thiskeyonly == 2)
{
int selected = 0;
/* Delete the specified public subkey. */
for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
{
if (thiskeyonly && targetnode != node)
continue;
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
selected = targetnode == node;
if (selected)
delete_kbnode (node);
}
else if (selected && node->pkt->pkttype == PKT_SIGNATURE)
delete_kbnode (node);
else
selected = 0;
}
commit_kbnode (&keyblock);
err = keydb_update_keyblock (ctrl, hd, keyblock);
if (err)
{
log_error (_("update failed: %s\n"), gpg_strerror (err));
goto leave;
}
}
else else
{ {
err = opt.dry_run? 0 : keydb_delete_keyblock (hd); err = keydb_delete_keyblock (hd);
if (err) if (err)
{ {
log_error (_("deleting keyblock failed: %s\n"), log_error (_("deleting keyblock failed: %s\n"),
@ -234,7 +323,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
revalidation_mark(). This makes sense - only deleting keys revalidation_mark(). This makes sense - only deleting keys
that have ownertrust set should trigger this. */ that have ownertrust set should trigger this. */
if (!secret && pk && !opt.dry_run && clear_ownertrusts (ctrl, pk)) if (!secret && pk && !opt.dry_run && thiskeyonly != 2
&& clear_ownertrusts (ctrl, pk))
{ {
if (opt.verbose) if (opt.verbose)
log_info (_("ownertrust information cleared\n")); log_info (_("ownertrust information cleared\n"));
@ -247,7 +337,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force,
return err; return err;
} }
/****************
/*
* Delete a public or secret key from a keyring. * Delete a public or secret key from a keyring.
*/ */
gpg_error_t gpg_error_t

View File

@ -428,8 +428,8 @@ new_subkey_list_item (KBNODE node)
(keyID or fingerprint) and does match the one at NODE. It is (keyID or fingerprint) and does match the one at NODE. It is
assumed that the packet at NODE is either a public or secret assumed that the packet at NODE is either a public or secret
subkey. */ subkey. */
static int int
exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node)
{ {
u32 kid[2]; u32 kid[2];
byte fpr[MAX_FINGERPRINT_LEN]; byte fpr[MAX_FINGERPRINT_LEN];

View File

@ -394,6 +394,8 @@ void export_print_stats (export_stats_t stats);
int parse_export_options(char *str,unsigned int *options,int noisy); int parse_export_options(char *str,unsigned int *options,int noisy);
gpg_error_t parse_and_set_export_filter (const char *string); gpg_error_t parse_and_set_export_filter (const char *string);
int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node);
int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
export_stats_t stats); export_stats_t stats);
int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,