Support the SETQUALITYBAR command of recent pinentries.
authorWerner Koch <wk@gnupg.org>
Tue, 18 Sep 2007 11:40:09 +0000 (11:40 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 18 Sep 2007 11:40:09 +0000 (11:40 +0000)
agent/ChangeLog
agent/agent.h
agent/call-pinentry.c
agent/command.c
agent/genkey.c

index a10d298..2aa2c13 100644 (file)
@@ -1,3 +1,14 @@
+2007-09-18  Werner Koch  <wk@g10code.com>
+
+       * agent.h (struct pin_entry_info_s): Add element WITH_QUALITYBAR.
+       * genkey.c (check_passphrase_constraints): New arg SILENT.
+       Changed all callers.
+       (agent_protect_and_store, agent_genkey): Enable qualitybar.
+       * call-pinentry.c (agent_askpin): Send that option.
+       (unescape_passphrase_string): New.
+       (inq_quality): New.
+       (estimate_passphrase_quality): New.
+
 2007-09-14  Marcus Brinkmann  <marcus@g10code.de>
 
        * call-pinentry.c (agent_popup_message_stop): Implement kill for
 2007-09-14  Marcus Brinkmann  <marcus@g10code.de>
 
        * call-pinentry.c (agent_popup_message_stop): Implement kill for
index 7d76e43..41b44c3 100644 (file)
@@ -169,6 +169,7 @@ struct pin_entry_info_s
   int max_digits; /* max. number of allowed digits allowed*/
   int max_tries;
   int failed_tries;
   int max_digits; /* max. number of allowed digits allowed*/
   int max_tries;
   int failed_tries;
+  int with_qualitybar; /* Set if the quality bar should be displayed.  */
   int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */
   void *check_cb_arg;  /* optional argument which might be of use in the CB */
   const char *cb_errtext; /* used by the cb to displaye a specific error */
   int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */
   void *check_cb_arg;  /* optional argument which might be of use in the CB */
   const char *cb_errtext; /* used by the cb to displaye a specific error */
@@ -269,7 +270,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
                      membuf_t *outbuf);
 
 /*-- genkey.c --*/
                      membuf_t *outbuf);
 
 /*-- genkey.c --*/
-int check_passphrase_constraints (ctrl_t ctrl, const char *pw);
+int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
 int agent_genkey (ctrl_t ctrl, 
                   const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
 int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
 int agent_genkey (ctrl_t ctrl, 
                   const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
 int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
index 14cc422..2afe9f1 100644 (file)
@@ -413,6 +413,106 @@ all_digitsp( const char *s)
 }  
 
 
 }  
 
 
+/* Return a new malloced string by unescaping the string S.  Escaping
+   is percent escaping and '+'/space mapping.  A binary Nul will
+   silently be replaced by a 0xFF.  Function returns NULL to indicate
+   an out of memory status.  PArsing stops at the end of the string or
+   a white space character. */
+static char *
+unescape_passphrase_string (const unsigned char *s)
+{
+  char *buffer, *d;
+
+  buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1);
+  if (!buffer)
+    return NULL;
+  while (*s && !spacep (s))
+    {
+      if (*s == '%' && s[1] && s[2])
+        { 
+          s++;
+          *d = xtoi_2 (s);
+          if (!*d)
+            *d = '\xff';
+          d++;
+          s += 2;
+        }
+      else if (*s == '+')
+        {
+          *d++ = ' ';
+          s++;
+        }
+      else
+        *d++ = *s++;
+    }
+  *d = 0; 
+  return buffer;
+}
+
+
+/* Estimate the quality of the passphrase PW and return a value in the
+   range 0..100.  */
+static int
+estimate_passphrase_quality (const char *pw)
+{
+  int goodlength = opt.min_passphrase_len + opt.min_passphrase_len/3;
+  int length;
+  const char *s;
+
+  if (goodlength < 1)
+    return 0;
+
+  for (length = 0, s = pw; *s; s++)
+    if (!spacep (s))
+      length ++;
+
+  if (length > goodlength)
+    return 100;
+  return ((length*10) / goodlength)*10;
+}
+
+
+/* Handle the QUALITY inquiry. */
+static int
+inq_quality (void *opaque, const char *line)
+{
+  assuan_context_t ctx = opaque;
+  char *pin;
+  int rc;
+  int percent;
+  char numbuf[20];
+
+  if (!strncmp (line, "QUALITY", 7) && (line[7] == ' ' || !line[7]))
+    {
+      line += 7;
+      while (*line == ' ')
+        line++;
+      
+      pin = unescape_passphrase_string (line);
+      if (!pin)
+        rc = gpg_error_from_syserror ();
+      else
+        {
+          percent = estimate_passphrase_quality (pin);
+          if (check_passphrase_constraints (NULL, pin, 1))
+            percent = -percent;
+          snprintf (numbuf, sizeof numbuf, "%d", percent);
+          rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+          xfree (pin);
+        }
+    }
+  else
+    {
+      log_error ("unsupported inquiry `%s' from pinentry\n", line);
+      rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+    }
+
+  return rc;
+}
+
+
+
+
 \f
 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
    number here and repeat it as long as we have invalid formed
 \f
 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
    number here and repeat it as long as we have invalid formed
