aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/agent.h5
-rw-r--r--agent/call-pinentry.c197
-rw-r--r--agent/command.c170
-rw-r--r--agent/genkey.c8
4 files changed, 322 insertions, 58 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 9c84f6a17..7ef0ffca7 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -478,7 +478,8 @@ gpg_error_t agent_askpin (ctrl_t ctrl,
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 agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok,
const char *notokay, int with_cancel);
int agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn);
@@ -515,7 +516,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
membuf_t *outbuf, int *r_padding);
/*-- genkey.c --*/
-int check_passphrase_constraints (ctrl_t ctrl, const char *pw,
+int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty,
char **failed_constraint);
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
char **r_passphrase);
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));
}
diff --git a/agent/command.c b/agent/command.c
index 689507bbc..f54762b44 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1543,9 +1543,22 @@ send_back_passphrase (assuan_context_t ctx, int via_data, const char *pw)
}
+/* Callback function to compare the first entered PIN with the one
+ currently being entered. */
+static gpg_error_t
+reenter_passphrase_cmp_cb (struct pin_entry_info_s *pi)
+{
+ const char *pin1 = pi->check_cb_arg;
+
+ if (!strcmp (pin1, pi->pin))
+ return 0; /* okay */
+ return gpg_error (GPG_ERR_BAD_PASSPHRASE);
+}
+
+
static const char hlp_get_passphrase[] =
"GET_PASSPHRASE [--data] [--check] [--no-ask] [--repeat[=N]]\n"
- " [--qualitybar] <cache_id>\n"
+ " [--qualitybar] [--newsymkey] <cache_id>\n"
" [<error_message> <prompt> <description>]\n"
"\n"
"This function is usually used to ask for a passphrase to be used\n"
@@ -1567,6 +1580,9 @@ static const char hlp_get_passphrase[] =
"cache the user will not be asked to enter a passphrase but the error\n"
"code GPG_ERR_NO_DATA is returned. \n"
"\n"
+ "If the option\"--newsymkey\" is used the agent asks for a new passphrase\n"
+ "to be used in symmetric-only encryption. This must not be empty.\n"
+ "\n"
"If the option \"--qualitybar\" is used a visual indication of the\n"
"entered passphrase quality is shown. (Unless no minimum passphrase\n"
"length has been configured.)";
@@ -1576,13 +1592,19 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
char *pw;
- char *response;
- char *cacheid = NULL, *desc = NULL, *prompt = NULL, *errtext = NULL;
+ char *response = NULL;
+ char *response2 = NULL;
+ char *cacheid = NULL; /* May point into LINE. */
+ char *desc = NULL; /* Ditto */
+ char *prompt = NULL; /* Ditto */
+ char *errtext = NULL; /* Ditto */
const char *desc2 = _("Please re-enter this passphrase");
char *p;
- int opt_data, opt_check, opt_no_ask, opt_qualbar;
+ int opt_data, opt_check, opt_no_ask, opt_qualbar, opt_newsymkey;
int opt_repeat = 0;
char *entry_errtext = NULL;
+ struct pin_entry_info_s *pi = NULL;
+ struct pin_entry_info_s *pi2 = NULL;
if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
@@ -1599,6 +1621,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
opt_repeat = 1;
}
opt_qualbar = has_option (line, "--qualitybar");
+ opt_newsymkey = has_option (line, "--newsymkey");
line = skip_options (line);
cacheid = line;
@@ -1648,26 +1671,116 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
{
rc = send_back_passphrase (ctx, opt_data, pw);
xfree (pw);
+ goto leave;
}
else if (opt_no_ask)
- rc = gpg_error (GPG_ERR_NO_DATA);
- else
{
- /* Note, that we only need to replace the + characters and
- should leave the other escaping in place because the escaped
- string is send verbatim to the pinentry which does the
- unescaping (but not the + replacing) */
- if (errtext)
- plus_to_blank (errtext);
- if (prompt)
- plus_to_blank (prompt);
- if (desc)
- plus_to_blank (desc);
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ goto leave;
+ }
+
+ /* Note, that we only need to replace the + characters and should
+ * leave the other escaping in place because the escaped string is
+ * send verbatim to the pinentry which does the unescaping (but not
+ * the + replacing) */
+ if (errtext)
+ plus_to_blank (errtext);
+ if (prompt)
+ plus_to_blank (prompt);
+ if (desc)
+ plus_to_blank (desc);
+ if (opt_newsymkey)
+ {
+ /* We do not want to break any existing usage of this command
+ * and thus we introduced the option --newsymkey to make this
+ * command more useful to query the passphrase for symmetric
+ * encryption. */
+ pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
+ if (!pi)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ pi2 = gcry_calloc_secure (1, sizeof (*pi2) + MAX_PASSPHRASE_LEN + 1);
+ if (!pi2)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ pi->max_length = MAX_PASSPHRASE_LEN + 1;
+ pi->max_tries = 3;
+ pi->with_qualitybar = opt_qualbar;
+ pi->with_repeat = opt_repeat;
+ pi2->max_length = MAX_PASSPHRASE_LEN + 1;
+ pi2->max_tries = 3;
+ pi2->check_cb = reenter_passphrase_cmp_cb;
+ pi2->check_cb_arg = pi->pin;
+
+ for (;;) /* (degenerated for-loop) */
+ {
+ xfree (response);
+ response = NULL;
+ rc = agent_get_passphrase (ctrl, &response,
+ desc,
+ prompt,
+ entry_errtext? entry_errtext:errtext,
+ opt_qualbar, cacheid, CACHE_MODE_USER,
+ pi);
+ if (rc)
+ goto leave;
+ xfree (entry_errtext);
+ entry_errtext = NULL;
+ /* We don't allow an empty passpharse in this mode. */
+ if (check_passphrase_constraints (ctrl, pi->pin, 1, &entry_errtext))
+ {
+ pi->failed_tries = 0;
+ pi2->failed_tries = 0;
+ continue;
+ }
+ if (*pi->pin && !pi->repeat_okay)
+ {
+ /* The passphrase is empty and the pinentry did not
+ * already run the repetition check, do it here. This
+ * is only called when using an old and simple pinentry. */
+ xfree (response);
+ response = NULL;
+ rc = agent_get_passphrase (ctrl, &response,
+ L_("Please re-enter this passphrase"),
+ prompt,
+ entry_errtext? entry_errtext:errtext,
+ opt_qualbar, cacheid, CACHE_MODE_USER,
+ pi2);
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE)
+ { /* The re-entered passphrase one did not match and
+ * the user did not hit cancel. */
+ entry_errtext = xtrystrdup (L_("does not match - try again"));
+ if (!entry_errtext)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ continue;
+ }
+ }
+ break;
+ }
+ if (!rc && *pi->pin)
+ {
+ /* Return the passphrase. */
+ if (cacheid)
+ agent_put_cache (ctrl, cacheid, CACHE_MODE_USER, pi->pin, 0);
+ rc = send_back_passphrase (ctx, opt_data, pi->pin);
+ }
+ }
+ else
+ {
next_try:
+ xfree (response);
+ response = NULL;
rc = agent_get_passphrase (ctrl, &response, desc, prompt,
entry_errtext? entry_errtext:errtext,
- opt_qualbar, cacheid, CACHE_MODE_USER);
+ opt_qualbar, cacheid, CACHE_MODE_USER, NULL);
xfree (entry_errtext);
entry_errtext = NULL;
if (!rc)
@@ -1675,27 +1788,24 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
int i;
if (opt_check
- && check_passphrase_constraints (ctrl, response, &entry_errtext))
+ && check_passphrase_constraints (ctrl, response,0,&entry_errtext))
{
- xfree (response);
goto next_try;
}
for (i = 0; i < opt_repeat; i++)
{
- char *response2;
-
if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
break;
+ xfree (response2);
+ response2 = NULL;
rc = agent_get_passphrase (ctrl, &response2, desc2, prompt,
errtext, 0,
- cacheid, CACHE_MODE_USER);
+ cacheid, CACHE_MODE_USER, NULL);
if (rc)
break;
if (strcmp (response2, response))
{
- xfree (response2);
- xfree (response);
entry_errtext = try_percent_escape
(_("does not match - try again"), NULL);
if (!entry_errtext)
@@ -1705,7 +1815,6 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
}
goto next_try;
}
- xfree (response2);
}
if (!rc)
{
@@ -1713,10 +1822,15 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
agent_put_cache (ctrl, cacheid, CACHE_MODE_USER, response, 0);
rc = send_back_passphrase (ctx, opt_data, response);
}
- xfree (response);
}
}
+ leave:
+ xfree (response);
+ xfree (response2);
+ xfree (entry_errtext);
+ xfree (pi2);
+ xfree (pi);
return leave_cmd (ctx, rc);
}
@@ -3555,7 +3669,9 @@ command_has_option (const char *cmd, const char *cmdopt)
if (!strcmp (cmd, "GET_PASSPHRASE"))
{
if (!strcmp (cmdopt, "repeat"))
- return 1;
+ return 1;
+ if (!strcmp (cmdopt, "newsymkey"))
+ return 1;
}
return 0;
diff --git a/agent/genkey.c b/agent/genkey.c
index 0d2038016..6f863db95 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -161,7 +161,7 @@ take_this_one_anyway (ctrl_t ctrl, const char *desc, const char *anyway_btn)
message describing the problem is returned in
*FAILED_CONSTRAINT. */
int
-check_passphrase_constraints (ctrl_t ctrl, const char *pw,
+check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty,
char **failed_constraint)
{
gpg_error_t err = 0;
@@ -180,7 +180,7 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw,
/* The first check is to warn about an empty passphrase. */
if (!*pw)
{
- const char *desc = (opt.enforce_passphrase_constraints?
+ const char *desc = (opt.enforce_passphrase_constraints || no_empty?
L_("You have not entered a passphrase!%0A"
"An empty passphrase is not allowed.") :
L_("You have not entered a passphrase - "
@@ -191,7 +191,7 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw,
err = 1;
if (failed_constraint)
{
- if (opt.enforce_passphrase_constraints)
+ if (opt.enforce_passphrase_constraints || no_empty)
*failed_constraint = xstrdup (desc);
else
err = take_this_one_anyway (ctrl, desc,
@@ -381,7 +381,7 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
initial_errtext = NULL;
if (!err)
{
- if (check_passphrase_constraints (ctrl, pi->pin, &initial_errtext))
+ if (check_passphrase_constraints (ctrl, pi->pin, 0, &initial_errtext))
{
pi->failed_tries = 0;
pi2->failed_tries = 0;