agent: Backport changes from 2.1 to support an external password manager.

* agent/agent.h (agent_askpin): Add arguments keyinfo and cache_mode.
Update callers.
(agent_get_passphrase): Likewise.
(agent_clear_passphrase): New function.
(opt): Add field allow_external_cache.
* agent/call-pinentry.c (start_pinentry): Send "OPTION
allow-external-password-cache" to the pinentry.
(PINENTRY_STATUS_PASSWORD_FROM_CACHE): New constant.
(pinentry_status_cb): New function.
(agent_askpin): Add arguments keyinfo and cache_mode.  If KEYINFO and
CACHE_MODE describe a cachable key, then send SETKEYINFO to the
pinentry.  Pass PINENTRY_STATUS_CB to the "GETPIN" invocation.  If the
passphrase was incorrect and PINENTRY_STATUS_PASSWORD_FROM_CACHE is
set, decrement PININFO->FAILED_TRIES.
(agent_get_passphrase): Add arguments keyinfo and cache_mode.  If
KEYINFO and CACHE_MODE describe a cachable key, then send SETKEYINFO
to the pinentry.
(agent_clear_passphrase): New function.
* agent/call-pinentry.c (start_pinentry): Act upon new var,
allow_external_cache.
* agent/command.c (cmd_clear_passphrase): Call agent_clear_passphrase.
* agent/gpg-agent.c (oNoAllowExternalCache): New.
(opts): Add option --no-allow-external-cache.
(parse_rereadable_options): Set this option.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>

Based on commits:

3a9305439b
e201c20f25
d7293cb317
56b5c9f94f
d3b5cad234
2180845959
This commit is contained in:
Neal H. Walfield 2015-05-19 13:53:43 +02:00
parent 80b6d614b7
commit dde8ddffd3
10 changed files with 186 additions and 21 deletions

View File

@ -104,6 +104,12 @@ struct
int ignore_cache_for_signing;
int allow_mark_trusted;
int allow_preset_passphrase;
/* Allow the use of an external password cache. If this option is
enabled (which is the default) we send an option to Pinentry
to allow it to enable such a cache. */
int allow_external_cache;
int keep_tty; /* Don't switch the TTY (for pinentry) on request */
int keep_display; /* Don't switch the DISPLAY (for pinentry) on request */
int ssh_support; /* Enable ssh-agent emulation. */
@ -273,16 +279,20 @@ int pinentry_active_p (ctrl_t ctrl, int waitseconds);
int agent_askpin (ctrl_t ctrl,
const char *desc_text, const char *prompt_text,
const char *inital_errtext,
struct pin_entry_info_s *pininfo);
struct pin_entry_info_s *pininfo,
const char *keyinfo, cache_mode_t cache_mode);
int agent_get_passphrase (ctrl_t ctrl, char **retpass,
const char *desc, const char *prompt,
const char *errtext, int with_qualitybar);
const char *errtext, int with_qualitybar,
const char *keyinfo, cache_mode_t cache_mode);
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);
int agent_popup_message_start (ctrl_t ctrl,
const char *desc, const char *ok_btn);
void agent_popup_message_stop (ctrl_t ctrl);
int agent_clear_passphrase (ctrl_t ctrl,
const char *keyinfo, cache_mode_t cache_mode);
/*-- cache.c --*/

View File

