[PATCH] Added user defined pinentry prompts for SCD.

Ben Kibbey bjk at luxsci.net
Wed Jan 11 04:19:11 CET 2012


This adds scdaemon "OPTION pin-prompt" and "OPTION pin-admin-prompt"
along with special escapes to replace in the prompt string to inform the
user of a signature count and admin PIN attempts remaining.

It also adds another "standard" pinentry escape "|I|" to ignore the
default pinentry prompt from gpg-agent and use the supplied 'info'
parameter unmodified (cannot be used with other pinentry flags).
---
 agent/divert-scd.c |   12 +++++-
 scd/app-common.h   |    9 ++++
 scd/app-openpgp.c  |  108 ++++++++++++++++++++++++++++++++++++++++++++++------
 scd/app.c          |   92 ++++++++++++++++++++++++++++++++++++++++++++
 scd/command.c      |   68 ++++++++++++++++++++++++++++++++-
 5 files changed, 274 insertions(+), 15 deletions(-)

diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index f176a6b..a2de217 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -166,6 +166,8 @@ encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
       'A' = The PIN is an Admin PIN, SO-PIN or alike.
       'P' = The PIN is a PUK (Personal Unblocking Key).
       'R' = The PIN is a Reset Code.
+      'I' = Ignore using the default prompt and use 'info' as the entire
+            prompt. Cannot be used with other flags.
 
    Example:
 
@@ -185,6 +187,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
   int newpin = 0;
   int resetcode = 0;
   int is_puk = 0;
+  int ignore = 0;
   const char *again_text = NULL;
   const char *prompt = "PIN";
 
@@ -212,6 +215,8 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
               prompt = _("Reset Code");
               resetcode = 1;
             }
+	  else if (*s == 'I')
+	    ignore = 1;
         }
       info = ends+1;
       any_flags = 1;
@@ -219,6 +224,9 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
   else if (info && *info == '|')
     log_debug ("pin_cb called without proper PIN info hack\n");
 
+  if (ignore)
+    any_flags = 0;
+
   /* If BUF has been passed as NULL, we are in keypad mode: The
      callback opens the popup and immediatley returns. */
   if (!buf)
