Fix typos in messages
[gnupg.git] / agent / findkey.c
index 6464b02..b842f9e 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,10 +552,28 @@ 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.  Stores NULL at RESULT if the operation shall be diverted
-   to a token; in this case an allocated S-expression with the
-   shadow_info part from the file is stored at SHADOW_INFO.
+   the GRIP.  If the operation shall be diverted to a token, an
+   allocated S-expression with the shadow_info part from the file is
+   stored at SHADOW_INFO; if not NULL will be stored at SHADOW_INFO.
    CACHE_MODE defines now the cache shall be used.  DESC_TEXT may be
    set to present a custom description for the pinentry.  LOOKUP_TTL
    is an optional function to convey a TTL to the cache manager; we do
@@ -562,7 +596,6 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
   unsigned char *buf;
   size_t len, buflen, erroff;
   gcry_sexp_t s_skey;
-  int got_shadow_info = 0;
 
   *result = NULL;
   if (shadow_info)
@@ -638,7 +671,6 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
                 {
                   memcpy (*shadow_info, s, n);
                   rc = 0;
-                  got_shadow_info = 1;
                 }
             }
           if (rc)
@@ -654,7 +686,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
     }
   gcry_sexp_release (s_skey);
   s_skey = NULL;
-  if (rc || got_shadow_info)
+  if (rc)
     {
       xfree (buf);
       if (r_passphrase)
@@ -690,7 +722,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
    string describing the names of the parameters.  ALGONAMESIZE and
    ELEMSSIZE give the allocated size of the provided buffers.  The
    buffers may be NULL if not required.  If R_LIST is not NULL the top
-   level list will be stored tehre; the caller needs to release it in
+   level list will be stored there; the caller needs to release it in
    this case.  */
 static gpg_error_t
 key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
@@ -778,27 +810,65 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
 }
 
 
+/* Return true if KEYPARMS holds an EdDSA key.  */
+static int
+is_eddsa (gcry_sexp_t keyparms)
+{
+  int result = 0;
+  gcry_sexp_t list;
+  const char *s;
+  size_t n;
+  int i;
+
+  list = gcry_sexp_find_token (keyparms, "flags", 0);
+  for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
+    {
+      s = gcry_sexp_nth_data (list, i, &n);
+      if (!s)
+        continue; /* Not a data element. */
+
+      if (n == 5 && !memcmp (s, "eddsa", 5))
+        {
+          result = 1;
+          break;
+        }
+    }
+  gcry_sexp_release (list);
+  return result;
+}
+
+
 /* Return the public key algorithm number if S_KEY is a DSA style key.
    If it is not a DSA style key, return 0.  */
 int
 agent_is_dsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an DSA key.  */
 
   if (!strcmp (algoname, "dsa"))
-    return GCRY_PK_DSA;
+    result = GCRY_PK_DSA;
   else if (!strcmp (algoname, "ecc"))
-    return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag.  */
+    {
+      if (is_eddsa (list))
+        result = 0;
+      else
+        result = GCRY_PK_ECDSA;
+    }
   else if (!strcmp (algoname, "ecdsa"))
-    return GCRY_PK_ECDSA;
+    result = GCRY_PK_ECDSA;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
@@ -806,18 +876,25 @@ agent_is_dsa_key (gcry_sexp_t s_key)
 int
 agent_is_eddsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an EdDSA key.  */
 
-  if (!strcmp (algoname, "eddsa"))
-    return 1;
+  if (!strcmp (algoname, "ecc") && is_eddsa (list))
+    result = 1;
+  else if (!strcmp (algoname, "eddsa")) /* backward compatibility.  */
+    result = 1;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
@@ -1102,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;
+}