Ask to insert the right OpenPGP card.
[gnupg.git] / g10 / passphrase.c
index b156d8f..83a6b0c 100644 (file)
@@ -1,12 +1,12 @@
 /* passphrase.c -  Get a passphrase
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- *               2005, 2006 Free Software Foundation, Inc.
+ *               2005, 2006, 2007, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -52,11 +50,10 @@ static char *next_pw = NULL;
 static char *last_pw = NULL;
 
 
-/* Hash a passphrase using the supplied s2k. If create is true, create
-   a new salt or what else must be filled into the s2k for a new key.
-   always needs: dek->algo, s2k->mode, s2k->hash_algo.  */
+/* Hash a passphrase using the supplied s2k. 
+   Always needs: dek->algo, s2k->mode, s2k->hash_algo.  */
 static void
-hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
+hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k)
 {
   gcry_md_hd_t md;
   int pass, i;
@@ -64,7 +61,7 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
   int pwlen = strlen(pw);
 
   assert ( s2k->hash_algo );
-  dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
+  dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
   if ( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) )
     BUG();
 
@@ -84,20 +81,17 @@ hash_passphrase ( DEK *dek, char *pw, STRING2KEY *s2k, int create )
           int len2 = pwlen + 8;
           ulong count = len2;
           
-          if ( create && !pass )
-            {
-              gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
-              if ( s2k->mode == 3 )
-                s2k->count = 96; /* 65536 iterations. */
-           }
-
           if ( s2k->mode == 3 )
             {
-              count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6);
+              count = S2K_DECODE_COUNT(s2k->count);
               if ( count < len2 )
                 count = len2;
            }
 
+          /* Fixme: To avoid DoS attacks by sending an sym-encrypted
+             packet with a very high S2K count, we should either cap
+             the iteration count or CPU seconds based timeout.  */
+
           /* A little bit complicated because we need a ulong for count. */
           while ( count > len2 )  /* maybe iterated+salted */
             { 
@@ -238,8 +232,8 @@ read_passphrase_from_fd( int fd )
 /*
  * Ask the GPG Agent for the passphrase.
  * Mode 0:  Allow cached passphrase
- *      1:  No cached passphrase FIXME: Not really implemented
- *      2:  Ditto, but change the text to "repeat entry"
+ *      1:  No cached passphrase; that is we are asking for a new passphrase
+ *          FIXME: Only partially implemented
  *
  * Note that TRYAGAIN_TEXT must not be translated.  If CANCELED is not
  * NULL, the function does set it to 1 if the user canceled the
@@ -248,7 +242,7 @@ read_passphrase_from_fd( int fd )
  * computed, this will be used as the cacheid.
  */
 static char *
-passphrase_get ( u32 *keyid, int mode, const char *cacheid,
+passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat,
                  const char *tryagain_text,
                  const char *custom_description,
                  const char *custom_prompt, int *canceled)
@@ -259,10 +253,11 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
   PKT_public_key *pk = xmalloc_clear( sizeof *pk );
   byte fpr[MAX_FINGERPRINT_LEN];
   int have_fpr = 0;
-  char *orig_codeset = NULL;
+  char *orig_codeset;
   char *my_prompt;
   char hexfprbuf[20*2+1];
   const char *my_cacheid;
+  int check = (mode == 1);
 
   if (canceled)
     *canceled = 0;
@@ -279,20 +274,7 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
       pk = NULL; /* oops: no key for some reason */
     }
   
-#ifdef ENABLE_NLS
-  /* The Assuan agent protocol requires us to transmit utf-8 strings */
-  orig_codeset = bind_textdomain_codeset (PACKAGE, NULL);
-#ifdef HAVE_LANGINFO_CODESET
-  if (!orig_codeset)
-    orig_codeset = nl_langinfo (CODESET);
-#endif
-  if (orig_codeset)
-    { /* We only switch when we are able to restore the codeset later. */
-      orig_codeset = xstrdup (orig_codeset);
-      if (!bind_textdomain_codeset (PACKAGE, "utf-8"))
-        orig_codeset = NULL; 
-    }
-#endif
+  orig_codeset = i18n_switchto_utf8 ();
 
   if (custom_description)
     atext = native_to_utf8 (custom_description);
