diff --git a/agent/ChangeLog b/agent/ChangeLog index 7c948e4c1..9a07219a4 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,22 @@ +2009-05-15 Werner Koch + + Fix bug #1053. + + * agent.h (lookup_ttl_t): New. + * findkey.c (unprotect): Add arg LOOKUP_TTL. + (agent_key_from_file): Ditto. + * pksign.c (agent_pksign_do): Ditto. + * command-ssh.c (ttl_from_sshcontrol): New. + (data_sign): Pass new function to agent_pksign_do. + (search_control_file): Add new arg R_TTL. + +2009-05-14 Werner Koch + + * command.c (cmd_get_passphrase): Add option --qualitybar. + * call-pinentry.c (agent_askpin): Factor some code out to ... + (setup_qualitybar): .. new. + (agent_get_passphrase): Add arg WITH_QUALITYBAR and implement it. + 2009-04-14 Marcus Brinkmann * call-pinentry.c (agent_get_confirmation): Try SETNOTOK command diff --git a/agent/agent.h b/agent/agent.h index ba31d9396..e37485fcc 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -201,6 +201,10 @@ typedef enum cache_mode_t; +/* The type of a function to lookup a TTL by a keygrip. */ +typedef int (*lookup_ttl_t)(const char *hexgrip); + + /*-- gpg-agent.c --*/ void agent_exit (int rc) JNLIB_GCC_A_NR; /* Also implemented in other tools */ const char *get_agent_socket_name (void); @@ -229,6 +233,7 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, + lookup_ttl_t lookup_ttl, gcry_sexp_t *result); gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, @@ -249,7 +254,7 @@ int agent_askpin (ctrl_t ctrl, struct pin_entry_info_s *pininfo); int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, - const char *errtext); + const char *errtext, int with_qualitybar); int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, const char *cancel); int agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn); @@ -270,7 +275,7 @@ void agent_unlock_cache_entry (void **cache_id); /*-- pksign.c --*/ int agent_pksign_do (ctrl_t ctrl, const char *desc_text, gcry_sexp_t *signature_sexp, - cache_mode_t cache_mode); + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl); int agent_pksign (ctrl_t ctrl, const char *desc_text, membuf_t *outbuf, cache_mode_t cache_mode); diff --git a/agent/cache.c b/agent/cache.c index addd1e8e1..10f9ef65a 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -194,8 +194,8 @@ agent_flush_cache (void) with a maximum lifetime of TTL seconds. If there is already data under this key, it will be replaced. Using a DATA of NULL deletes the entry. A TTL of 0 is replaced by the default TTL and a TTL of - -1 set infinite timeout. CACHE_MODE is stored with the cache entry - and used t select different timeouts. */ + -1 set infinite timeout. CACHE_MODE is stored with the cache entry + and used to select different timeouts. */ int agent_put_cache (const char *key, cache_mode_t cache_mode, const char *data, int ttl) diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 38561e3e8..41fe6f409 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -570,6 +570,59 @@ inq_quality (void *opaque, const char *line) } +/* Helper for agent_askpin and agent_get_passphrase. */ +static int +setup_qualitybar (void) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + char *tmpstr, *tmpstr2; + const char *tooltip; + + /* TRANSLATORS: This string is displayed by Pinentry as the label + for the quality bar. */ + tmpstr = try_percent_escape (_("Quality:"), "\t\r\n\f\v"); + snprintf (line, DIM(line)-1, "SETQUALITYBAR %s", tmpstr? tmpstr:""); + line[DIM(line)-1] = 0; + xfree (tmpstr); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc == 103 /*(Old assuan error code)*/ + || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) + ; /* Ignore Unknown Command from old Pinentry versions. */ + else if (rc) + return rc; + + tmpstr2 = gnupg_get_help_string ("pinentry.qualitybar.tooltip", 0); + if (tmpstr2) + tooltip = tmpstr2; + else + { + /* TRANSLATORS: This string is a tooltip, shown by pinentry when + hovering over the quality bar. Please use an appropriate + string to describe what this is about. The length of the + tooltip is limited to about 900 characters. If you do not + translate this entry, a default english text (see source) + will be used. */ + tooltip = _("pinentry.qualitybar.tooltip"); + if (!strcmp ("pinentry.qualitybar.tooltip", tooltip)) + tooltip = ("The quality of the text entered above.\n" + "Please ask your administrator for " + "details about the criteria."); + } + tmpstr = try_percent_escape (tooltip, "\t\r\n\f\v"); + xfree (tmpstr2); + snprintf (line, DIM(line)-1, "SETQUALITYBAR_TT %s", tmpstr? tmpstr:""); + line[DIM(line)-1] = 0; + xfree (tmpstr); + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc == 103 /*(Old assuan error code)*/ + || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) + ; /* Ignore Unknown Command from old pinentry versions. */ + else if (rc) + return rc; + + return 0; +} @@ -627,51 +680,8 @@ agent_askpin (ctrl_t ctrl, to the pinentry. */ if (pininfo->with_qualitybar && opt.min_passphrase_len ) { - char *tmpstr, *tmpstr2; - const char *tooltip; - - /* TRANSLATORS: This string is displayed by pinentry as the - label for the quality bar. */ - tmpstr = try_percent_escape (_("Quality:"), "\t\r\n\f\v"); - snprintf (line, DIM(line)-1, "SETQUALITYBAR %s", tmpstr? tmpstr:""); - line[DIM(line)-1] = 0; - xfree (tmpstr); - rc = assuan_transact (entry_ctx, line, - NULL, NULL, NULL, NULL, NULL, NULL); - if (rc == 103 /*(Old assuan error code)*/ - || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) - ; /* Ignore Unknown Command from old pinentry versions. */ - else if (rc) - return unlock_pinentry (rc); - - tmpstr2 = gnupg_get_help_string ("pinentry.qualitybar.tooltip", 0); - if (tmpstr2) - tooltip = tmpstr2; - else - { - /* TRANSLATORS: This string is a tooltip, shown by pinentry - when hovering over the quality bar. Please use an - appropriate string to describe what this is about. The - length of the tooltip is limited to about 900 characters. - If you do not translate this entry, a default english - text (see source) will be used. */ - tooltip = _("pinentry.qualitybar.tooltip"); - if (!strcmp ("pinentry.qualitybar.tooltip", tooltip)) - tooltip = ("The quality of the text entered above.\n" - "Please ask your administrator for " - "details about the criteria."); - } - tmpstr = try_percent_escape (tooltip, "\t\r\n\f\v"); - xfree (tmpstr2); - snprintf (line, DIM(line)-1, "SETQUALITYBAR_TT %s", tmpstr? tmpstr:""); - line[DIM(line)-1] = 0; - xfree (tmpstr); - rc = assuan_transact (entry_ctx, line, - NULL, NULL, NULL, NULL, NULL, NULL); - if (rc == 103 /*(Old assuan error code)*/ - || gpg_err_code (rc) == GPG_ERR_ASS_UNKNOWN_CMD) - ; /* Ignore Unknown Command from old pinentry versions. */ - else if (rc) + rc = setup_qualitybar (); + if (rc) return unlock_pinentry (rc); } @@ -764,7 +774,7 @@ agent_askpin (ctrl_t ctrl, int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, - const char *errtext) + const char *errtext, int with_qualitybar) { int rc; @@ -798,6 +808,13 @@ agent_get_passphrase (ctrl_t ctrl, if (rc) return unlock_pinentry (rc); + if (with_qualitybar && opt.min_passphrase_len) + { + rc = setup_qualitybar (); + if (rc) + return unlock_pinentry (rc); + } + if (errtext) { snprintf (line, DIM(line)-1, "SETERROR %s", errtext); @@ -815,7 +832,7 @@ agent_get_passphrase (ctrl_t ctrl, assuan_begin_confidential (entry_ctx); rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, - NULL, NULL, NULL, NULL); + inq_quality, entry_ctx, NULL, NULL); /* Most pinentries out in the wild return the old Assuan error code for canceled which gets translated to an assuan Cancel error and not to the code for a user cancel. Fix this here. */ diff --git a/agent/command-ssh.c b/agent/command-ssh.c index c262cade7..76f310a33 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -1,5 +1,5 @@ /* command-ssh.c - gpg-agent's ssh-agent emulation layer - * Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + * Copyright (C) 2004, 2005, 2006, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -710,17 +710,20 @@ open_control_file (FILE **r_fp, int append) /* Search the file at stream FP from the beginning until a matching HEXGRIP is found; return success in this case and store true at - DISABLED if the found key has been disabled. */ + DISABLED if the found key has been disabled. If R_TTL is not NULL + a specified TTL for that key is stored there. */ static gpg_error_t -search_control_file (FILE *fp, const char *hexgrip, int *disabled) +search_control_file (FILE *fp, const char *hexgrip, + int *r_disabled, int *r_ttl) { int c, i; - char *p, line[256]; - + char *p, *pend, line[256]; + long ttl; + assert (strlen (hexgrip) == 40 ); rewind (fp); - *disabled = 0; + *r_disabled = 0; next_line: do { @@ -746,10 +749,10 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled) } while (!*p || *p == '\n' || *p == '#'); - *disabled = 0; + *r_disabled = 0; if (*p == '!') { - *disabled = 1; + *r_disabled = 1; for (p++; spacep (p); p++) ; } @@ -763,7 +766,17 @@ search_control_file (FILE *fp, const char *hexgrip, int *disabled) return gpg_error (GPG_ERR_BAD_DATA); } - /* Fixme: Get TTL and flags. */ + ttl = strtol (p, &pend, 10); + p = pend; + if (!(spacep (p) || *p == '\n') || ttl < -1) + { + log_error ("invalid TTL value in ssh control file; assuming 0\n"); + ttl = 0; + } + if (r_ttl) + *r_ttl = ttl; + + /* Here is the place to parse flags if we need them. */ return 0; /* Okay: found it. */ } @@ -788,7 +801,7 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl) if (err) return err; - err = search_control_file (fp, hexgrip, &disabled); + err = search_control_file (fp, hexgrip, &disabled, NULL); if (err && gpg_err_code(err) == GPG_ERR_EOF) { struct tm *tp; @@ -808,6 +821,29 @@ add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl) } +/* Scan the sshcontrol file and return the TTL. */ +static int +ttl_from_sshcontrol (const char *hexgrip) +{ + FILE *fp; + int disabled, ttl; + + if (!hexgrip || strlen (hexgrip) != 40) + return 0; /* Wrong input: Use global default. */ + + if (open_control_file (&fp, 0)) + return 0; /* Error: Use the global default TTL. */ + + if (search_control_file (fp, hexgrip, &disabled, &ttl) + || disabled) + ttl = 0; /* Use the global default if not found or disabled. */ + + fclose (fp); + + return ttl; +} + + @@ -1875,7 +1911,7 @@ ssh_handler_request_identities (ctrl_t ctrl, hexgrip[40] = 0; if ( strlen (hexgrip) != 40 ) continue; - if (search_control_file (ctrl_fp, hexgrip, &disabled) + if (search_control_file (ctrl_fp, hexgrip, &disabled, NULL) || disabled) continue; @@ -1972,6 +2008,7 @@ ssh_handler_request_identities (ctrl_t ctrl, return ret_err; } + /* This function hashes the data contained in DATA of size DATA_N according to the message digest algorithm specified by MD_ALGORITHM and writes the message digest to HASH, which needs to large enough @@ -2017,7 +2054,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder, err = agent_pksign_do (ctrl, _("Please enter the passphrase " "for the ssh key%0A %c"), &signature_sexp, - CACHE_MODE_SSH); + CACHE_MODE_SSH, ttl_from_sshcontrol); ctrl->use_auth_call = 0; if (err) goto out; diff --git a/agent/command.c b/agent/command.c index 728b160a8..01f3ae24e 100644 --- a/agent/command.c +++ b/agent/command.c @@ -986,7 +986,8 @@ send_back_passphrase (assuan_context_t ctx, int via_data, const char *pw) } -/* GET_PASSPHRASE [--data] [--check] [--no-ask] [--repeat[=N]] +/* GET_PASSPHRASE [--data] [--check] [--no-ask] [--repeat[=N]] + [--qualitybar] [ ] This function is usually used to ask for a passphrase to be used @@ -1007,6 +1008,10 @@ send_back_passphrase (assuan_context_t ctx, int via_data, const char *pw) If the option "--no-ask" is used and the passphrase is not in the cache the user will not be asked to enter a passphrase but the error code GPG_ERR_NO_DATA is returned. + + If the option "--qualitybar" is used a visual indication of the + entered passphrase quality is shown. (Unless no minimum passphrase + length has been configured.) */ static int @@ -1020,7 +1025,8 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) const char *desc2 = _("Please re-enter this passphrase"); char *p; void *cache_marker; - int opt_data, opt_check, opt_no_ask, opt_repeat = 0; + int opt_data, opt_check, opt_no_ask, opt_qualbar; + int opt_repeat = 0; char *repeat_errtext = NULL; opt_data = has_option (line, "--data"); @@ -1034,6 +1040,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) else opt_repeat = 1; } + opt_qualbar = has_option (line, "--qualitybar"); line = skip_options (line); cacheid = line; @@ -1102,7 +1109,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); + repeat_errtext? repeat_errtext:errtext, + opt_qualbar); xfree (repeat_errtext); repeat_errtext = NULL; if (!rc) @@ -1119,7 +1127,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) char *response2; rc = agent_get_passphrase (ctrl, &response2, desc2, prompt, - errtext); + errtext, 0); if (rc) break; if (strcmp (response2, response)) @@ -1265,7 +1273,8 @@ cmd_passwd (assuan_context_t ctx, char *line) ctrl->in_passwd++; rc = agent_key_from_file (ctrl, ctrl->server_local->keydesc, - grip, &shadow_info, CACHE_MODE_IGNORE, &s_skey); + grip, &shadow_info, CACHE_MODE_IGNORE, NULL, + &s_skey); if (rc) ; else if (!s_skey) diff --git a/agent/findkey.c b/agent/findkey.c index 867e19634..5fe735242 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -297,11 +297,12 @@ modify_description (const char *in, const char *comment, char **result) /* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP should be the hex encoded keygrip of that key to be used with the caching mechanism. DESC_TEXT may be set to override the default - description used for the pinentry. */ + description used for the pinentry. If LOOKUP_TTL is given this + function is used to lookup the default ttl. */ static int unprotect (ctrl_t ctrl, const char *desc_text, unsigned char **keybuf, const unsigned char *grip, - cache_mode_t cache_mode) + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl) { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; @@ -406,7 +407,8 @@ unprotect (ctrl_t ctrl, const char *desc_text, return rc; } } - agent_put_cache (hexgrip, cache_mode, pi->pin, 0); + agent_put_cache (hexgrip, cache_mode, pi->pin, + lookup_ttl? lookup_ttl (hexgrip) : 0); xfree (*keybuf); *keybuf = arg.unprotected_key; } @@ -488,11 +490,16 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result) to a token; in this case an allocated S-expression with the shadow_info part from the file is stored at SHADOW_INFO. CACHE_MODE defines now the cache shall be used. DESC_TEXT may be - set to present a custom description for the pinentry. */ + set to present a custom description for the pinentry. LOOKUP_TTL + is an optional function to convey a TTL to the cache manager; we do + not simply pass the TTL value because the value is only needed if an + unprotect action was needed and looking up the TTL may have some + overhead (e.g. scanning the sshcontrol file). */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, - cache_mode_t cache_mode, gcry_sexp_t *result) + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + gcry_sexp_t *result) { int rc; unsigned char *buf; @@ -502,7 +509,7 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text, *result = NULL; if (shadow_info) - *shadow_info = NULL; + *shadow_info = NULL; rc = read_key_file (grip, &s_skey); if (rc) @@ -563,7 +570,8 @@ agent_key_from_file (ctrl_t ctrl, const char *desc_text, if (!rc) { - rc = unprotect (ctrl, desc_text_final, &buf, grip, cache_mode); + rc = unprotect (ctrl, desc_text_final, &buf, grip, + cache_mode, lookup_ttl); if (rc) log_error ("failed to unprotect the secret key: %s\n", gpg_strerror (rc)); diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 75e8e8f73..9e1c47d16 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -66,7 +66,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, } rc = agent_key_from_file (ctrl, desc_text, ctrl->keygrip, &shadow_info, - CACHE_MODE_NORMAL, &s_skey); + CACHE_MODE_NORMAL, NULL, &s_skey); if (rc) { if (gpg_err_code (rc) == GPG_ERR_ENOENT) diff --git a/agent/pksign.c b/agent/pksign.c index 926438e87..25cadb29e 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -125,10 +125,12 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits, /* SIGN whatever information we have accumulated in CTRL and return - the signature S-Expression. */ + the signature S-expression. LOOKUP is an optional function to + provide a way for lower layers to ask for the caching TTL. */ int agent_pksign_do (ctrl_t ctrl, const char *desc_text, - gcry_sexp_t *signature_sexp, cache_mode_t cache_mode) + gcry_sexp_t *signature_sexp, + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl) { gcry_sexp_t s_skey = NULL, s_sig = NULL; unsigned char *shadow_info = NULL; @@ -138,7 +140,8 @@ agent_pksign_do (ctrl_t ctrl, const char *desc_text, return gpg_error (GPG_ERR_NO_SECKEY); rc = agent_key_from_file (ctrl, desc_text, ctrl->keygrip, - &shadow_info, cache_mode, &s_skey); + &shadow_info, cache_mode, lookup_ttl, + &s_skey); if (rc) { log_error ("failed to read the secret key\n"); @@ -238,7 +241,7 @@ agent_pksign (ctrl_t ctrl, const char *desc_text, size_t len = 0; int rc = 0; - rc = agent_pksign_do (ctrl, desc_text, &s_sig, cache_mode); + rc = agent_pksign_do (ctrl, desc_text, &s_sig, cache_mode, NULL); if (rc) goto leave;