Add missing variable.
[gnupg.git] / agent / findkey.c
index 9405342..a03d0c4 100644 (file)
@@ -1,6 +1,6 @@
 /* findkey.c - Locate the secret key
- * Copyright (C) 2001, 2002, 2003, 2004, 2005,
- *               2007  Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007,
+ *               2010, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <unistd.h>
 #include <sys/stat.h>
 #include <assert.h>
-#include <pth.h> /* (we use pth_sleep) */
+#include <npth.h> /* (we use pth_sleep) */
 
 #include "agent.h"
 #include "i18n.h"
+#include "../common/ssh-utils.h"
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
 /* Helper to pass data to the check callback of the unprotect function. */
-struct try_unprotect_arg_s 
+struct try_unprotect_arg_s
 {
   ctrl_t ctrl;
   const unsigned char *protected_key;
@@ -57,10 +58,9 @@ agent_write_private_key (const unsigned char *grip,
                          const void *buffer, size_t length, int force)
 {
   char *fname;
-  FILE *fp;
+  estream_t fp;
   char hexgrip[40+4+1];
-  int fd;
-  
+
   bin2hex (grip, 20, hexgrip);
   strcpy (hexgrip+40, ".key");
 
@@ -70,56 +70,32 @@ agent_write_private_key (const unsigned char *grip,
     {
       log_error ("secret key file `%s' already exists\n", fname);
       xfree (fname);
-      return gpg_error (GPG_ERR_GENERAL);
-    }
-
-  /* In FORCE mode we would like to create FNAME but only if it does
-     not already exist.  We cannot make this guarantee just using
-     POSIX (GNU provides the "x" opentype for fopen, however, this is
-     not portable).  Thus, we use the more flexible open function and
-     then use fdopen to obtain a stream. */
-  fd = open (fname, force? (O_CREAT | O_TRUNC | O_WRONLY | O_BINARY)
-                         : (O_CREAT | O_EXCL | O_WRONLY | O_BINARY),
-             S_IRUSR | S_IWUSR 
-#ifndef HAVE_W32_SYSTEM
-                 | S_IRGRP 
-#endif
-                 );
-  if (fd < 0)
-    fp = NULL;
-  else
-    {
-      fp = fdopen (fd, "wb");
-      if (!fp)
-        { 
-          int save_e = errno;
-          close (fd);
-          errno = save_e;
-        }
+      return gpg_error (GPG_ERR_EEXIST);
     }
 
-  if (!fp) 
-    { 
-      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
-      log_error ("can't create `%s': %s\n", fname, strerror (errno));
+  fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw");
+  if (!fp)
+    {
+      gpg_error_t tmperr = gpg_error_from_syserror ();
+      log_error ("can't create `%s': %s\n", fname, gpg_strerror (tmperr));
       xfree (fname);
       return tmperr;
     }
 
-  if (fwrite (buffer, length, 1, fp) != 1)
+  if (es_fwrite (buffer, length, 1, fp) != 1)
     {
-      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
-      log_error ("error writing `%s': %s\n", fname, strerror (errno));
-      fclose (fp);
-      remove (fname);
+      gpg_error_t tmperr = gpg_error_from_syserror ();
+      log_error ("error writing `%s': %s\n", fname, gpg_strerror (tmperr));
+      es_fclose (fp);
+      gnupg_remove (fname);
       xfree (fname);
       return tmperr;
     }
-  if ( fclose (fp) )
+  if (es_fclose (fp))
     {
-      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
-      log_error ("error closing `%s': %s\n", fname, strerror (errno));
-      remove (fname);
+      gpg_error_t tmperr = gpg_error_from_syserror ();
+      log_error ("error closing `%s': %s\n", fname, gpg_strerror (tmperr));
+      gnupg_remove (fname);
       xfree (fname);
       return tmperr;
     }
@@ -129,7 +105,7 @@ agent_write_private_key (const unsigned char *grip,
 }
 
 
-/* Callback function to try the unprotection from the passpharse query
+/* Callback function to try the unprotection from the passphrase query
    code. */
 static int
 try_unprotect_cb (struct pin_entry_info_s *pi)
@@ -168,7 +144,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
       if (strcmp (now, tmptime) > 0 )
         {
           /* Passphrase "expired".  */
-          desc = xtryasprintf 
+          desc = xtryasprintf
             (_("This passphrase has not been changed%%0A"
                "since %.4s-%.2s-%.2s.  Please change it now."),
              protected_at, protected_at+4, protected_at+6);
@@ -194,7 +170,8 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
                                         _("I'll change it later"), 0);
           if (!err)
             arg->change_required = 1;
-          else if (gpg_err_code (err) == GPG_ERR_CANCELED)
+          else if (gpg_err_code (err) == GPG_ERR_CANCELED
+                   || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
             err = 0;
         }
       xfree (desc);
@@ -209,12 +186,14 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
 
    %% - Replaced by a single %
    %c - Replaced by the content of COMMENT.
+   %F - Replaced by an ssh style fingerprint computed from KEY.
 
    The functions returns 0 on success or an error code.  On success a
    newly allocated string is stored at the address of RESULT.
  */
 static gpg_error_t
-modify_description (const char *in, const char *comment, char **result)
+modify_description (const char *in, const char *comment, const gcry_sexp_t key,
+                    char **result)
 {
   size_t comment_length;
   size_t in_len;
@@ -222,6 +201,7 @@ modify_description (const char *in, const char *comment, char **result)
   char *out;
   size_t i;
   int special, pass;
+  char *ssh_fpr = NULL;
 
   comment_length = strlen (comment);
   in_len  = strlen (in);
@@ -257,6 +237,18 @@ modify_description (const char *in, const char *comment, char **result)
                     out_len += comment_length;
                   break;
 
+                case 'F': /* SSH style fingerprint.  */
+                  if (!ssh_fpr && key)
+                    ssh_get_fingerprint_string (key, &ssh_fpr);
+                  if (ssh_fpr)
+                    {
+                      if (out)
+                        out = stpcpy (out, ssh_fpr);
+                      else
+                        out_len += strlen (ssh_fpr);
+                    }
+                  break;
+
                 default: /* Invalid special sequences are kept as they are. */
                   if (out)
                     {
@@ -278,31 +270,40 @@ modify_description (const char *in, const char *comment, char **result)
                 out_len++;
             }
         }
-      
+
       if (!pass)
         {
           *result = out = xtrymalloc (out_len + 1);
           if (!out)
-            return gpg_error_from_syserror ();
+            {
+              xfree (ssh_fpr);
+              return gpg_error_from_syserror ();
+            }
         }
     }
 
   *out = 0;
   assert (*result + out_len == out);
+  xfree (ssh_fpr);
   return 0;
 }
 
-  
+
 
 /* Unprotect the canconical encoded S-expression key in KEYBUF.  GRIP
    should be the hex encoded keygrip of that key to be used with the
    caching mechanism. DESC_TEXT may be set to override the default
    description used for the pinentry.  If LOOKUP_TTL is given this
-   function is used to lookup the default ttl. */
+   function is used to lookup the default ttl.  If R_PASSPHRASE is not
+   NULL, the function succeeded and the key was protected the used
+   passphrase (entered or from the cache) is stored there; if not NULL
+   will be stored.  The caller needs to free the returned
+   passphrase. */
 static int
-unprotect (ctrl_t ctrl, const char *desc_text,
-           unsigned char **keybuf, const unsigned char *grip, 
-           cache_mode_t cache_mode, lookup_ttl_t lookup_ttl)
+unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
+           unsigned char **keybuf, const unsigned char *grip,
+           cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+           char **r_passphrase)
 {
   struct pin_entry_info_s *pi;
   struct try_unprotect_arg_s arg;
@@ -310,28 +311,57 @@ unprotect (ctrl_t ctrl, const char *desc_text,
   unsigned char *result;
   size_t resultlen;
   char hexgrip[40+1];
-  
+
+  if (r_passphrase)
+    *r_passphrase = NULL;
+
   bin2hex (grip, 20, hexgrip);
 
+  /* Initially try to get it using a cache nonce.  */
+  if (cache_nonce)
+    {
+      char *pw;
+
+      pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+      if (pw)
+        {
+          rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
+          if (!rc)
+            {
+              if (r_passphrase)
+                *r_passphrase = pw;
+              else
+                xfree (pw);
+              xfree (*keybuf);
+              *keybuf = result;
+              return 0;
+            }
+          xfree (pw);
+        }
+    }
+
   /* First try to get it from the cache - if there is none or we can't
      unprotect it, we fall back to ask the user */
   if (cache_mode != CACHE_MODE_IGNORE)
     {
-      void *cache_marker;
-      const char *pw;
-      
+      char *pw;
+
     retry:
-      pw = agent_get_cache (hexgrip, cache_mode, &cache_marker);
+      pw = agent_get_cache (hexgrip, cache_mode);
       if (pw)
         {
           rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
-          agent_unlock_cache_entry (&cache_marker);
           if (!rc)
             {
+              if (r_passphrase)
+                *r_passphrase = pw;
+              else
+                xfree (pw);
               xfree (*keybuf);
               *keybuf = result;
               return 0;
             }
+          xfree (pw);
           rc  = 0;
         }
 
@@ -352,7 +382,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
             {
               /* We need to give the other thread a chance to actually put
                  it into the cache. */
-              pth_sleep (1); 
+              npth_sleep (1);
               goto retry;
             }
           /* Timeout - better call pinentry now the plain way. */
@@ -381,7 +411,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
         {
           size_t canlen, erroff;
           gcry_sexp_t s_skey;
-          
+
           assert (arg.unprotected_key);
           canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
           rc = gcry_sexp_sscan (&s_skey, &erroff,
@@ -395,11 +425,11 @@ unprotect (ctrl_t ctrl, const char *desc_text,
               xfree (pi);
               return rc;
             }
-          rc = agent_protect_and_store (ctrl, s_skey);
+          rc = agent_protect_and_store (ctrl, s_skey, NULL);
           gcry_sexp_release (s_skey);
           if (rc)
             {
-              log_error ("changing the passphrase failed: %s\n", 
+              log_error ("changing the passphrase failed: %s\n",
                          gpg_strerror (rc));
               wipememory (arg.unprotected_key, canlen);
               xfree (arg.unprotected_key);
@@ -407,8 +437,13 @@ unprotect (ctrl_t ctrl, const char *desc_text,
               return rc;
             }
         }
-      agent_put_cache (hexgrip, cache_mode, pi->pin, 
-                       lookup_ttl? lookup_ttl (hexgrip) : 0);
+      else
+        {
+          agent_put_cache (hexgrip, cache_mode, pi->pin,
+                           lookup_ttl? lookup_ttl (hexgrip) : 0);
+          if (r_passphrase && *pi->pin)
+            *r_passphrase = xtrystrdup (pi->pin);
+        }
       xfree (*keybuf);
       *keybuf = arg.unprotected_key;
     }
@@ -425,20 +460,20 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
 {
   int rc;
   char *fname;
-  FILE *fp;
+  estream_t fp;
   struct stat st;
   unsigned char *buf;
   size_t buflen, erroff;
   gcry_sexp_t s_skey;
   char hexgrip[40+4+1];
-  
+
   *result = NULL;
 
   bin2hex (grip, 20, hexgrip);
   strcpy (hexgrip+40, ".key");
 
   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
-  fp = fopen (fname, "rb");
+  fp = es_fopen (fname, "rb");
   if (!fp)
     {
       rc = gpg_error_from_syserror ();
@@ -447,24 +482,37 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
       xfree (fname);
       return rc;
     }
-  
-  if (fstat (fileno(fp), &st))
+
+  if (fstat (es_fileno (fp), &st))
     {
       rc = gpg_error_from_syserror ();
       log_error ("can't stat `%s': %s\n", fname, strerror (errno));
       xfree (fname);
-      fclose (fp);
+      es_fclose (fp);
       return rc;
     }
 
   buflen = st.st_size;
   buf = xtrymalloc (buflen+1);
-  if (!buf || fread (buf, buflen, 1, fp) != 1)
+  if (!buf)
     {
       rc = gpg_error_from_syserror ();
-      log_error ("error reading `%s': %s\n", fname, strerror (errno));
+      log_error ("error allocating %zu bytes for `%s': %s\n",
+                 buflen, fname, strerror (errno));
       xfree (fname);
-      fclose (fp);
+      es_fclose (fp);
+      xfree (buf);
+      return rc;
+
+    }
+
+  if (es_fread (buf, buflen, 1, fp) != 1)
+    {
+      rc = gpg_error_from_syserror ();
+      log_error ("error reading %zu bytes from `%s': %s\n",
+                 buflen, fname, strerror (errno));
+      xfree (fname);
+      es_fclose (fp);
       xfree (buf);
       return rc;
     }
@@ -472,7 +520,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
   /* Convert the file into a gcrypt S-expression object.  */
   rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
   xfree (fname);
-  fclose (fp);
+  es_fclose (fp);
   xfree (buf);
   if (rc)
     {
@@ -492,24 +540,32 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
    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
-   not simply pass the TTL value because the value is only needed if an
-   unprotect action was needed and looking up the TTL may have some
-   overhead (e.g. scanning the sshcontrol file). */
+   not simply pass the TTL value because the value is only needed if
+   an unprotect action was needed and looking up the TTL may have some
+   overhead (e.g. scanning the sshcontrol file).  If a CACHE_NONCE is
+   given that cache item is first tried to get a passphrase.  If
+   R_PASSPHRASE is not NULL, the function succeeded and the key was
+   protected the used passphrase (entered or from the cache) is stored
+   there; if not NULL will be stored.  The caller needs to free the
+   returned passphrase.   */
 gpg_error_t
-agent_key_from_file (ctrl_t ctrl, const char *desc_text,
+agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
+                     const char *desc_text,
                      const unsigned char *grip, unsigned char **shadow_info,
                      cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
-                     gcry_sexp_t *result)
+                     gcry_sexp_t *result, char **r_passphrase)
 {
   int rc;
   unsigned char *buf;
   size_t len, buflen, erroff;
   gcry_sexp_t s_skey;
   int got_shadow_info = 0;
-  
+
   *result = NULL;
   if (shadow_info)
     *shadow_info = NULL;
+  if (r_passphrase)
+    *r_passphrase = NULL;
 
   rc = read_key_file (grip, &s_skey);
   if (rc)
@@ -528,56 +584,36 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
       break; /* no unprotection needed */
     case PRIVATE_KEY_PROTECTED:
       {
-       gcry_sexp_t comment_sexp;
-       size_t comment_length;
        char *desc_text_final;
-       const char *comment = NULL;
+       char *comment = NULL;
 
         /* Note, that we will take the comment as a C string for
            display purposes; i.e. all stuff beyond a Nul character is
            ignored.  */
-       comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
-       if (comment_sexp)
-         comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
-       if (!comment)
-         {
-           comment = "";
-           comment_length = 0;
-         }
+        {
+          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);
+        }
 
         desc_text_final = NULL;
        if (desc_text)
-         {
-            if (comment[comment_length])
-              {
-                /* Not a C-string; create one.  We might here allocate
-                   more than actually displayed but well, that
-                   shouldn't be a problem.  */
-                char *tmp = xtrymalloc (comment_length+1);
-                if (!tmp)
-                  rc = gpg_error_from_syserror ();
-                else
-                  {
-                    memcpy (tmp, comment, comment_length);
-                    tmp[comment_length] = 0;
-                    rc = modify_description (desc_text, tmp, &desc_text_final);
-                    xfree (tmp);
-                  }
-              }
-            else
-              rc = modify_description (desc_text, comment, &desc_text_final);
-         }
+          rc = modify_description (desc_text, comment? comment:"", s_skey,
+                                   &desc_text_final);
+        gcry_free (comment);
 
        if (!rc)
          {
-           rc = unprotect (ctrl, desc_text_final, &buf, grip,
-                            cache_mode, lookup_ttl);
+           rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip,
+                            cache_mode, lookup_ttl, r_passphrase);
            if (rc)
              log_error ("failed to unprotect the secret key: %s\n",
                         gpg_strerror (rc));
          }
-        
-       gcry_sexp_release (comment_sexp);
+
        xfree (desc_text_final);
       }
       break;
@@ -618,6 +654,11 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
   if (rc || got_shadow_info)
     {
       xfree (buf);
+      if (r_passphrase)
+        {
+          xfree (*r_passphrase);
+          *r_passphrase = NULL;
+        }
       return rc;
     }
 
@@ -629,6 +670,11 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
     {
       log_error ("failed to build S-Exp (off=%u): %s\n",
                  (unsigned int)erroff, gpg_strerror (rc));
+      if (r_passphrase)
+        {
+          xfree (*r_passphrase);
+          *r_passphrase = NULL;
+        }
       return rc;
     }
 
@@ -637,50 +683,32 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text,
 }
 
 
-
-/* Return the public key for the keygrip GRIP.  The result is stored
-   at RESULT.  This function extracts the public key from the private
-   key database.  On failure an error code is returned and NULL stored
-   at RESULT. */
-gpg_error_t
-agent_public_key_from_file (ctrl_t ctrl, 
-                            const unsigned char *grip,
-                            gcry_sexp_t *result)
+/* Return the string name from the S-expression S_KEY as well as a
+   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
+   this case.  */
+static gpg_error_t
+key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
+                     char *r_algoname, size_t algonamesize,
+                     char *r_elems, size_t elemssize)
 {
-  int i, idx, rc;
-  gcry_sexp_t s_skey;
-  const char *algoname;
-  gcry_sexp_t uri_sexp, comment_sexp;
-  const char *uri, *comment;
-  size_t uri_length, comment_length;
-  char *format, *p;
-  void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2
-                           for comment + end-of-list.  */
-  int argidx;
   gcry_sexp_t list, l2;
-  const char *name;
-  const char *s;
+  const char *name, *algoname, *elems;
   size_t n;
-  const char *elems;
-  gcry_mpi_t *array;
 
-  (void)ctrl;
-
-  *result = NULL;
+  if (r_list)
+    *r_list = NULL;
 
-  rc = read_key_file (grip, &s_skey);
-  if (rc)
-    return rc;
-
-  list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 );
+  list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 );
   if (!list)
-    list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 );
+    list = gcry_sexp_find_token (s_key, "protected-private-key", 0 );
   if (!list)
-    list = gcry_sexp_find_token (s_skey, "private-key", 0 );
+    list = gcry_sexp_find_token (s_key, "private-key", 0 );
   if (!list)
     {
       log_error ("invalid private key format\n");
-      gcry_sexp_release (s_skey);
       return gpg_error (GPG_ERR_BAD_SECKEY);
     }
 
@@ -698,6 +726,16 @@ agent_public_key_from_file (ctrl_t ctrl,
       algoname = "dsa";
       elems = "pqgy";
     }
+  else if (n==5 && !memcmp (name, "ecdsa", 5))
+    {
+      algoname = "ecdsa";
+      elems = "pabgnq";
+    }
+  else if (n==4 && !memcmp (name, "ecdh", 4))
+    {
+      algoname = "ecdh";
+      elems = "pabgnq";
+    }
   else if (n==3 && !memcmp (name, "elg", 3))
     {
       algoname = "elg";
@@ -707,22 +745,130 @@ agent_public_key_from_file (ctrl_t ctrl,
     {
       log_error ("unknown private key algorithm\n");
       gcry_sexp_release (list);
-      gcry_sexp_release (s_skey);
       return gpg_error (GPG_ERR_BAD_SECKEY);
     }
 
+  if (r_algoname)
+    {
+      if (strlen (algoname) >= algonamesize)
+        return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+      strcpy (r_algoname, algoname);
+    }
+  if (r_elems)
+    {
+      if (strlen (elems) >= elemssize)
+        return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+      strcpy (r_elems, elems);
+    }
+
+  if (r_list)
+    *r_list = list;
+  else
+    gcry_sexp_release (list);
+
+  return 0;
+}
+
+
+/* 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)
+{
+  char algoname[6];
+
+  if (!s_key)
+    return 0;
+
+  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+    return 0; /* Error - assume it is not an DSA key.  */
+
+  if (!strcmp (algoname, "dsa"))
+    return GCRY_PK_DSA;
+  else if (!strcmp (algoname, "ecdsa"))
+    return GCRY_PK_ECDSA;
+  else
+    return 0;
+}
+
+
+
+/* Return the key for the keygrip GRIP.  The result is stored at
+   RESULT.  This function extracts the key from the private key
+   database and returns it as an S-expression object as it is.  On
+   failure an error code is returned and NULL stored at RESULT. */
+gpg_error_t
+agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip,
+                         gcry_sexp_t *result)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_skey;
+
+  (void)ctrl;
+
+  *result = NULL;
+
+  err = read_key_file (grip, &s_skey);
+  if (!err)
+    *result = s_skey;
+  return err;
+}
+
+
+/* Return the public key for the keygrip GRIP.  The result is stored
+   at RESULT.  This function extracts the public key from the private
+   key database.  On failure an error code is returned and NULL stored
+   at RESULT. */
+gpg_error_t
+agent_public_key_from_file (ctrl_t ctrl,
+                            const unsigned char *grip,
+                            gcry_sexp_t *result)
+{
+  gpg_error_t err;
+  int i, idx;
+  gcry_sexp_t s_skey;
+  char algoname[6];
+  char elems[6];
+  gcry_sexp_t uri_sexp, comment_sexp;
+  const char *uri, *comment;
+  size_t uri_length, comment_length;
+  char *format, *p;
+  void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2
+                           for comment + end-of-list.  */
+  int argidx;
+  gcry_sexp_t list, l2;
+  const char *s;
+  gcry_mpi_t *array;
+
+  (void)ctrl;
+
+  *result = NULL;
+
+  err = read_key_file (grip, &s_skey);
+  if (err)
+    return err;
+
+  err = key_parms_from_sexp (s_skey, &list,
+                            algoname, sizeof algoname,
+                            elems, sizeof elems);
+  if (err)
+    {
+      gcry_sexp_release (s_skey);
+      return err;
+    }
+
   /* Allocate an array for the parameters and copy them out of the
      secret key.   FIXME: We should have a generic copy function. */
   array = xtrycalloc (strlen(elems) + 1, sizeof *array);
   if (!array)
     {
-      rc = gpg_error_from_syserror ();
+      err = gpg_error_from_syserror ();
       gcry_sexp_release (list);
       gcry_sexp_release (s_skey);
-      return rc;
+      return err;
     }
 
-  for (idx=0, s=elems; *s; s++, idx++ ) 
+  for (idx=0, s=elems; *s; s++, idx++ )
     {
       l2 = gcry_sexp_find_token (list, s, 1);
       if (!l2)
@@ -768,8 +914,8 @@ agent_public_key_from_file (ctrl_t ctrl,
 
 
   /* FIXME: The following thing is pretty ugly code; we should
-     investigate how to make it cleaner. Probably code to handle
-     canonical S-expressions in a memory buffer is better suioted for
+     investigate how to make it cleaner.  Probably code to handle
+     canonical S-expressions in a memory buffer is better suited for
      such a task.  After all that is what we do in protect.c.  Neeed
      to find common patterns and write a straightformward API to use
      them.  */
@@ -778,18 +924,18 @@ agent_public_key_from_file (ctrl_t ctrl,
   format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
   if (!format)
     {
-      rc = gpg_error_from_syserror ();
+      err = gpg_error_from_syserror ();
       for (i=0; array[i]; i++)
         gcry_mpi_release (array[i]);
       xfree (array);
       gcry_sexp_release (uri_sexp);
       gcry_sexp_release (comment_sexp);
-      return rc;
+      return err;
     }
 
   argidx = 0;
   p = stpcpy (stpcpy (format, "(public-key("), algoname);
-  for (idx=0, s=elems; *s; s++, idx++ ) 
+  for (idx=0, s=elems; *s; s++, idx++ )
     {
       *p++ = '(';
       *p++ = *s;
@@ -816,8 +962,8 @@ agent_public_key_from_file (ctrl_t ctrl,
   *p = 0;
   assert (argidx < DIM (args));
   args[argidx] = NULL;
-    
-  rc = gcry_sexp_build_array (&list, NULL, format, args);
+
+  err = gcry_sexp_build_array (&list, NULL, format, args);
   xfree (format);
   for (i=0; array[i]; i++)
     gcry_mpi_release (array[i]);
@@ -825,22 +971,22 @@ agent_public_key_from_file (ctrl_t ctrl,
   gcry_sexp_release (uri_sexp);
   gcry_sexp_release (comment_sexp);
 
-  if (!rc)
+  if (!err)
     *result = list;
-  return rc;
+  return err;
 }
 
 
 
-/* Return the secret key as an S-Exp after locating it using the grip.
-   Returns NULL if key is not available. 0 = key is available */
+/* Check whether the the secret key identified by GRIP is available.
+   Returns 0 is the key is available.  */
 int
 agent_key_available (const unsigned char *grip)
 {
   int result;
   char *fname;
   char hexgrip[40+4+1];
-  
+
   bin2hex (grip, 20, hexgrip);
   strcpy (hexgrip+40, ".key");
 
@@ -866,7 +1012,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
   int keytype;
 
   (void)ctrl;
-  
+
   if (r_keytype)
     *r_keytype = PRIVATE_KEY_UNKNOWN;
   if (r_shadow_info)
@@ -874,7 +1020,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
 
   {
     gcry_sexp_t sexp;
-    
+
     err = read_key_file (grip, &sexp);
     if (err)
       {
@@ -888,12 +1034,12 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
     if (err)
       return err;
   }
-  
+
   keytype = agent_private_key_type (buf);
   switch (keytype)
     {
     case PRIVATE_KEY_CLEAR:
-      break; 
+      break;
     case PRIVATE_KEY_PROTECTED:
       /* If we ever require it we could retrieve the comment fields
          from such a key. */