@@ -463,6 +563,16 @@ agent_askpin (ctrl_t ctrl,
   if (rc)
     return unlock_pinentry (rc);
 
   if (rc)
     return unlock_pinentry (rc);
 
+  /* If a passphrase quality indicator has been requested and a
+     minimum passphrase length has not been disabled, send the command
+     to the pinentry.  */
+  if (pininfo->with_qualitybar && opt.min_passphrase_len )
+    {
+      rc = assuan_transact (entry_ctx, "SETQUALITYBAR",
+                            NULL, NULL, NULL, NULL, NULL, NULL);
+      if (rc)
+        return unlock_pinentry (rc);
+    }
 
   if (initial_errtext)
     { 
 
   if (initial_errtext)
     { 
@@ -497,7 +607,7 @@ agent_askpin (ctrl_t ctrl,
         }
       
       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
         }
       
       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
-                            NULL, NULL, NULL, NULL);
+                            inq_quality, entry_ctx, NULL, NULL);
       /* Most pinentries out in the wild return the old Assuan error code
          for canceled which gets translated to an assuan Cancel error and
          not to the code for a user cancel.  Fix this here. */
       /* Most pinentries out in the wild return the old Assuan error code
          for canceled which gets translated to an assuan Cancel error and
          not to the code for a user cancel.  Fix this here. */
