1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

Implemented more gpg-agen options to support certain passphrase policies.

New tool gpg-check-pattern.
This commit is contained in:
Werner Koch 2007-08-27 18:10:27 +00:00
parent 503f91e0ae
commit 15d0cb42a1
28 changed files with 1166 additions and 72 deletions

View file

@ -1,3 +1,22 @@
2007-08-27 Werner Koch <wk@g10code.com>
* gpg-agent.c: Add options --min-passphrase-nonalpha,
--check-passphrase-pattern and --enforce-passphrase-constraints.
(MIN_PASSPHRASE_NONALPHA): Init nonalpha option to 1.
(main): Declare options for gpgconf.
* agent.h (struct): Add members MIN_PASSPHRASE_NONALPHA,
ENFORCE_PASSPHRASE_CONSTRAINTS and CHECK_PASSPHRASE_PATTERN.
* genkey.c (nonalpha_charcount): New.
(check_passphrase_pattern): New.
(check_passphrase_constraints): Implement. Factor some code out...
(take_this_one_anyway, take_this_one_anyway2): .. New.
* call-pinentry.c (agent_show_message): New.
(agent_askpin): We better reset the pin buffer before asking.
* trustlist.c (insert_colons): New.
(agent_marktrusted): Pretty print the fpr.
2007-08-22 Werner Koch <wk@g10code.com>
* findkey.c (O_BINARY): Make sure it is defined.

View file

@ -80,8 +80,15 @@ struct
unsigned long max_cache_ttl; /* Default. */
unsigned long max_cache_ttl_ssh; /* for SSH. */
/* Flag disallowin bypassing of the warning. */
int enforce_passphrase_constraints;
/* The require minmum length of a passphrase. */
unsigned int min_passphrase_len;
/* The minimum number of non-alpha characters in a passphrase. */
unsigned int min_passphrase_nonalpha;
/* File name with a patternfile or NULL if not enabled. */
const char *check_passphrase_pattern;
int running_detached; /* We are running detached from the tty. */
@ -227,6 +234,7 @@ int agent_get_passphrase (ctrl_t ctrl, char **retpass,
const char *errtext);
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);
int agent_popup_message_start (ctrl_t ctrl,
const char *desc, const char *ok_btn);
void agent_popup_message_stop (ctrl_t ctrl);

View file

@ -213,7 +213,9 @@ start_pinentry (ctrl_t ctrl)
#endif
if (fflush (NULL))
{
#ifndef HAVE_W32_SYSTEM
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
#endif
log_error ("error flushing pending output: %s\n", strerror (errno));
/* At least Windows XP fails here with EBADF. According to docs
and Wine an fflush(NULL) is the same as _flushall. However
@ -476,6 +478,7 @@ agent_askpin (ctrl_t ctrl,
{
memset (&parm, 0, sizeof parm);
parm.size = pininfo->max_length;
*pininfo->pin = 0; /* Reset the PIN. */
parm.buffer = (unsigned char*)pininfo->pin;
if (errtext)
@ -671,6 +674,55 @@ agent_get_confirmation (ctrl_t ctrl,
}
/* Pop up the PINentry, display the text DESC and a button with the
text OK_BTN (which may be NULL to use the default of "OK") and waut
for the user to hit this button. The return value is not
relevant. */
int
agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn)
{
int rc;
char line[ASSUAN_LINELENGTH];
rc = start_pinentry (ctrl);
if (rc)
return rc;
if (desc)
snprintf (line, DIM(line)-1, "SETDESC %s", desc);
else
snprintf (line, DIM(line)-1, "RESET");
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, 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. */
if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
if (rc)
return unlock_pinentry (rc);
if (ok_btn)
{
snprintf (line, DIM(line)-1, "SETOK %s", ok_btn);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL,
NULL, NULL, NULL);
if (rc)
return unlock_pinentry (rc);
}
rc = assuan_transact (entry_ctx, "CONFIRM --one-button", NULL, NULL, NULL,
NULL, NULL, NULL);
if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
return unlock_pinentry (rc);
}
/* The thread running the popup message. */
static void *
popup_message_thread (void *arg)