@@ -305,8 +313,8 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
     }
   else
     {
-      char *desc;
-      if ( asprintf (&desc,
+      char *desc = NULL;
+      if (!ignore && asprintf (&desc,
                      _("Please enter the PIN%s%s%s to unlock the card"),
                      info? " (`":"",
                      info? info:"",
diff --git a/scd/app-common.h b/scd/app-common.h
index e3d23c2..d89c5dc 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -34,6 +34,11 @@
 #define APP_CHANGE_FLAG_RESET    1
 #define APP_CHANGE_FLAG_NULLPIN  2
 
+/* For user defined pinentry prompts. */
+enum {
+    PIN_PROMPT,
+    PIN_ADMIN_PROMPT,
+};
 
 struct app_local_s;  /* Defined by all app-*.c.  */
 
@@ -119,6 +124,7 @@ struct app_ctx_s {
     gpg_error_t (*check_pin) (app_t app, const char *keyidstr,
                       gpg_error_t (*pincb)(void*, const char *, char **),
                       void *pincb_arg);
+    gpg_error_t (*set_pin_prompt)(app_t app, int which, const char *prompt);
   } fnc;
 
 };
@@ -192,6 +198,7 @@ gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
                         time_t createtime,
                         gpg_error_t (*pincb)(void*, const char *, char **),
                         void *pincb_arg);
+gpg_error_t app_set_pin_prompt (app_t app, int which, const char *prompt);
 gpg_error_t app_get_challenge (app_t app, size_t nbytes,
                                unsigned char *buffer);
 gpg_error_t app_change_pin (app_t app, ctrl_t ctrl,
@@ -201,6 +208,8 @@ gpg_error_t app_change_pin (app_t app, ctrl_t ctrl,
 gpg_error_t app_check_pin (app_t app, const char *keyidstr,
                    gpg_error_t (*pincb)(void*, const char *, char **),
                    void *pincb_arg);
+char *expand_pin_prompt(const char *prompt, const char *prepend, int which,
+		   ...);
 
 
 /*-- app-openpgp.c --*/
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index e3a4484..a840c98 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -198,6 +198,8 @@ struct app_local_s {
     rsa_key_format_t format;
   } keyattr[3];
 
+  char *pin_prompt;          /* As set with set_pin_prompt() or a default. */
+  char *pin_admin_prompt;
 };
 
 
@@ -242,6 +244,8 @@ do_deinit (app_t app)
           xfree (app->app_local->pk[i].key);
           app->app_local->pk[i].read_done = 0;
         }
+      xfree(app->app_local->pin_prompt);
+      xfree(app->app_local->pin_admin_prompt);
       xfree (app->app_local);
       app->app_local = NULL;
     }
@@ -1520,19 +1524,41 @@ verify_a_chv (app_t app,
 
   if (chvno == 1)
     {
+      if (app->app_local->pin_prompt)
+	{
+	  prompt_buffer = expand_pin_prompt(app->app_local->pin_prompt, "|I|",
+	      PIN_PROMPT, sigcount);
+	  if (!prompt_buffer)
+	    return gpg_error_from_syserror();
+	}
+      else
+      {
 #define PROMPTSTRING  _("||Please enter the PIN%%0A[sigs done: %lu]")
-      size_t promptsize = strlen (PROMPTSTRING) + 50;
+	size_t promptsize;
 
-      prompt_buffer = xtrymalloc (promptsize);
-      if (!prompt_buffer)
-        return gpg_error_from_syserror ();
-      snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
-      prompt = prompt_buffer;
+	promptsize = strlen (PROMPTSTRING) + 50;
+	prompt_buffer = xtrymalloc (promptsize);
+	if (!prompt_buffer)
+	  return gpg_error_from_syserror ();
+	snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
 #undef PROMPTSTRING
+      }
+
+      prompt = prompt_buffer;
     }
   else
-    prompt = _("||Please enter the PIN");
-
+    {
+      if (app->app_local->pin_prompt)
+	{
+	  prompt_buffer = expand_pin_prompt(app->app_local->pin_prompt, "|I|",
+	      -1, NULL);
+	  if (!prompt_buffer)
+	    return gpg_error_from_syserror();
+	  prompt = prompt_buffer;
+	}
+      else
+	prompt = _("||Please enter the PIN");
+    }
 
   if (!opt.disable_keypad
       && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
@@ -1673,11 +1699,21 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
     {
       /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
          the start of the string.  Use %%0A to force a linefeed.  */
-      prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
-                               "[remaining attempts: %d]"), remaining);
+      if (app->app_local->pin_admin_prompt)
+	prompt = expand_pin_prompt(app->app_local->pin_admin_prompt, "|I|",
+	    PIN_ADMIN_PROMPT, remaining);
+      else
+	prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
+	      "[remaining attempts: %d]"), remaining);
     }
   else
-    prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
+    {
+      if (app->app_local->pin_admin_prompt)
+	prompt = expand_pin_prompt(app->app_local->pin_admin_prompt, "|I|",
+	    -1, NULL);
+      else
+	prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
+    }
 
   if (!prompt)
     return gpg_error_from_syserror ();
@@ -1999,7 +2035,21 @@ do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
                   prompt = promptbuf;
                 }
               else
-                prompt = _("||Please enter the PIN");
+		{
+		  if (app->app_local->pin_prompt)
+		  {
+		    promptbuf = expand_pin_prompt(app->app_local->pin_prompt,
+			"|I|", -1, NULL);
+		    if (!promptbuf)
+		      {
+			rc = gpg_error_from_syserror();
+			goto leave;
+		      }
+		    prompt = promptbuf;
+		  }
+		  else
+		    prompt = _("||Please enter the PIN");
+		}
               rc = pincb (pincb_arg, prompt, &oldpinvalue);
               xfree (promptbuf);
               promptbuf = NULL;