index bb3d52f..431639b 100644 (file)
@@ -912,7 +912,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
         }
       while (!rc
              && opt_check
         }
       while (!rc
              && opt_check
-             && check_passphrase_constraints (ctrl, response));
+             && check_passphrase_constraints (ctrl, response, 0));
 
       if (!rc)
         {
 
       if (!rc)
         {
index 48ba39d..9901e9e 100644 (file)
@@ -168,9 +168,10 @@ take_this_one_anyway (ctrl_t ctrl, const char *desc)
 
 /* Check whether the passphrase PW is suitable. Returns 0 if the
    passphrase is suitable and true if it is not and the user should be
 
 /* Check whether the passphrase PW is suitable. Returns 0 if the
    passphrase is suitable and true if it is not and the user should be
-   asked to provide a different one. */
+   asked to provide a different one.  If SILENT is set, no message are
+   displayed.  */
 int
 int
-check_passphrase_constraints (ctrl_t ctrl, const char *pw)
+check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent)
 {
   gpg_error_t err;
   unsigned int minlen = opt.min_passphrase_len;
 {
   gpg_error_t err;
   unsigned int minlen = opt.min_passphrase_len;
@@ -181,7 +182,12 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
 
   if (utf8_charcount (pw) < minlen ) 
     {
 
   if (utf8_charcount (pw) < minlen ) 
     {
-      char *desc = xtryasprintf 
+      char *desc;
+      
+      if (silent)
+        return gpg_error (GPG_ERR_INV_PASSPHRASE);
+
+      desc = xtryasprintf 
         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
                     "is obviously not secure.  A passphrase should%%0A"
                     "be at least %u character long.", 
         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
                     "is obviously not secure.  A passphrase should%%0A"
                     "be at least %u character long.", 
@@ -198,7 +204,12 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
 
   if (nonalpha_count (pw) < minnonalpha ) 
     {
 
   if (nonalpha_count (pw) < minnonalpha ) 
     {
-      char *desc = xtryasprintf 
+      char *desc;
+
+      if (silent)
+        return gpg_error (GPG_ERR_INV_PASSPHRASE);
+
+      desc = xtryasprintf 
         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
                     "is obviously not secure.  A passphrase should%%0A"
                     "contain at least %u digit or special character.", 
         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
                     "is obviously not secure.  A passphrase should%%0A"
                     "contain at least %u digit or special character.", 
@@ -226,6 +237,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
                     "is obviously not secure.  A passphrase may not%0A"
                     "be a known term or match certain pattern.");
 
                     "is obviously not secure.  A passphrase may not%0A"
                     "be a known term or match certain pattern.");
 
+      if (silent)
+        return gpg_error (GPG_ERR_INV_PASSPHRASE);
+
       err = take_this_one_anyway (ctrl, desc);
       if (err)
         return err;
       err = take_this_one_anyway (ctrl, desc);
       if (err)
         return err;
@@ -242,6 +256,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
                             "Please confirm that you do not want to "
                             "have any protection on your key."));
       
                             "Please confirm that you do not want to "
                             "have any protection on your key."));
       
+      if (silent)
+        return gpg_error (GPG_ERR_INV_PASSPHRASE);
+
       err = take_this_one_anyway2 (ctrl, desc,
                                    _("Yes, protection is not needed"));
       if (err)
       err = take_this_one_anyway2 (ctrl, desc,
                                    _("Yes, protection is not needed"));
       if (err)
@@ -296,6 +313,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
     pi2 = pi + (sizeof *pi + 100);
     pi->max_length = 100;
     pi->max_tries = 3;
     pi2 = pi + (sizeof *pi + 100);
     pi->max_length = 100;
     pi->max_tries = 3;
+    pi->with_qualitybar = 1;
     pi2->max_length = 100;
     pi2->max_tries = 3;
     pi2->check_cb = reenter_compare_cb;
     pi2->max_length = 100;
     pi2->max_tries = 3;
     pi2->check_cb = reenter_compare_cb;
@@ -306,7 +324,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
     initial_errtext = NULL;
     if (!rc)
       {
     initial_errtext = NULL;
     if (!rc)
       {
-        if (check_passphrase_constraints (ctrl, pi->pin))
+        if (check_passphrase_constraints (ctrl, pi->pin, 0))
           {
             pi->failed_tries = 0;
             pi2->failed_tries = 0;
           {
             pi->failed_tries = 0;
             pi2->failed_tries = 0;
@@ -417,6 +435,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
     pi2 = pi + (sizeof *pi + 100);
     pi->max_length = 100;
     pi->max_tries = 3;
     pi2 = pi + (sizeof *pi + 100);
     pi->max_length = 100;
     pi->max_tries = 3;
+    pi->with_qualitybar = 1;
     pi2->max_length = 100;
     pi2->max_tries = 3;
     pi2->check_cb = reenter_compare_cb;
     pi2->max_length = 100;
     pi2->max_tries = 3;
     pi2->check_cb = reenter_compare_cb;
@@ -427,7 +446,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
     initial_errtext = NULL;
     if (!rc)
       {
     initial_errtext = NULL;
     if (!rc)
       {
-        if (check_passphrase_constraints (ctrl, pi->pin))
+        if (check_passphrase_constraints (ctrl, pi->pin, 0))
           {
             pi->failed_tries = 0;
             pi2->failed_tries = 0;
           {
             pi->failed_tries = 0;
             pi2->failed_tries = 0;