@@ -321,10 +303,11 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
 
 #undef KEYIDSTRING
 
-#define PROMPTSTRING _("You need a passphrase to unlock the secret" \
-                      " key for user:\n" \
+#define PROMPTSTRING _("Please enter the passphrase to unlock the" \
+                      " secret key for the OpenPGP certificate:\n" \
                       "\"%.*s\"\n" \
-                      "%u-bit %s key, ID %s, created %s%s\n" )
+                      "%u-bit %s key, ID %s,\n" \
+                       "created %s%s.\n" )
 
       atext = xmalloc ( 100 + strlen (PROMPTSTRING)  
                         + uidlen + 15 + strlen(algo_name) + keystrlen()
@@ -345,8 +328,6 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
       }
       
     }
-  else if (mode == 2 ) 
-    atext = xstrdup ( _("Repeat passphrase\n") );
   else
     atext = xstrdup ( _("Enter passphrase\n") );
                 
@@ -363,11 +344,15 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
 
   my_prompt = custom_prompt ? native_to_utf8 (custom_prompt): NULL;
 
-  rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext, &pw);
+  rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext,
+                             repeat, check, &pw);
   
   xfree (my_prompt);
   xfree (atext); atext = NULL;
 
+  i18n_switchback (orig_codeset);
+
+
   if (!rc)
     ;
   else if ( gpg_err_code (rc) == GPG_ERR_CANCELED )
@@ -377,15 +362,21 @@ passphrase_get ( u32 *keyid, int mode, const char *cacheid,
         *canceled = 1;
     }
   else 
-    log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
-      
-#ifdef ENABLE_NLS
-  if (orig_codeset)
     {
-      bind_textdomain_codeset (PACKAGE, orig_codeset);
-      xfree (orig_codeset);
+      log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
+      /* Due to limitations in the API of the upper layers they
+         consider an error as no passphrase entered.  This works in
+         most cases but not during key creation where this should
+         definitely not happen and let it continue without requiring a
+         passphrase.  Given that now all the upper layers handle a
+         cancel correctly, we simply set the cancel flag now for all
+         errors from the agent.  */ 
+      if (canceled)
+        *canceled = 1;
+
+      write_status_error ("get_passphrase", rc);
     }
-#endif
+
   if (pk)
     free_public_key( pk );
   if (rc)
@@ -405,6 +396,8 @@ void
 passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo )
 {
   int rc;
+
+  (void)algo;
     
   if (!cacheid)
     {
@@ -437,71 +430,36 @@ passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo )
 }
 
 
-/****************
- * Ask for a passphrase and return that string.
- */
-char *
-ask_passphrase (const char *description,
-                const char *tryagain_text,
-                const char *promptid,
-                const char *prompt,
-                const char *cacheid, int *canceled)
-{
-  char *pw = NULL;
-  
-  if (canceled)
-    *canceled = 0;
-
-  if (!opt.batch && description)
-    {
-      if (strchr (description, '%'))
-        {
-          char *tmp = unescape_percent_string (description);
-          tty_printf ("\n%s\n", tmp);
-          xfree (tmp);
-        }
-      else
-        tty_printf ("\n%s\n",description);
-    }
-               
-  if (have_static_passphrase ()) 
-    {
-      pw = xmalloc_secure (strlen(fd_passwd)+1);
-      strcpy (pw, fd_passwd);
-    }
-  else
-    pw = passphrase_get (NULL, 0, cacheid,
-                         tryagain_text, description, prompt,
-                         canceled );
-
-  if (!pw || !*pw)
-    write_status( STATUS_MISSING_PASSPHRASE );
-
-  return pw;
-}
-
-
 /* Return a new DEK object Using the string-to-key sepcifier S2K.  Use
- * KEYID and PUBKEY_ALGO to prompt the user.
+   KEYID and PUBKEY_ALGO to prompt the user.  Returns NULL is the user
+   selected to cancel the passphrase entry and if CANCELED is not
+   NULL, sets it to true.
 
    MODE 0:  Allow cached passphrase
         1:  Ignore cached passphrase 
-        2:  Ditto, but change the text to "repeat entry"
+        2:  Ditto, but create a new key
+        3:  Allow cached passphrase; use the S2K salt as the cache ID
+        4:  Ditto, but create a new key
 */
 DEK *
