diff options
author | Werner Koch <[email protected]> | 2020-07-08 12:20:01 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2020-07-08 12:20:01 +0000 |
commit | eace4bbe1ded8b01f9ad52ebc1871f2fd13c3a08 (patch) | |
tree | e4877d2ff6e5b18bffa088539a261a8de91d4563 /agent/call-pinentry.c | |
parent | gpg: Fix flaw in symmetric algorithm selection in mixed mode. (diff) | |
download | gnupg-eace4bbe1ded8b01f9ad52ebc1871f2fd13c3a08.tar.gz gnupg-eace4bbe1ded8b01f9ad52ebc1871f2fd13c3a08.zip |
agent: New option --newsymkey for GET_PASSPHRASE
* agent/call-pinentry.c (agent_get_passphrase): Add arg pininfo.
* agent/genkey.c (check_passphrase_constraints): New arg no_empty.
* agent/command.c (reenter_passphrase_cmp_cb): New.
(cmd_get_passphrase): Add option --newsymkey.
--
This new option allows to present a passphrase with the usual repeat
box as it is used by gpg-agent's internal key generation.
Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'agent/call-pinentry.c')
-rw-r--r-- | agent/call-pinentry.c | 197 |
1 files changed, 172 insertions, 25 deletions
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 658be4611..89249b6e8 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -849,7 +849,7 @@ inq_quality (void *opaque, const char *line) else { percent = estimate_passphrase_quality (pin); - if (check_passphrase_constraints (NULL, pin, NULL)) + if (check_passphrase_constraints (NULL, pin, 0, NULL)) percent = -percent; snprintf (numbuf, sizeof numbuf, "%d", percent); rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); @@ -1301,9 +1301,11 @@ agent_askpin (ctrl_t ctrl, } if ((pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE)) - /* The password was read from the cache. Don't count this - against the retry count. */ - pininfo->failed_tries --; + { + /* The password was read from the cache. Don't count this + against the retry count. */ + pininfo->failed_tries --; + } } return unlock_pinentry (ctrl, gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN @@ -1313,14 +1315,20 @@ agent_askpin (ctrl_t ctrl, /* Ask for the passphrase using the supplied arguments. The returned - passphrase needs to be freed by the caller. */ + passphrase needs to be freed by the caller. PININFO is optional + and can be used to have constraints checinkg while the pinentry + dialog is open (like what we do in agent_askpin). This is very + similar to agent_akpin and we should eventually merge the two + functions. */ int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, const char *errtext, int with_qualitybar, - const char *keyinfo, cache_mode_t cache_mode) + const char *keyinfo, cache_mode_t cache_mode, + struct pin_entry_info_s *pininfo) { int rc; + int is_pin; char line[ASSUAN_LINELENGTH]; struct entry_parm_s parm; @@ -1330,17 +1338,42 @@ agent_get_passphrase (ctrl_t ctrl, if (ctrl->pinentry_mode != PINENTRY_MODE_ASK) { + unsigned char *passphrase; + size_t size; + if (ctrl->pinentry_mode == PINENTRY_MODE_CANCEL) return gpg_error (GPG_ERR_CANCELED); - if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK && pininfo) { - size_t size; + *pininfo->pin = 0; /* Reset the PIN. */ + rc = pinentry_loopback (ctrl, "PASSPHRASE", + &passphrase, &size, + pininfo->max_length - 1); + if (rc) + return rc; + + memcpy (&pininfo->pin, passphrase, size); + wipememory (passphrase, size); + xfree (passphrase); + pininfo->pin[size] = 0; + if (pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + rc = pininfo->check_cb (pininfo); + } + return rc; + } + else if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) + { + /* Legacy variant w/o PININFO. */ return pinentry_loopback (ctrl, "PASSPHRASE", (unsigned char **)retpass, &size, MAX_PASSPHRASE_LEN); } + return gpg_error (GPG_ERR_NO_PIN_ENTRY); } @@ -1348,9 +1381,14 @@ agent_get_passphrase (ctrl_t ctrl, if (rc) return rc; - if (!prompt) - prompt = desc && strstr (desc, "PIN")? L_("PIN:"): L_("Passphrase:"); - + /* Set IS_PIN and if needed a default prompt. */ + if (prompt) + is_pin = !!strstr (prompt, "PIN"); + else + { + is_pin = desc && strstr (desc, "PIN"); + prompt = is_pin? L_("PIN:"): L_("Passphrase:"); + } /* If we have a KEYINFO string and are normal, user, or ssh cache mode, we tell that the Pinentry so it may use it for own caching @@ -1371,7 +1409,6 @@ agent_get_passphrase (ctrl_t ctrl, if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD) return unlock_pinentry (ctrl, rc); - if (desc) build_cmd_setdesc (line, DIM(line), desc); else @@ -1385,7 +1422,8 @@ agent_get_passphrase (ctrl_t ctrl, if (rc) return unlock_pinentry (ctrl, rc); - if (with_qualitybar && opt.min_passphrase_len) + if ((with_qualitybar || (pininfo && pininfo->with_qualitybar)) + && opt.min_passphrase_len) { rc = setup_qualitybar (ctrl); if (rc) @@ -1395,23 +1433,132 @@ agent_get_passphrase (ctrl_t ctrl, if (errtext) { snprintf (line, DIM(line), "SETERROR %s", errtext); - rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return unlock_pinentry (ctrl, rc); } - memset (&parm, 0, sizeof parm); - parm.size = ASSUAN_LINELENGTH/2 - 5; - parm.buffer = gcry_malloc_secure (parm.size+10); - if (!parm.buffer) - return unlock_pinentry (ctrl, out_of_core ()); + if (!pininfo) + { + /* Legacy method without PININFO. */ + memset (&parm, 0, sizeof parm); + parm.size = ASSUAN_LINELENGTH/2 - 5; + parm.buffer = gcry_malloc_secure (parm.size+10); + if (!parm.buffer) + return unlock_pinentry (ctrl, out_of_core ()); - rc = do_getpin (ctrl, &parm); - if (rc) - xfree (parm.buffer); - else - *retpass = parm.buffer; - return unlock_pinentry (ctrl, rc); + rc = do_getpin (ctrl, &parm); + if (rc) + xfree (parm.buffer); + else + *retpass = parm.buffer; + return unlock_pinentry (ctrl, rc); + } + + /* We got PININFO. */ + + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEATERROR %s", + L_("does not match - try again")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + pininfo->with_repeat = 0; /* Pinentry does not support it. */ + } + pininfo->repeat_okay = 0; + pininfo->status = 0; + + for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++) + { + memset (&parm, 0, sizeof parm); + parm.size = pininfo->max_length; + parm.buffer = (unsigned char*)pininfo->pin; + *pininfo->pin = 0; /* Reset the PIN. */ + + if (errtext) + { + /* TRANSLATORS: The string is appended to an error message in + the pinentry. The %s is the actual error message, the + two %d give the current and maximum number of tries. */ + snprintf (line, DIM(line), L_("SETERROR %s (try %d of %d)"), + errtext, pininfo->failed_tries+1, pininfo->max_tries); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (ctrl, rc); + errtext = NULL; + } + + if (pininfo->with_repeat) + { + snprintf (line, DIM(line), "SETREPEAT %s", L_("Repeat:")); + rc = assuan_transact (entry_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (ctrl, rc); + } + + rc = do_getpin (ctrl, &parm); + pininfo->status = parm.status; + if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA) + errtext = is_pin? L_("PIN too long") + : L_("Passphrase too long"); + else if (rc) + return unlock_pinentry (ctrl, rc); + + if (!errtext && pininfo->min_digits) + { + /* do some basic checks on the entered PIN. */ + if (!all_digitsp (pininfo->pin)) + errtext = L_("Invalid characters in PIN"); + else if (pininfo->max_digits + && strlen (pininfo->pin) > pininfo->max_digits) + errtext = L_("PIN too long"); + else if (strlen (pininfo->pin) < pininfo->min_digits) + errtext = L_("PIN too short"); + } + + if (!errtext && pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + rc = pininfo->check_cb (pininfo); + /* When pinentry cache causes an error, return now. */ + if (rc && (pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE)) + return unlock_pinentry (ctrl, rc); + + if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE) + { + if (pininfo->cb_errtext) + errtext = pininfo->cb_errtext; + else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE + || gpg_err_code (rc) == GPG_ERR_BAD_PIN) + errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase")); + } + else if (rc) + return unlock_pinentry (ctrl, rc); + } + + if (!errtext) + { + if (pininfo->with_repeat + && (pininfo->status & PINENTRY_STATUS_PIN_REPEATED)) + pininfo->repeat_okay = 1; + return unlock_pinentry (ctrl, 0); /* okay, got a PIN or passphrase */ + } + + if ((pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE)) + { + /* The password was read from the Pinentry's own cache. + Don't count this against the retry count. */ + pininfo->failed_tries--; + } + } + + return unlock_pinentry (ctrl, gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN + : GPG_ERR_BAD_PASSPHRASE)); } |