@ -352,6 +352,19 @@ start_pinentry (ctrl_t ctrl)
if (rc)
return unlock_pinentry (rc);
/* Indicate to the pinentry that it may read from an external cache.
It is essential that the pinentry respect this. If the cached
password is not up to date and retry == 1, then, using a version
of GPG Agent that doesn't support this, won't issue another pin
request and the user won't get a chance to correct the
password. */
rc = assuan_transact (entry_ctx, "OPTION allow-external-password-cache",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
return unlock_pinentry (rc);
value = session_env_getenv (ctrl->session_env, "GPG_TTY");
if (value)
{
@ -399,6 +412,22 @@ start_pinentry (ctrl_t ctrl)
return unlock_pinentry (rc);
}
if (opt.allow_external_cache)
{
/* Indicate to the pinentry that it may read from an external cache.
It is essential that the pinentry respect this. If the
cached password is not up to date and retry == 1, then, using
a version of GPG Agent that doesn't support this, won't issue
another pin request and the user won't get a chance to
correct the password. */
rc = assuan_transact (entry_ctx, "OPTION allow-external-password-cache",
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_UNKNOWN_OPTION)
return unlock_pinentry (rc);
}
{
/* Provide a few default strings for use by the pinentries. This
may help a pinentry to avoid implementing localization code. */
@ -411,6 +440,7 @@ start_pinentry (ctrl_t ctrl)
{ "ok", N_("|pinentry-label|_OK") },
{ "cancel", N_("|pinentry-label|_Cancel") },
{ "prompt", N_("|pinentry-label|PIN:") },
{ "pwmngr", N_("|pinentry-label|_Save in password manager") },
{ NULL, NULL}
};
char *optstr;
@ -700,15 +730,36 @@ setup_qualitybar (void)
}
enum
{
PINENTRY_STATUS_PASSWORD_FROM_CACHE = 1 << 9
};
/* Check the button_info line for a close action. Also check for the
PIN_REPEATED flag. */
static gpg_error_t
pinentry_status_cb (void *opaque, const char *line)
{
unsigned int *flag = opaque;
if (strcmp (line, "PASSWORD_FROM_CACHE") == 0)
{
*flag |= PINENTRY_STATUS_PASSWORD_FROM_CACHE;
}
return 0;
}
/* 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
numbers. */
numbers. KEYINFO and CACHEMODE are used to tell pinentry something
about the key. */
int
agent_askpin (ctrl_t ctrl,
const char *desc_text, const char *prompt_text,
const char *initial_errtext,
struct pin_entry_info_s *pininfo)
struct pin_entry_info_s *pininfo,
const char *keyinfo, cache_mode_t cache_mode)
{
int rc;
char line[ASSUAN_LINELENGTH];
@ -716,6 +767,7 @@ agent_askpin (ctrl_t ctrl,
const char *errtext = NULL;
int is_pin = 0;
int saveflag;
unsigned int pinentry_status;
if (opt.batch)
return 0; /* fixme: we should return BAD PIN */
@ -738,6 +790,25 @@ agent_askpin (ctrl_t ctrl,
if (rc)
return rc;
/* 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
purposes. Most pinentries won't have this implemented and thus
we do not error out in this case. */
if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH))
snprintf (line, DIM(line)-1, "SETKEYINFO %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
else
snprintf (line, DIM(line)-1, "SETKEYINFO --clear");
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD)
return unlock_pinentry (rc);
snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
@ -792,11 +863,13 @@ agent_askpin (ctrl_t ctrl,
return unlock_pinentry (rc);
errtext = NULL;
}
saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
assuan_begin_confidential (entry_ctx);
pinentry_status = 0;
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
inq_quality, entry_ctx, NULL, NULL);
inq_quality, entry_ctx,
pinentry_status_cb, &pinentry_status);
assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
/* Most pinentries out in the wild return the old Assuan error code
for canceled which gets translated to an assuan Cancel error and
@ -840,6 +913,11 @@ agent_askpin (ctrl_t ctrl,
if (!errtext)
return unlock_pinentry (0); /* okay, got a PIN or passphrase */
if ((pinentry_status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
/* The password was read from the cache. Don't count this
against the retry count. */
pininfo->failed_tries --;
}
return unlock_pinentry (gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN
@ -849,11 +927,12 @@ 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. */
int
agent_get_passphrase (ctrl_t ctrl,
char **retpass, const char *desc, const char *prompt,
const char *errtext, int with_qualitybar)
const char *errtext, int with_qualitybar,
const char *keyinfo, cache_mode_t cache_mode)
{
int rc;
@ -873,6 +952,26 @@ agent_get_passphrase (ctrl_t ctrl,
prompt = desc && strstr (desc, "PIN")? "PIN": _("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
purposes. Most pinentries won't have this implemented and thus
we do not error out in this case. */
if (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH))
snprintf (line, DIM(line)-1, "SETKEYINFO %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
else
snprintf (line, DIM(line)-1, "SETKEYINFO --clear");
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
if (rc && gpg_err_code (rc) != GPG_ERR_ASS_UNKNOWN_CMD)
return unlock_pinentry (rc);
if (desc)
snprintf (line, DIM(line)-1, "SETDESC %s", desc);
else
@ -1185,3 +1284,28 @@ agent_popup_message_stop (ctrl_t ctrl)
}
int
agent_clear_passphrase (ctrl_t ctrl,
const char *keyinfo, cache_mode_t cache_mode)
{
int rc;
char line[ASSUAN_LINELENGTH];
if (! (keyinfo && (cache_mode == CACHE_MODE_NORMAL
|| cache_mode == CACHE_MODE_USER
|| cache_mode == CACHE_MODE_SSH)))
return gpg_error (GPG_ERR_NOT_SUPPORTED);
rc = start_pinentry (ctrl);
if (rc)
return rc;
snprintf (line, DIM(line)-1, "CLEARPASSPHRASE %c/%s",
cache_mode == CACHE_MODE_USER? 'u' :
cache_mode == CACHE_MODE_SSH? 's' : 'n',
keyinfo);
rc = assuan_transact (entry_ctx, line,
NULL, NULL, NULL, NULL, NULL, NULL);
return unlock_pinentry (rc);
}

View File

@ -2881,7 +2881,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl, int confirm)
pi2->check_cb_arg = pi->pin;
next_try:
err = agent_askpin (ctrl, description, NULL, initial_errtext, pi);
err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL, 0);
initial_errtext = NULL;
if (err)
goto out;
@ -2889,7 +2889,7 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl, int confirm)
/* Unless the passphrase is empty, ask to confirm it. */
if (pi->pin && *pi->pin)
{
err = agent_askpin (ctrl, description2, NULL, NULL, pi2);
err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL, 0);
if (err == -1)
{ /* The re-entered one did not match and the user did not
hit cancel. */

View File

@ -1269,8 +1269,8 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
next_try:
rc = agent_get_passphrase (ctrl, &response, desc, prompt,
repeat_errtext? repeat_errtext:errtext,
opt_qualbar);
repeat_errtext? repeat_errtext:errtext,
opt_qualbar, cacheid, CACHE_MODE_USER);
xfree (repeat_errtext);
repeat_errtext = NULL;
if (!rc)
@ -1287,7 +1287,8 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
char *response2;
rc = agent_get_passphrase (ctrl, &response2, desc2, prompt,
errtext, 0);
errtext, 0,
cacheid, CACHE_MODE_USER);
if (rc)
break;
if (strcmp (response2, response))
@ -1329,6 +1330,7 @@ static const char hlp_clear_passphrase[] =
static gpg_error_t
cmd_clear_passphrase (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
char *cacheid = NULL;
char *p;
@ -1343,6 +1345,9 @@ cmd_clear_passphrase (assuan_context_t ctx, char *line)
return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of cacheID");
agent_put_cache (cacheid, CACHE_MODE_USER, NULL, 0);
agent_clear_passphrase (ctrl, cacheid, CACHE_MODE_USER);
return 0;
}

