diff options
Diffstat (limited to 'g10/delkey.c')
-rw-r--r-- | g10/delkey.c | 104 |
1 files changed, 97 insertions, 7 deletions
diff --git a/g10/delkey.c b/g10/delkey.c index 6f816080e..b5ab47434 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,7 +1,7 @@ /* delkey.c - delete keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, * 2005, 2006 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 2014, 2019 Werner Koch * * 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; kbnode_t keyblock = NULL; kbnode_t node, kbctx; + kbnode_t targetnode; KEYDB_HANDLE hd; PKT_public_key *pk = NULL; u32 keyid[2]; int okay=0; int yes; 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; @@ -70,6 +72,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, /* Search the userid. */ err = classify_user_id (username, &desc, 1); exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR); + thiskeyonly = desc.exact; if (!err) err = keydb_search (hd, &desc, 1, NULL); if (err) @@ -95,7 +98,35 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, err = gpg_error (GPG_ERR_GENERAL); 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); if (!secret && !force) @@ -136,7 +167,32 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, else { print_key_info (ctrl, NULL, 0, pk, secret); - 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 (secret? "delete_key.secret.okay": "delete_key.okay", @@ -173,6 +229,9 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; + if (thiskeyonly && targetnode != node) + continue; + if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) continue; /* No secret key for that public (sub)key. */ @@ -214,9 +273,38 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, if (firsterr) 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 { - err = opt.dry_run? 0 : keydb_delete_keyblock (hd); + err = keydb_delete_keyblock (hd); if (err) { log_error (_("deleting keyblock failed: %s\n"), @@ -229,7 +317,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, revalidation_mark(). This makes sense - only deleting keys 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) log_info (_("ownertrust information cleared\n")); @@ -242,7 +331,8 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, return err; } -/**************** + +/* * Delete a public or secret key from a keyring. */ gpg_error_t |