View file

@ -27,6 +27,8 @@
#include "agent.h"
#include "i18n.h"
#include "exechelp.h"
#include "sysutils.h"
static int
store_key (gcry_sexp_t private, const char *passphrase, int force)
@ -70,6 +72,100 @@ store_key (gcry_sexp_t private, const char *passphrase, int force)
}
/* Count the number of non-alpha characters in S. Control characters
and non-ascii characters are not considered. */
static size_t
nonalpha_count (const char *s)
{
size_t n;
for (n=0; *s; s++)
if (isascii (*s) && ( isdigit (*s) || ispunct (*s) ))
n++;
return n;
}
/* Check PW against a list of pattern. Return 0 if PW does not match
these pattern. */
static int
check_passphrase_pattern (ctrl_t ctrl, const char *pw)
{
gpg_error_t err = 0;
const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN);
FILE *infp;
const char *argv[10];
pid_t pid;
int result, i;
infp = gnupg_tmpfile ();
if (!infp)
{
err = gpg_error_from_syserror ();
log_error (_("error creating temporary file: %s\n"), strerror (errno));
return 1; /* Error - assume password should not be used. */
}
if (fwrite (pw, strlen (pw), 1, infp) != 1)
{
err = gpg_error_from_syserror ();
log_error (_("error writing to temporary file: %s\n"),
strerror (errno));
fclose (infp);
return 1; /* Error - assume password should not be used. */
}
rewind (infp);
i = 0;
argv[i++] = "--null";
argv[i++] = "--",
argv[i++] = opt.check_passphrase_pattern,
argv[i] = NULL;
assert (i < sizeof argv);
if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
result = 1; /* Execute error - assume password should no be used. */
else if (gnupg_wait_process (pgmname, pid))
result = 1; /* Helper returned an error - probably a match. */
else
result = 0; /* Success; i.e. no match. */
/* Overwrite our temporary file. */
rewind (infp);
for (i=((strlen (pw)+99)/100)*100; i > 0; i--)
putc ('\xff', infp);
fflush (infp);
fclose (infp);
return result;
}
static int
take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn)
{
gpg_error_t err;
if (opt.enforce_passphrase_constraints)
{
err = agent_show_message (ctrl, desc, _("Enter new passphrase"));
if (!err)
err = gpg_error (GPG_ERR_CANCELED);
}
else
err = agent_get_confirmation (ctrl, desc,
anyway_btn, _("Enter new passphrase"));
return err;
}
static int
take_this_one_anyway (ctrl_t ctrl, const char *desc)
{
return take_this_one_anyway2 (ctrl, desc, _("Take this one anyway"));
}
/* 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. */
@ -78,7 +174,8 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
{
gpg_error_t err;
unsigned int minlen = opt.min_passphrase_len;
unsigned int minnonalpha = opt.min_passphrase_nonalpha;
if (!pw)
pw = "";
@ -93,25 +190,60 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
"be at least %u characters long.", minlen), minlen );
if (!desc)
return gpg_error_from_syserror ();
err = agent_get_confirmation (ctrl, desc,
_("Take this one anyway"),
_("Enter new passphrase"));
err = take_this_one_anyway (ctrl, desc);
xfree (desc);
if (err)
return err;
}
if (nonalpha_count (pw) < minnonalpha )
{
char *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.",
"Warning: You have entered a passphrase that%%0A"
"is obviously not secure. A passphrase should%%0A"
"contain at least %u digits or special characters.",
minnonalpha), minnonalpha );
if (!desc)
return gpg_error_from_syserror ();
err = take_this_one_anyway (ctrl, desc);
xfree (desc);
if (err)
return err;
}
/* If configured check the passphrase against a list of know words
and pattern. The actual test is done by an external program.
The warning message is generic to give the user no hint on how to
circumvent this list. */
if (*pw && opt.check_passphrase_pattern &&
check_passphrase_pattern (ctrl, pw))
{
const char *desc =
/* */ _("Warning: You have entered a passphrase that%0A"
"is obviously not secure. A passphrase may not%0A"
"be a known term or match certain pattern.");
err = take_this_one_anyway (ctrl, desc);
if (err)
return err;
}
/* The final check is to warn about an empty passphrase. */
if (!*pw)
{
const char *desc = _("You have not entered a passphrase - "
"this is in general a bad idea!%0A"
"Please confirm that you do not want to "
"have any protection on your key.");
const char *desc = (opt.enforce_passphrase_constraints?
_("You have not entered a passphrase!%0A"
"An empty passphrase is not allowed.") :
_("You have not entered a passphrase - "
"this is in general a bad idea!%0A"
"Please confirm that you do not want to "
"have any protection on your key."));
err = agent_get_confirmation (ctrl, desc,
_("Yes, protection is not needed"),
_("Enter new passphrase"));
err = take_this_one_anyway2 (ctrl, desc,
_("Yes, protection is not needed"));
if (err)
return err;
}