-passphrase_to_dek (u32 *keyid, int pubkey_algo,
-                  int cipher_algo, STRING2KEY *s2k, int mode,
-                   const char *tryagain_text, int *canceled)
+passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
+                       int cipher_algo, STRING2KEY *s2k, int mode,
+                       const char *tryagain_text, 
+                       const char *custdesc, const char *custprompt,
+                       int *canceled)
 {
   char *pw = NULL;
   DEK *dek;
   STRING2KEY help_s2k;
-  
-  if (canceled)
-    *canceled = 0;
+  int dummy_canceled;
+
+  if (!canceled)
+    canceled = &dummy_canceled;
+  *canceled = 0;
   
   if ( !s2k )
     {
+      assert (mode != 3 && mode != 4);
       /* This is used for the old rfc1991 mode 
        * Note: This must match the code in encode.c with opt.rfc1991 set */
       s2k = &help_s2k;
@@ -509,6 +467,15 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
       s2k->hash_algo = S2K_DIGEST_ALGO;
     }
 
+  /* Create a new salt or what else to be filled into the s2k for a
+     new key.  */
+  if ((mode == 2 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
+    {
+      gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
+      if ( s2k->mode == 3 )
+        s2k->count = opt.s2k_count;
+    }
+
   /* If we do not have a passphrase available in NEXT_PW and status
      information are request, we print them now. */
   if ( !next_pw && is_status_enabled() ) 
@@ -600,31 +567,33 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
     }
   else if ( have_static_passphrase () ) 
     {
-      /* Return the passphrase we have store in FD_PASSWD. */
+      /* Return the passphrase we have stored in FD_PASSWD. */
       pw = xmalloc_secure ( strlen(fd_passwd)+1 );
       strcpy ( pw, fd_passwd );
     }
   else 
     {
+      char *cacheid = NULL;
+      char buf[1+16+1];
+
+      if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
+       {
+         memset (buf, 0, sizeof buf);
+         *buf = 'S';
+         bin2hex (s2k->salt, 8, buf + 1);
+          cacheid = buf;
+       }
+
       /* Divert to the gpg-agent. */
-      pw = passphrase_get ( keyid, mode == 2? 1: 0, NULL,
-                            tryagain_text, NULL, NULL, canceled );
-      if (!pw)
-        pw = xstrdup ("");
-      if ( *pw && mode == 2 )
+      pw = passphrase_get (keyid, mode == 2, cacheid,
+                           (mode == 2 || mode == 4)? opt.passwd_repeat : 0,
+                           tryagain_text, custdesc, custprompt, canceled);
+      if (*canceled)
         {
-          char *pw2 = passphrase_get ( keyid, 2, NULL, NULL, NULL,
-                                       NULL, canceled );
-          if (!pw2)
-            pw2 = xstrdup ("");
-          if ( strcmp(pw, pw2) )
-            {
-              xfree(pw2);
-              xfree(pw);
-              return NULL;
-            }
-          xfree(pw2);
-       }
+          xfree (pw);
+         write_status( STATUS_MISSING_PASSPHRASE );
+          return NULL;
+        }
     }
     
   if ( !pw || !*pw )
@@ -635,11 +604,22 @@ passphrase_to_dek (u32 *keyid, int pubkey_algo,
      get_last_passphrase(). */
   dek = xmalloc_secure_clear ( sizeof *dek );
   dek->algo = cipher_algo;
-  if ( !*pw && mode == 2 )
+  if ( (!pw || !*pw) && (mode == 2 || mode == 4))
     dek->keylen = 0;
   else
-    hash_passphrase( dek, pw, s2k, mode==2 );
+    hash_passphrase (dek, pw, s2k);
   xfree(last_pw);
   last_pw = pw;
   return dek;
 }
+
+
+DEK *
+passphrase_to_dek (u32 *keyid, int pubkey_algo,
+                  int cipher_algo, STRING2KEY *s2k, int mode,
+                   const char *tryagain_text, int *canceled)
+{
+  return passphrase_to_dek_ext (keyid, pubkey_algo, cipher_algo,
+                                s2k, mode, tryagain_text, NULL, NULL,
+                                canceled);
+}