* configure.ac: Require libgcrypt 1.1.94.
[gnupg.git] / agent / findkey.c
index db36cb1..9866b54 100644 (file)
@@ -1,5 +1,5 @@
 /* findkey.c - locate the secret key
- *     Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -138,9 +138,10 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
 
 /* 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. */
+   caching mechanism. DESC_TEXT may be set to override the default
+   description used for the pinentry. */
 static int
-unprotect (CTRL ctrl,
+unprotect (CTRL ctrl, const char *desc_text,
            unsigned char **keybuf, const unsigned char *grip, int ignore_cache)
 {
   struct pin_entry_info_s *pi;
@@ -154,7 +155,7 @@ unprotect (CTRL ctrl,
     sprintf (hexgrip+2*i, "%02X", grip[i]);
   hexgrip[40] = 0;
 
-  /* first try to get it from the cache - if there is none or we can't
+  /* 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 (!ignore_cache)
     {
@@ -184,7 +185,7 @@ unprotect (CTRL ctrl,
   arg.unprotected_key = NULL;
   pi->check_cb_arg = &arg;
 
-  rc = agent_askpin (ctrl, NULL, pi);
+  rc = agent_askpin (ctrl, desc_text, NULL, pi);
   if (!rc)
     {
       assert (arg.unprotected_key);
@@ -198,16 +199,17 @@ unprotect (CTRL ctrl,
 
 
 
-/* Return the secret key as an S-Exp after locating it using the grip.
-   Returns NULL if key is not available or the operation should be
-   diverted to a token.  In the latter case shadow_info will point to
-   an allocated S-Expression with the shadow_info part from the file.
-   With IGNORE_CACHE passed as true the passphrase is not taken from
-   the cache.*/
-gcry_sexp_t
-agent_key_from_file (CTRL ctrl,
+/* Return the secret key as an S-Exp in RESULT after locating it using
+   the grip.  Returns NULL in RESULT if the operation should be
+   diverted to a token; SHADOW_INFO will point then to an allocated
+   S-Expression with the shadow_info part from the file.  With
+   IGNORE_CACHE passed as true the passphrase is not taken from the
+   cache.  DESC_TEXT may be set to present a custom description for the
+   pinentry. */
+gpg_error_t
+agent_key_from_file (CTRL ctrl, const char *desc_text,
                      const unsigned char *grip, unsigned char **shadow_info,
-                     int ignore_cache)
+                     int ignore_cache, gcry_sexp_t *result)
 {
   int i, rc;
   char *fname;
@@ -217,7 +219,9 @@ agent_key_from_file (CTRL ctrl,
   size_t len, buflen, erroff;
   gcry_sexp_t s_skey;
   char hexgrip[40+4+1];
+  int got_shadow_info = 0;
   
+  *result = NULL;
   if (shadow_info)
       *shadow_info = NULL;
 
@@ -229,28 +233,31 @@ agent_key_from_file (CTRL ctrl,
   fp = fopen (fname, "rb");
   if (!fp)
     {
+      rc = gpg_error_from_errno (errno);
       log_error ("can't open `%s': %s\n", fname, strerror (errno));
       xfree (fname);
-      return NULL;
+      return rc;
     }
   
   if (fstat (fileno(fp), &st))
     {
+      rc = gpg_error_from_errno (errno);
       log_error ("can't stat `%s': %s\n", fname, strerror (errno));
       xfree (fname);
       fclose (fp);
-      return NULL;
+      return rc;
     }
 
   buflen = st.st_size;
   buf = xmalloc (buflen+1);
   if (fread (buf, buflen, 1, fp) != 1)
     {
+      rc = gpg_error_from_errno (errno);
       log_error ("error reading `%s': %s\n", fname, strerror (errno));
       xfree (fname);
       fclose (fp);
       xfree (buf);
-      return NULL;
+      return rc;
     }
 
   rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen);
@@ -261,15 +268,16 @@ agent_key_from_file (CTRL ctrl,
     {
       log_error ("failed to build S-Exp (off=%u): %s\n",
                  (unsigned int)erroff, gpg_strerror (rc));
-      return NULL;
+      return rc;
     }
   len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
   assert (len);
   buf = xtrymalloc (len);
   if (!buf)
     {
+      rc = out_of_core ();
       gcry_sexp_release (s_skey);
-      return NULL;
+      return rc;
     }
   len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
   assert (len);
@@ -280,7 +288,7 @@ agent_key_from_file (CTRL ctrl,
     case PRIVATE_KEY_CLEAR:
       break; /* no unprotection needed */
     case PRIVATE_KEY_PROTECTED:
-      rc = unprotect (ctrl, &buf, grip, ignore_cache);
+      rc = unprotect (ctrl, desc_text, &buf, grip, ignore_cache);
       if (rc)
         log_error ("failed to unprotect the secret key: %s\n",
                    gpg_strerror (rc));
@@ -303,37 +311,39 @@ agent_key_from_file (CTRL ctrl,
                 {
                   memcpy (*shadow_info, s, n);
                   rc = 0;
+                  got_shadow_info = 1;
                 }
             }
           if (rc)
             log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc));
         }
-      rc = -1; /* ugly interface: we return an error but keep a value
-                  in shadow_info.  */
+      else
+        rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
       break;
     default:
       log_error ("invalid private key format\n");
       rc = gpg_error (GPG_ERR_BAD_SECKEY);
       break;
     }
-  if (rc)
+  if (rc || got_shadow_info)
     {
       xfree (buf);
-      return NULL;
+      return rc;
     }
 
-  /* arggg FIXME: does scan support secure memory? */
-  rc = gcry_sexp_sscan (&s_skey, &erroff,
-                        buf, gcry_sexp_canon_len (buf, 0, NULL, NULL));
+  buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL);
+  rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen);
+  wipememory (buf, buflen);
   xfree (buf);
   if (rc)
     {
       log_error ("failed to build S-Exp (off=%u): %s\n",
                  (unsigned int)erroff, gpg_strerror (rc));
-      return NULL;
+      return rc;
     }
 
-  return s_skey;
+  *result = s_skey;
+  return 0;
 }
 
 /* Return the secret key as an S-Exp after locating it using the grip.