View file

@ -88,7 +88,10 @@ enum cmd_and_opt_values
oDefCacheTTLSSH,
oMaxCacheTTL,
oMaxCacheTTLSSH,
oEnforcePassphraseConstraints,
oMinPassphraseLen,
oMinPassphraseNonalpha,
oCheckPassphrasePattern,
oUseStandardSocket,
oNoUseStandardSocket,
@ -149,7 +152,12 @@ static ARGPARSE_OPTS opts[] = {
{ oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
{ oMaxCacheTTL, "max-cache-ttl", 4, "@" },
{ oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
{ oEnforcePassphraseConstraints, "enforce-passphrase-constraints", 0, "@"},
{ oMinPassphraseLen, "min-passphrase-len", 4, "@" },
{ oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
{ oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
N_("do not use the PIN cache when signing")},
{ oAllowMarkTrusted, "allow-mark-trusted", 0,
@ -168,6 +176,7 @@ static ARGPARSE_OPTS opts[] = {
#define MAX_CACHE_TTL (120*60) /* 2 hours */
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
#define MIN_PASSPHRASE_LEN (8)
#define MIN_PASSPHRASE_NONALPHA (1)
/* The timer tick used for housekeeping stuff. For Windows we use a
longer period as the SetWaitableTimer seems to signal earlier than
@ -362,7 +371,10 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
opt.max_cache_ttl = MAX_CACHE_TTL;
opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
opt.enforce_passphrase_constraints = 0;
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
opt.check_passphrase_pattern = NULL;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 0;
opt.disable_scdaemon = 0;
@ -402,7 +414,16 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
case oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = pargs->r.ret_ulong; break;
case oEnforcePassphraseConstraints:
opt.enforce_passphrase_constraints=1;
break;
case oMinPassphraseLen: opt.min_passphrase_len = pargs->r.ret_ulong; break;
case oMinPassphraseNonalpha:
opt.min_passphrase_nonalpha = pargs->r.ret_ulong;
break;
case oCheckPassphrasePattern:
opt.check_passphrase_pattern = pargs->r.ret_str;
break;
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
@ -723,8 +744,15 @@ main (int argc, char **argv )
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
printf ("max-cache-ttl-ssh:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
printf ("enforce-passphrase-constraints:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("min-passphrase-len:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
printf ("min-passphrase-nonalpha:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
MIN_PASSPHRASE_NONALPHA);
printf ("check-passphrase-pattern:%lu:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
printf ("no-grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("ignore-cache-for-signing:%lu:\n",

View file

@ -455,13 +455,40 @@ agent_listtrusted (void *assuan_context)
}
/* Create a copy of string with colons inserted after each two bytes.
Caller needs to release the string. In case of a memory failure,
NULL is returned. */
static char *
insert_colons (const char *string)
{
char *buffer, *p;
size_t n = strlen (string);
p = buffer = xtrymalloc ( n + (n+2)/3 + 1 );
if (!buffer)
return NULL;
while (*string)
{
*p++ = *string++;
if (*string)
{
*p++ = *string++;
if (*string)
*p++ = ':';
}
}
*p = 0;
return buffer;
}
/* Insert the given fpr into our trustdb. We expect FPR to be an all
uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'.
This function does first check whether that key has alreay been put
This function does first check whether that key has already been put
into the trustdb and returns success in this case. Before a FPR
actually gets inserted, the user is asked by means of the pin-entry
whether this is actual wants he want to do.
*/
actually gets inserted, the user is asked by means of the Pinentry
whether this is actual wants he want to do. */
gpg_error_t
agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
{
@ -469,6 +496,8 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
char *desc;
char *fname;
FILE *fp;
char *fprformatted;
/* Check whether we are at all allowed to modify the trustlist.
This is useful so that the trustlist may be a symlink to a global
@ -494,6 +523,9 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
return gpg_error (GPG_ERR_NOT_SUPPORTED);
/* Insert a new one. */
fprformatted = insert_colons (fpr);
if (!fprformatted)
return out_of_core ();
if (asprintf (&desc,
/* TRANSLATORS: This prompt is shown by the Pinentry
and has one special property: A "%%0A" is used by
@ -503,12 +535,15 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
plain % sign, you need to encode it as "%%25". The
second "%s" gets replaced by a hexdecimal
fingerprint string whereas the first one receives
the name as store in the certificate. */
the name as stored in the certificate. */
_("Please verify that the certificate identified as:%%0A"
" \"%s\"%%0A"
"has the fingerprint:%%0A"
" %s"), name, fpr) < 0 )
return out_of_core ();
" %s"), name, fprformatted) < 0 )
{
xfree (fprformatted);
return out_of_core ();
}
/* TRANSLATORS: "Correct" is the label of a button and intended to
be hit if the fingerprint matches the one of the CA. The other
@ -519,8 +554,11 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
gpgsm may stop asking further questions. We won't do this for
the second question of course. */
if (err)
return (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED ?
gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED) : err);
{
xfree (fprformatted);
return (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED ?
gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED) : err);
}
@ -537,12 +575,18 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
" \"%s\"%%0A"
"to correctly certify user certificates?"),
name) < 0 )
return out_of_core ();
{
xfree (fprformatted);
return out_of_core ();
}
err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"));
free (desc);
if (err)
return err;
{
xfree (fprformatted);
return err;
}
/* Now check again to avoid duplicates. We take the lock to make
sure that nobody else plays with our file. Frankly we don't work
@ -552,6 +596,7 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
if (!agent_istrusted (ctrl, fpr))
{
unlock_trusttable ();
xfree (fprformatted);
return 0;
}
@ -566,6 +611,7 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
log_error ("can't create `%s': %s\n", fname, gpg_strerror (err));
xfree (fname);
unlock_trusttable ();
xfree (fprformatted);
return err;
}
fputs (headerblurb, fp);
@ -578,13 +624,14 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
log_error ("can't open `%s': %s\n", fname, gpg_strerror (err));
xfree (fname);
unlock_trusttable ();
xfree (fprformatted);
return err;
}
/* Append the key. */
fputs ("\n# ", fp);
print_sanitized_string (fp, name, 0);
fprintf (fp, "\n%s %c\n", fpr, flag);
fprintf (fp, "\n%s %c\n", fprformatted, flag);
if (ferror (fp))
err = gpg_error_from_syserror ();
@ -595,6 +642,7 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
agent_reload_trustlist ();
xfree (fname);
unlock_trusttable ();
xfree (fprformatted);
return err;
}