aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/agent.h14
-rw-r--r--agent/call-pinentry.c136
-rw-r--r--agent/command-ssh.c4
-rw-r--r--agent/command.c11
-rw-r--r--agent/divert-scd.c6
-rw-r--r--agent/findkey.c2
-rw-r--r--agent/genkey.c8
-rw-r--r--agent/gpg-agent.c9
-rw-r--r--doc/gpg-agent.texi14
-rw-r--r--tools/gpgconf-comp.c3
10 files changed, 186 insertions, 21 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 938a9aa0f..f81743f2f 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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 --*/
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
index c945c1305..75e1b238c 100644
--- a/agent/call-pinentry.c
+++ b/agent/call-pinentry.c
@@ -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);
+}
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index ea6080ae7..2aacecc46 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -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. */
diff --git a/agent/command.c b/agent/command.c
index 2405c54e9..765f916b3 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -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;
}
diff --git a/agent/divert-scd.c b/agent/divert-scd.c
index 1f36f6e98..34ef498fc 100644
--- a/agent/divert-scd.c
+++ b/agent/divert-scd.c
@@ -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);
}
diff --git a/agent/findkey.c b/agent/findkey.c
index 550e40319..6d85cfdf7 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -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);
diff --git a/agent/genkey.c b/agent/genkey.c
index d5af9e06d..65477adf2 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -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. */
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index bf2a26d47..479f91802 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -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
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index c3dfd82b7..5c0dec773 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -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
diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c
index 499398943..2454f9326 100644
--- a/tools/gpgconf-comp.c
+++ b/tools/gpgconf-comp.c
@@ -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 },