diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | agent/divert-scd.c | 12 | ||||
-rw-r--r-- | scd/app-common.h | 10 | ||||
-rw-r--r-- | scd/app-openpgp.c | 109 | ||||
-rw-r--r-- | scd/app.c | 125 | ||||
-rw-r--r-- | scd/command.c | 70 |
6 files changed, 313 insertions, 15 deletions
@@ -4,6 +4,8 @@ Noteworthy changes in version 2.1.0beta4 (unreleased) * GPG now accepts a space separated fingerprint as a user ID. This allows to copy and paste the fingerprint from the key listing. + * New option for SCDAEMON to set a user defined pinentry prompt. + Noteworthy changes in version 2.1.0beta3 (2011-12-20) ----------------------------------------------------- diff --git a/agent/divert-scd.c b/agent/divert-scd.c index f176a6b94..a2de217bd 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 e3d23c2b4..d95493262 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -34,6 +34,12 @@ #define APP_CHANGE_FLAG_RESET 1 #define APP_CHANGE_FLAG_NULLPIN 2 +/* For user defined pinentry prompts. */ +enum { + PIN_PROMPT_NONE = -1, + PIN_SIGN_PROMPT, + PIN_ADMIN_PROMPT, +}; struct app_local_s; /* Defined by all app-*.c. */ @@ -119,6 +125,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 +199,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 +209,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 e3a448413..ea3e52e6b 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_SIGN_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|", + PIN_PROMPT_NONE, 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|", + PIN_PROMPT_NONE, 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|", PIN_PROMPT_NONE, 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,40 @@ 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_SIGN_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 +3934,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: @@ -922,6 +922,131 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, return err; } +/* For use with user-defined pinentry prompts that are set with the OPTION + * command. This function is called from an application before a pinentry is + * invoked. It replaces special escape strings in the user-defined 'prompt' + * with a single (for now) value set by the calling function. What data type + * the vararg value is depends on the 'which' parameter and is set in the + * calling function. + * + * The 'prepend' parameter are any pinentry flags to be prepended to the + * expanded prompt. These flags, if any, are not set by the user-defined + * prompt but the calling function. In most cases it will be NULL or "|I|". + * + * The following expandos are recognized: + * + * expando | which | description + * ---------------------------------------- + * PIN_PROMPT_NONE NOP. The user-defined prompt is returned along + * with any 'prepend' parameter. + * |S| PIN_SIGN_PROMPT signature count (unsigned long) + * |A| PIN_ADMIN_PROMPT remaining attempts (int) + * + * The following example shows the default pinentry prompt when no + * user-defined prompt is set: + * + * "Please enter the PIN%%0A[sigs done: |S|]" + * + * Here, |S| is expanded to the number of signatures created so far. + */ +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_SIGN_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; + default: + break; + } + + va_end (ap); + + if (!token) + { + if (prepend) + { + len = strlen (prepend)+strlen (prompt)+1; + p = xtrymalloc (len); + if (!p) + return NULL; + + snprintf (p, len, "%s%s", prepend, prompt); + return p; + } + + 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 diff --git a/scd/command.c b/scd/command.c index 88f8ec2c9..2704e57b7 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 set by the user 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_SIGN_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,22 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) ctrl->server_local->event_signal = i; #endif } + /* A user-defined prompt to show in a pinentry overriding the default. See + * app.c:expand_pin_prompt() for details. */ + else if (!strcmp (key, "pin-prompt")) + { + if (ctrl->app_ctx) + return app_set_pin_prompt (ctrl->app_ctx, PIN_SIGN_PROMPT, value); + else + return set_pinentry_prompt (ctrl->server_local, PIN_SIGN_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 +579,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_SIGN_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 +2163,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; |