@@ -3707,6 +3757,39 @@ parse_algorithm_attribute (app_t app, int keyno)
   xfree (relptr);
 }
 
+gpg_error_t do_set_pin_prompt(app_t app, int which, const char *prompt)
+{
+  gpg_error_t rc = 0;
+  char **p = NULL;
+
+  switch (which)
+    {
+      case PIN_PROMPT:
+	p = &app->app_local->pin_prompt;
+	break;
+      case PIN_ADMIN_PROMPT:
+	p = &app->app_local->pin_admin_prompt;
+	break;
+      default:
+	  break;
+    }
+
+  if (p)
+    {
+      xfree(*p);
+      *p = NULL;
+
+      if (prompt && *prompt != '-' && *(prompt+1) != 0)
+	{
+	  *p = xtrystrdup(prompt);
+	  if (!*p)
+	    rc = gpg_error_from_syserror();
+	}
+    }
+
+  return rc;
+}
+
 /* Select the OpenPGP application on the card in SLOT.  This function
    must be used before any other OpenPGP application functions. */
 gpg_error_t
@@ -3850,6 +3933,7 @@ app_select_openpgp (app_t app)
       app->fnc.decipher = do_decipher;
       app->fnc.change_pin = do_change_pin;
       app->fnc.check_pin = do_check_pin;
+      app->fnc.set_pin_prompt = do_set_pin_prompt;
    }
 
 leave:
diff --git a/scd/app.c b/scd/app.c
index 76dc8b4..0e722c6 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -923,6 +923,98 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
 }
 
 
+/* Replaces special escapes in a user defined pinentry prompt with the
+ * argument value where 'which' is one of:
+ *
+ * PIN_PROMPT:       |S| - signature count (unsigned long)
+ * PIN_ADMIN_PROMPT: |A| - remaining attempts (int)
+ *
+ * The escapes in the prompt are replaced with the values. A prompt is set
+ * with the OPTION command and reset to the default when it's value is -.
+ */
+char *expand_pin_prompt(const char *prompt, const char *prepend,
+	int which, ...)
+{
+  va_list ap;
+  size_t len, n;
+  char *buf;
+  unsigned long luval;
+  int intval;
+  char valuebuf[50] = {0};
+  char *p, *token = NULL, *tokenp;
+
+  if (!prompt)
+    return NULL;
+
+  len = strlen(prompt);
+  len += prepend ? strlen(prepend) : 0;
+  va_start(ap, which);
+
+  switch (which)
+    {
+      /* Signature count. */
+      case PIN_PROMPT:
+	luval = va_arg(ap, unsigned long);
+	snprintf(valuebuf, sizeof(valuebuf), "%lu", luval);
+	token = "|S|";
+	break;
+      /* Pin tries remaining. */
+      case PIN_ADMIN_PROMPT:
+	intval = va_arg(ap, int);
+	snprintf(valuebuf, sizeof(valuebuf), "%i", intval);
+	token = "|A|";
+	break;
+    }
+
+  va_end(ap);
+
+  if (!token)
+    return xtrystrdup(prompt);
+
+  len += strlen(valuebuf)+1;
+  buf = xtrymalloc(len);
+  if (!buf)
+    return NULL;
+
+  buf[0] = 0;
+  if (prepend)
+    strcpy(buf, prepend);
+
+  strcat(buf, prompt);
+
+  if (prepend)
+    p = buf+strlen(prepend);
+  else
+    p = buf;
+
+  tokenp = strstr(p, token);
+  if (!tokenp)
+    return buf;
+
+  p = tokenp+strlen(token);
+  len -= strlen(token)+1;
+  memmove(&buf[len-strlen(p)], p, strlen(p));
+
+  for (n = 0; valuebuf[n]; n++)
+    *tokenp++ = valuebuf[n];
+
+  buf[len] = 0;
+  return buf;
+}
+
+/* Set the prompt shown in the pinentry dialog. If not set then a default will
+ * be used. */
+gpg_error_t app_set_pin_prompt(app_t app, int which, const char *prompt)
+{
+  if (!app)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!app->fnc.set_pin_prompt)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+  return app->fnc.set_pin_prompt(app, which, prompt);
+}
+
 /* Perform a GET CHALLENGE operation.  This fucntion is special as it
    directly accesses the card without any application specific
    wrapper. */