View File

@ -266,7 +266,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
if (any_flags)
{
rc = agent_askpin (ctrl, info, prompt, again_text, pi);
rc = agent_askpin (ctrl, info, prompt, again_text, pi, NULL, 0);
again_text = NULL;
if (!rc && newpin)
{
@ -288,7 +288,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
is_puk?
_("Repeat this PUK"):
_("Repeat this PIN")),
prompt, NULL, pi2);
prompt, NULL, pi2, NULL, 0);
if (!rc && strcmp (pi->pin, pi2->pin))
{
again_text = (resetcode?
@ -312,7 +312,7 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
info? info:"",
info? "')":"") < 0)
desc = NULL;
rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi);
rc = agent_askpin (ctrl, desc?desc:info, prompt, NULL, pi, NULL, 0);
xfree (desc);
}

View File

@ -393,7 +393,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
arg.change_required = 0;
pi->check_cb_arg = &arg;
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi, hexgrip, cache_mode);
if (!rc)
{
assert (arg.unprotected_key);

View File

@ -321,7 +321,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
pi2->check_cb_arg = pi->pin;
next_try:
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, NULL, 0);
initial_errtext = NULL;
if (!rc)
{
@ -333,7 +333,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
}
if (pi->pin && *pi->pin)
{
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL, 0);
if (rc == -1)
{ /* The re-entered one did not match and the user did not
hit cancel. */
@ -443,7 +443,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
pi2->check_cb_arg = pi->pin;
next_try:
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi, NULL, 0);
initial_errtext = NULL;
if (!rc)
{
@ -456,7 +456,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
/* Unless the passphrase is empty, ask to confirm it. */
if (pi->pin && *pi->pin)
{
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
rc = agent_askpin (ctrl, text2, NULL, NULL, pi2, NULL, 0);
if (rc == -1)
{ /* The re-entered one did not match and the user did not
hit cancel. */

View File

@ -114,6 +114,7 @@ enum cmd_and_opt_values
oAllowMarkTrusted,
oNoAllowMarkTrusted,
oAllowPresetPassphrase,
oNoAllowExternalCache,
oKeepTTY,
oKeepDISPLAY,
oSSHSupport,
@ -198,6 +199,8 @@ static ARGPARSE_OPTS opts[] = {
"@"
#endif
},
{ oNoAllowExternalCache, "no-allow-external-cache", 0,
N_("disallow the use of an external password cache") },
{ oWriteEnvFile, "write-env-file", 2|8,
N_("|FILE|write environment settings also to FILE")},
{0}
@ -509,6 +512,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 1;
opt.disable_scdaemon = 0;
opt.allow_external_cache = 1;
return 1;
}
@ -571,6 +575,9 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
case oNoAllowExternalCache: opt.allow_external_cache = 0;
break;
default:
return 0; /* not handled */
}
@ -969,6 +976,8 @@ main (int argc, char **argv )
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("no-allow-mark-trusted:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("no-allow-external-cache:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("disable-scdaemon:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
#ifdef HAVE_W32_SYSTEM

View File

@ -352,6 +352,19 @@ Allow clients to use the loopback pinentry features; see the option
@option{pinentry-mode} for details.
@end ifset
@ifset gpgtwoone
@item --no-allow-external-cache
@opindex no-allow-external-cache
Tell Pinentry not to enable features which use an external cache for
passphrases.
Some desktop environments prefer to unlock all
credentials with one master password and may have installed a Pinentry
which employs an additional external cache to implement such a policy.
By using this option the Pinentry is advised not to make use of such a
cache and instead always ask the user for the requested passphrase.
@end ifset
@item --ignore-cache-for-signing
@opindex ignore-cache-for-signing
This option will let @command{gpg-agent} bypass the passphrase cache for all
@ -713,6 +726,7 @@ again. Only certain options are honored: @code{quiet},
@code{verbose}, @code{debug}, @code{debug-all}, @code{debug-level},
@code{no-grab}, @code{pinentry-program}, @code{default-cache-ttl},
@code{max-cache-ttl}, @code{ignore-cache-for-signing},
@code{no-allow-external-cache},
@code{allow-mark-trusted}, @code{disable-scdaemon}, and
@code{disable-check-own-socket}. @code{scdaemon-program} is also
supported but due to the current implementation, which calls the

View File

@ -532,6 +532,9 @@ static gc_option_t gc_options_gpg_agent[] =
{ "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_BASIC, "gnupg", "do not use the PIN cache when signing",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "no-allow-external-cache", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_BASIC, "gnupg", "disallow the use of an external password cache",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
{ "no-allow-mark-trusted", GC_OPT_FLAG_RUNTIME,
GC_LEVEL_ADVANCED, "gnupg", "disallow clients to mark keys as \"trusted\"",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },