agent: Add command DELETE_KEY.
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 15:03:54 +0000 (17:03 +0200)
* agent/command.c (cmd_delete_key): New.
* agent/findkey.c (modify_description): Add '%C' feature.
(remove_key_file): New.
(agent_delete_key): New.
* agent/command-ssh.c (search_control_file): Make arg R_DISABLE
optional.

* configure.ac: Require libgpg-error 1.13.

agent/agent.h
agent/command-ssh.c
agent/command.c
agent/findkey.c
configure.ac

index 58e5841..4ed8c7f 100644 (file)
@@ -334,6 +334,8 @@ int agent_key_available (const unsigned char *grip);
 gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
                                       int *r_keytype,
                                       unsigned char **r_shadow_info);
+gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text,
+                              const unsigned char *grip);
 
 /*-- call-pinentry.c --*/
 void initialize_module_call_pinentry (void);
index 364a8cc..a814681 100644 (file)
@@ -1031,7 +1031,8 @@ search_control_file (ssh_control_file_t cf, const char *hexgrip,
 
   assert (strlen (hexgrip) == 40 );
 
-  *r_disabled = 0;
+  if (r_disabled)
+    *r_disabled = 0;
   if (r_ttl)
     *r_ttl = 0;
   if (r_confirm)
@@ -1047,7 +1048,8 @@ search_control_file (ssh_control_file_t cf, const char *hexgrip,
     }
   if (!err)
     {
-      *r_disabled = cf->item.disabled;
+      if (r_disabled)
+        *r_disabled = cf->item.disabled;
       if (r_ttl)
         *r_ttl = cf->item.ttl;
       if (r_confirm)
index fab27f0..52876a9 100644 (file)
@@ -695,7 +695,7 @@ static const char hlp_setkeydesc[] =
   "blanks unless they are percent or '+' escaped.\n"
   "\n"
   "The description is only valid for the next PKSIGN, PKDECRYPT,\n"
-  "IMPORT_KEY or EXPORT_KEY operation.";
+  "IMPORT_KEY, EXPORT_KEY, or DELETE_KEY operation.";
 static gpg_error_t
 cmd_setkeydesc (assuan_context_t ctx, char *line)
 {
@@ -2244,6 +2244,39 @@ cmd_export_key (assuan_context_t ctx, char *line)
 
   return leave_cmd (ctx, err);
 }
+
+
+\f
+static const char hlp_delete_key[] =
+  "DELETE_KEY <hexstring_with_keygrip>\n"
+  "\n"
+  "Delete a secret key from the key store.\n"
+  "As safeguard the agent asks the user for confirmation.\n";
+static gpg_error_t
+cmd_delete_key (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  unsigned char grip[20];
+
+  line = skip_options (line);
+
+  err = parse_keygrip (ctx, line, grip);
+  if (err)
+    goto leave;
+
+  err = agent_delete_key (ctrl, ctrl->server_local->keydesc, grip);
+  if (err)
+    goto leave;
+
+ leave:
+  xfree (ctrl->server_local->keydesc);
+  ctrl->server_local->keydesc = NULL;
+
+  return leave_cmd (ctx, err);
+}
+
+
 \f
 static const char hlp_keytocard[] =
   "KEYTOCARD [--force] <hexstring_with_keygrip> <serialno> <id> <timestamp>\n"
@@ -2926,6 +2959,7 @@ register_commands (assuan_context_t ctx)
     { "KEYWRAP_KEY",    cmd_keywrap_key, hlp_keywrap_key },
     { "IMPORT_KEY",     cmd_import_key, hlp_import_key },
     { "EXPORT_KEY",     cmd_export_key, hlp_export_key },
+    { "DELETE_KEY",     cmd_delete_key, hlp_delete_key },
     { "GETVAL",         cmd_getval,    hlp_getval },
     { "PUTVAL",         cmd_putval,    hlp_putval },
     { "UPDATESTARTUPTTY",  cmd_updatestartuptty, hlp_updatestartuptty },
index 84d2cfd..e01c5c1 100644 (file)
@@ -1,6 +1,7 @@
 /* findkey.c - Locate the secret key
  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007,
  *               2010, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -189,6 +190,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
 
    %% - Replaced by a single %
    %c - Replaced by the content of COMMENT.
+   %C - Same as %c but put into parentheses.
    %F - Replaced by an ssh style fingerprint computed from KEY.
 
    The functions returns 0 on success or an error code.  On success a
@@ -240,6 +242,20 @@ modify_description (const char *in, const char *comment, const gcry_sexp_t key,
                     out_len += comment_length;
                   break;
 
+                case 'C': /* Comment.  */
+                  if (!comment_length)
+                    ;
+                  else if (out)
+                    {
+                      *out++ = '(';
+                      memcpy (out, comment, comment_length);
+                      out += comment_length;
+                      *out++ = ')';
+                    }
+                  else
+                    out_len += comment_length + 2;
+                  break;
+
                 case 'F': /* SSH style fingerprint.  */
                   if (!ssh_fpr && key)
                     ssh_get_fingerprint_string (key, &ssh_fpr);
@@ -536,6 +552,24 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
 }
 
 
+/* Remove the key identified by GRIP from the private key directory.  */
+static gpg_error_t
+remove_key_file (const unsigned char *grip)
+{
+  gpg_error_t err = 0;
+  char *fname;
+  char hexgrip[40+4+1];
+
+  bin2hex (grip, 20, hexgrip);
+  strcpy (hexgrip+40, ".key");
+  fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
+  if (gnupg_remove (fname))
+    err = gpg_error_from_syserror ();
+  xfree (fname);
+  return err;
+}
+
+
 /* Return the secret key as an S-Exp in RESULT after locating it using
    the GRIP.  If the operation shall be diverted to a token, an
    allocated S-expression with the shadow_info part from the file is
@@ -1145,3 +1179,112 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
   xfree (buf);
   return err;
 }
+
+
+\f
+/* Delete the key with GRIP from the disk after having asked for
+   confirmation using DESC_TEXT.  Common error codes are:
+     GPG_ERR_NO_SECKEY
+     GPG_ERR_KEY_ON_CARD
+     GPG_ERR_NOT_CONFIRMED
+*/
+gpg_error_t
+agent_delete_key (ctrl_t ctrl, const char *desc_text,
+                  const unsigned char *grip)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_skey = NULL;
+  unsigned char *buf = NULL;
+  size_t len;
+  char *desc_text_final = NULL;
+  char *comment = NULL;
+  ssh_control_file_t cf = NULL;
+  char hexgrip[40+4+1];
+  char *default_desc = NULL;
+
+  err = read_key_file (grip, &s_skey);
+  if (gpg_err_code (err) == GPG_ERR_ENOENT)
+    err = gpg_error (GPG_ERR_NO_SECKEY);
+  if (err)
+    goto leave;
+
+  err = make_canon_sexp (s_skey, &buf, &len);
+  if (err)
+    goto leave;
+
+  switch (agent_private_key_type (buf))
+    {
+    case PRIVATE_KEY_CLEAR:
+    case PRIVATE_KEY_PROTECTED:
+      {
+        bin2hex (grip, 20, hexgrip);
+        if (!desc_text)
+          {
+            default_desc = xtryasprintf
+              ("Do you really want to delete the key identified by keygrip%%0A"
+               "  %s%%0A  %%C%%0A?", hexgrip);
+            desc_text = default_desc;
+          }
+
+        /* Note, that we will take the comment as a C string for
+           display purposes; i.e. all stuff beyond a Nul character is
+           ignored.  */
+        {
+          gcry_sexp_t comment_sexp;
+
+          comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
+          if (comment_sexp)
+            comment = gcry_sexp_nth_string (comment_sexp, 1);
+          gcry_sexp_release (comment_sexp);
+        }
+
+       if (desc_text)
+          err = modify_description (desc_text, comment? comment:"", s_skey,
+                                    &desc_text_final);
+       if (err)
+          goto leave;
+
+        err = agent_get_confirmation (ctrl, desc_text_final,
+                                      _("Delete key"), _("No"), 0);
+        if (err)
+          goto leave;
+
+        cf = ssh_open_control_file ();
+        if (cf)
+          {
+            if (!ssh_search_control_file (cf, hexgrip, NULL, NULL, NULL))
+              {
+                err = agent_get_confirmation
+                  (ctrl,
+                   _("Warning: This key is also listed for use with SSH!\n"
+                     "Deleting the key will may remove your ability to"
+                     "access remote machines."),
+                   _("Delete key"), _("No"), 0);
+                if (err)
+                  goto leave;
+              }
+          }
+
+        err = remove_key_file (grip);
+      }
+      break;
+
+    case PRIVATE_KEY_SHADOWED:
+      err = gpg_error (GPG_ERR_KEY_ON_CARD);
+      break;
+
+    default:
+      log_error ("invalid private key format\n");
+      err = gpg_error (GPG_ERR_BAD_SECKEY);
+      break;
+    }
+
+ leave:
+  ssh_close_control_file (cf);
+  gcry_free (comment);
+  xfree (desc_text_final);
+  xfree (default_desc);
+  xfree (buf);
+  gcry_sexp_release (s_skey);
+  return err;
+}
index e384fba..096e3e1 100644 (file)
@@ -44,7 +44,7 @@ m4_define([mym4_full_version],[mym4_version[]mym4_betastring])
 
 AC_INIT([gnupg],[mym4_full_version], [http://bugs.gnupg.org])
 
-NEED_GPG_ERROR_VERSION=1.11
+NEED_GPG_ERROR_VERSION=1.13
 
 NEED_LIBGCRYPT_API=1
 NEED_LIBGCRYPT_VERSION=1.6.0