diff --git a/scd/command.c b/scd/command.c
index 88f8ec2..6e6d89b 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -139,6 +139,13 @@ struct server_local_s
      this session.  */
   int stopme;
 
+  /* User-defined pinentry prompt strings. Needed both here and in the app
+   * since they may be defined before an app is selected with
+   * select_application().  They are copied to the app when
+   * select_application() succeeds and further modifications done in the app.
+   * */
+  char *pin_prompt;
+  char *pin_admin_prompt;
 };
 
 
@@ -387,6 +394,39 @@ reset_notify (assuan_context_t ctx, char *line)
   return 0;
 }
 
+static gpg_error_t set_pinentry_prompt(struct server_local_s *srv, int which,
+    const char *prompt)
+{
+  gpg_error_t rc = 0;
+  char **p = NULL;
+
+  switch (which)
+    {
+      case PIN_PROMPT:
+	p = &srv->pin_prompt;
+	break;
+      case PIN_ADMIN_PROMPT:
+	p = &srv->pin_admin_prompt;
+	break;
+      default:
+	  break;
+    }
+
+  if (p)
+    {
+      xfree(*p);
+      *p = NULL;
+
+      if (prompt && *prompt != '-' && *(prompt+1) != 0)
+	{
+	  *p = xtrystrdup(prompt);
+	  if (!*p)
+	    rc = gpg_error_from_syserror();
+	}
+    }
+
+  return rc;
+}
 
 static gpg_error_t
 option_handler (assuan_context_t ctx, const char *key, const char *value)
@@ -407,6 +447,20 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
       ctrl->server_local->event_signal = i;
 #endif
     }
+  else if (!strcmp (key, "pin-prompt"))
+    {
+      if (ctrl->app_ctx)
+	return app_set_pin_prompt(ctrl->app_ctx, PIN_PROMPT, value);
+      else
+	return set_pinentry_prompt(ctrl->server_local, PIN_PROMPT, value);
+    }
+  else if (!strcmp (key, "pin-admin-prompt"))
+    {
+      if (ctrl->app_ctx)
+	return app_set_pin_prompt(ctrl->app_ctx, PIN_ADMIN_PROMPT, value);
+      else
+	return set_pinentry_prompt(ctrl->server_local, PIN_ADMIN_PROMPT, value);
+    }
 
  return 0;
 }
@@ -523,7 +577,17 @@ open_card (ctrl_t ctrl, const char *apptype)
             err = gpg_error (GPG_ERR_CARD);
 	}
       else
-        err = select_application (ctrl, slot, apptype, &ctrl->app_ctx);
+	{
+	  err = select_application (ctrl, slot, apptype, &ctrl->app_ctx);
+	  if (!err)
+	    {
+	      err = app_set_pin_prompt(ctrl->app_ctx, PIN_PROMPT,
+		  ctrl->server_local->pin_prompt);
+	      if (!err)
+		err = app_set_pin_prompt(ctrl->app_ctx, PIN_ADMIN_PROMPT,
+		    ctrl->server_local->pin_admin_prompt);
+	    }
+	}
     }
 
   TEST_CARD_REMOVAL (ctrl, err);
@@ -2097,6 +2161,8 @@ scd_command_handler (ctrl_t ctrl, int fd)
       sl->next_session = ctrl->server_local->next_session;
     }
   stopme = ctrl->server_local->stopme || reader_disabled;
+  xfree(ctrl->server_local->pin_prompt);
+  xfree(ctrl->server_local->pin_admin_prompt);
   xfree (ctrl->server_local);
   ctrl->server_local = NULL;
 
-- 
1.7.7.3




More information about the Gnupg-devel mailing list