diff --git a/agent/ChangeLog b/agent/ChangeLog index a10d29800..2aa2c1375 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,14 @@ +2007-09-18 Werner Koch + + * agent.h (struct pin_entry_info_s): Add element WITH_QUALITYBAR. + * genkey.c (check_passphrase_constraints): New arg SILENT. + Changed all callers. + (agent_protect_and_store, agent_genkey): Enable qualitybar. + * call-pinentry.c (agent_askpin): Send that option. + (unescape_passphrase_string): New. + (inq_quality): New. + (estimate_passphrase_quality): New. + 2007-09-14 Marcus Brinkmann * call-pinentry.c (agent_popup_message_stop): Implement kill for diff --git a/agent/agent.h b/agent/agent.h index 7d76e4380..41b44c322 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -169,6 +169,7 @@ struct pin_entry_info_s int max_digits; /* max. number of allowed digits allowed*/ int max_tries; int failed_tries; + int with_qualitybar; /* Set if the quality bar should be displayed. */ int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */ void *check_cb_arg; /* optional argument which might be of use in the CB */ const char *cb_errtext; /* used by the cb to displaye a specific error */ @@ -269,7 +270,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, membuf_t *outbuf); /*-- genkey.c --*/ -int check_passphrase_constraints (ctrl_t ctrl, const char *pw); +int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent); int agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparmlen, membuf_t *outbuf); int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey); diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 14cc4229a..2afe9f12a 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -413,6 +413,106 @@ all_digitsp( const char *s) } +/* Return a new malloced string by unescaping the string S. Escaping + is percent escaping and '+'/space mapping. A binary Nul will + silently be replaced by a 0xFF. Function returns NULL to indicate + an out of memory status. PArsing stops at the end of the string or + a white space character. */ +static char * +unescape_passphrase_string (const unsigned char *s) +{ + char *buffer, *d; + + buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1); + if (!buffer) + return NULL; + while (*s && !spacep (s)) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d = xtoi_2 (s); + if (!*d) + *d = '\xff'; + d++; + s += 2; + } + else if (*s == '+') + { + *d++ = ' '; + s++; + } + else + *d++ = *s++; + } + *d = 0; + return buffer; +} + + +/* Estimate the quality of the passphrase PW and return a value in the + range 0..100. */ +static int +estimate_passphrase_quality (const char *pw) +{ + int goodlength = opt.min_passphrase_len + opt.min_passphrase_len/3; + int length; + const char *s; + + if (goodlength < 1) + return 0; + + for (length = 0, s = pw; *s; s++) + if (!spacep (s)) + length ++; + + if (length > goodlength) + return 100; + return ((length*10) / goodlength)*10; +} + + +/* Handle the QUALITY inquiry. */ +static int +inq_quality (void *opaque, const char *line) +{ + assuan_context_t ctx = opaque; + char *pin; + int rc; + int percent; + char numbuf[20]; + + if (!strncmp (line, "QUALITY", 7) && (line[7] == ' ' || !line[7])) + { + line += 7; + while (*line == ' ') + line++; + + pin = unescape_passphrase_string (line); + if (!pin) + rc = gpg_error_from_syserror (); + else + { + percent = estimate_passphrase_quality (pin); + if (check_passphrase_constraints (NULL, pin, 1)) + percent = -percent; + snprintf (numbuf, sizeof numbuf, "%d", percent); + rc = assuan_send_data (ctx, numbuf, strlen (numbuf)); + xfree (pin); + } + } + else + { + log_error ("unsupported inquiry `%s' from pinentry\n", line); + rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + } + + return rc; +} + + + + /* 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 @@ -463,6 +563,16 @@ agent_askpin (ctrl_t ctrl, if (rc) return unlock_pinentry (rc); + /* If a passphrase quality indicator has been requested and a + minimum passphrase length has not been disabled, send the command + to the pinentry. */ + if (pininfo->with_qualitybar && opt.min_passphrase_len ) + { + rc = assuan_transact (entry_ctx, "SETQUALITYBAR", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (rc); + } if (initial_errtext) { @@ -497,7 +607,7 @@ agent_askpin (ctrl_t ctrl, } 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.c b/agent/command.c index bb3d52fbb..431639b1c 100644 --- a/agent/command.c +++ b/agent/command.c @@ -912,7 +912,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) } while (!rc && opt_check - && check_passphrase_constraints (ctrl, response)); + && check_passphrase_constraints (ctrl, response, 0)); if (!rc) { diff --git a/agent/genkey.c b/agent/genkey.c index 48ba39dee..9901e9e1e 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -168,9 +168,10 @@ take_this_one_anyway (ctrl_t ctrl, const char *desc) /* Check whether the passphrase PW is suitable. Returns 0 if the passphrase is suitable and true if it is not and the user should be - asked to provide a different one. */ + asked to provide a different one. If SILENT is set, no message are + displayed. */ int -check_passphrase_constraints (ctrl_t ctrl, const char *pw) +check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent) { gpg_error_t err; unsigned int minlen = opt.min_passphrase_len; @@ -181,7 +182,12 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw) if (utf8_charcount (pw) < minlen ) { - char *desc = xtryasprintf + char *desc; + + if (silent) + return gpg_error (GPG_ERR_INV_PASSPHRASE); + + desc = xtryasprintf ( ngettext ("Warning: You have entered a passphrase that%%0A" "is obviously not secure. A passphrase should%%0A" "be at least %u character long.", @@ -198,7 +204,12 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw) if (nonalpha_count (pw) < minnonalpha ) { - char *desc = xtryasprintf + char *desc; + + if (silent) + return gpg_error (GPG_ERR_INV_PASSPHRASE); + + desc = xtryasprintf ( ngettext ("Warning: You have entered a passphrase that%%0A" "is obviously not secure. A passphrase should%%0A" "contain at least %u digit or special character.", @@ -226,6 +237,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw) "is obviously not secure. A passphrase may not%0A" "be a known term or match certain pattern."); + if (silent) + return gpg_error (GPG_ERR_INV_PASSPHRASE); + err = take_this_one_anyway (ctrl, desc); if (err) return err; @@ -242,6 +256,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw) "Please confirm that you do not want to " "have any protection on your key.")); + if (silent) + return gpg_error (GPG_ERR_INV_PASSPHRASE); + err = take_this_one_anyway2 (ctrl, desc, _("Yes, protection is not needed")); if (err) @@ -296,6 +313,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen, pi2 = pi + (sizeof *pi + 100); pi->max_length = 100; pi->max_tries = 3; + pi->with_qualitybar = 1; pi2->max_length = 100; pi2->max_tries = 3; pi2->check_cb = reenter_compare_cb; @@ -306,7 +324,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen, initial_errtext = NULL; if (!rc) { - if (check_passphrase_constraints (ctrl, pi->pin)) + if (check_passphrase_constraints (ctrl, pi->pin, 0)) { pi->failed_tries = 0; pi2->failed_tries = 0; @@ -417,6 +435,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) pi2 = pi + (sizeof *pi + 100); pi->max_length = 100; pi->max_tries = 3; + pi->with_qualitybar = 1; pi2->max_length = 100; pi2->max_tries = 3; pi2->check_cb = reenter_compare_cb; @@ -427,7 +446,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) initial_errtext = NULL; if (!rc) { - if (check_passphrase_constraints (ctrl, pi->pin)) + if (check_passphrase_constraints (ctrl, pi->pin, 0)) { pi->failed_tries = 0; pi2->failed_tries = 0;