gpg: Re-enable secret key deletion.
authorWerner Koch <wk@gnupg.org>
Tue, 15 Apr 2014 14:40:48 +0000 (16:40 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 15 Apr 2014 17:44:50 +0000 (19:44 +0200)
* g10/call-agent.c (agent_delete_key): New.
* g10/keydb.h (FORMAT_KEYDESC_DELKEY): New.
* g10/passphrase.c (gpg_format_keydesc): Support new format.
* g10/delkey.c (do_delete_key): Add secret key deletion.

doc/DETAILS
g10/call-agent.c
g10/call-agent.h
g10/delkey.c
g10/getkey.c
g10/keydb.h
g10/passphrase.c

index 3f9e747..03c200e 100644 (file)
@@ -818,6 +818,7 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
     - 1 :: No such key
     - 2 :: Must delete secret key first
     - 3 :: Ambigious specification
+    - 4 :: Key is stored on a smartcard.
 
 *** PROGRESS <what> <char> <cur> <total>
     Used by the primegen and Public key functions to indicate
index 4ce6a06..5ad0983 100644 (file)
@@ -2127,6 +2127,44 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
 
 
 \f
+/* Ask the agent to delete the key identified by HEXKEYGRIP.  If DESC
+   is not NULL, display DESC instead of the default description
+   message.  */
+gpg_error_t
+agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
+{
+  gpg_error_t err;
+  char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s dfltparm;
+
+  memset (&dfltparm, 0, sizeof dfltparm);
+  dfltparm.ctrl = ctrl;
+
+  err = start_agent (ctrl, 0);
+  if (err)
+    return err;
+
+  if (!hexkeygrip || strlen (hexkeygrip) != 40)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (desc)
+    {
+      snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
+      err = assuan_transact (agent_ctx, line,
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+      if (err)
+        return err;
+    }
+
+  snprintf (line, DIM(line)-1, "DELETE_KEY %s", hexkeygrip);
+  err = assuan_transact (agent_ctx, line, NULL, NULL,
+                         default_inq_cb, &dfltparm,
+                         NULL, NULL);
+  return err;
+}
+
+
+\f
 /* Ask the agent to change the passphrase of the key identified by
    HEXKEYGRIP.  If DESC is not NULL, display DESC instead of the
    default description message.  If CACHE_NONCE_ADDR is not NULL the
index 2b944d2..1deb854 100644 (file)
@@ -185,6 +185,10 @@ gpg_error_t agent_export_key (ctrl_t ctrl, const char *keygrip,
                               const char *desc, char **cache_nonce_addr,
                               unsigned char **r_result, size_t *r_resultlen);
 
+/* Delete a key from the agent.  */
+gpg_error_t agent_delete_key (ctrl_t ctrl, const char *hexkeygrip,
+                              const char *desc);
+
 /* Change the passphrase of a key.  */
 gpg_error_t agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
                           char **cache_nonce_addr, char **passwd_nonce_addr);
index 2e4477b..3de705d 100644 (file)
@@ -40,6 +40,7 @@
 #include "ttyio.h"
 #include "status.h"
 #include "i18n.h"
+#include "call-agent.h"
 
 
 /****************
@@ -52,7 +53,7 @@ do_delete_key( const char *username, int secret, int force, int *r_sec_avail )
 {
   gpg_error_t err;
   kbnode_t keyblock = NULL;
-  kbnode_t node;
+  kbnode_t node, kbctx;
   KEYDB_HANDLE hd;
   PKT_public_key *pk = NULL;
   u32 keyid[2];
@@ -156,9 +157,47 @@ do_delete_key( const char *username, int secret, int force, int *r_sec_avail )
     {
       if (secret)
        {
-         log_error (_("deleting secret key not implemented\n"));
-         err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
-         goto leave;
+          char *prompt;
+          gpg_error_t firsterr = 0;
+          char *hexgrip;
+
+          setup_main_keyids (keyblock);
+          for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
+            {
+              if (!(node->pkt->pkttype == PKT_PUBLIC_KEY
+                    || node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
+                continue;
+
+              if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key))
+                continue;  /* No secret key for that public (sub)key.  */
+
+              prompt = gpg_format_keydesc (node->pkt->pkt.public_key,
+                                           FORMAT_KEYDESC_DELKEY, 1);
+              err = hexkeygrip_from_pk (node->pkt->pkt.public_key, &hexgrip);
+              if (!err)
+                err = agent_delete_key (NULL, hexgrip, prompt);
+              xfree (prompt);
+              xfree (hexgrip);
+              if (err)
+                {
+                  if (gpg_err_code (err) == GPG_ERR_KEY_ON_CARD)
+                    write_status_text (STATUS_DELETE_PROBLEM, "1");
+                  log_error (_("deleting secret %s failed: %s\n"),
+                             (node->pkt->pkttype == PKT_PUBLIC_KEY
+                              ? _("key"):_("subkey")),
+                             gpg_strerror (err));
+                  if (!firsterr)
+                    firsterr = err;
+                  if (gpg_err_code (err) == GPG_ERR_CANCELED
+                      || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
+                    break;
+                }
+
+            }
+
+          err = firsterr;
+          if (firsterr)
+            goto leave;
        }
       else
        {
index 777f383..458672a 100644 (file)
@@ -1237,6 +1237,38 @@ getkey_end (getkey_ctx_t ctx)
  ************* Merging stuff ********************
  ************************************************/
 
+/* Set the mainkey_id fields for all keys in KEYBLOCK.  This is
+   usually done by merge_selfsigs but at some places we only need the
+   main_kid but the the full merging.  The function also guarantees
+   that all pk->keyids are computed. */
+void
+setup_main_keyids (kbnode_t keyblock)
+{
+  u32 kid[2], mainkid[2];
+  kbnode_t kbctx, node;
+  PKT_public_key *pk;
+
+  if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY)
+    BUG ();
+  pk = keyblock->pkt->pkt.public_key;
+
+  keyid_from_pk (pk, mainkid);
+  for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
+    {
+      if (!(node->pkt->pkttype == PKT_PUBLIC_KEY
+            || node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
+        continue;
+      pk = node->pkt->pkt.public_key;
+      keyid_from_pk (pk, kid); /* Make sure pk->keyid is set.  */
+      if (!pk->main_keyid[0] && !pk->main_keyid[1])
+        {
+          pk->main_keyid[0] = mainkid[0];
+          pk->main_keyid[1] = mainkid[1];
+        }
+    }
+}
+
+
 /* Merge all self-signatures with the keys.  */
 void
 merge_keys_and_selfsig (KBNODE keyblock)
index 492cde3..b21d955 100644 (file)
@@ -201,7 +201,7 @@ void emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo);
 #define FORMAT_KEYDESC_NORMAL  0
 #define FORMAT_KEYDESC_IMPORT  1
 #define FORMAT_KEYDESC_EXPORT  2
-
+#define FORMAT_KEYDESC_DELKEY  3
 char *gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped);
 
 
@@ -248,6 +248,7 @@ void getkey_end (getkey_ctx_t ctx);
 
 gpg_error_t enum_secret_keys (void **context, PKT_public_key *pk);
 
+void setup_main_keyids (kbnode_t keyblock);
 void merge_keys_and_selfsig( KBNODE keyblock );
 char*get_user_id_string( u32 *keyid );
 char*get_user_id_string_native( u32 *keyid );
index 7c1d6aa..280d8a9 100644 (file)
@@ -702,7 +702,7 @@ gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped)
                        (int)uidlen, uid,
                        nbits_from_pk (pk), algo_name,
                        keystr (pk->keyid), timestr,
-                       maink?maink:"", trailer );
+                       maink?maink:"", trailer);
   xfree (maink);
   xfree (uid);