aboutsummaryrefslogtreecommitdiffstats
path: root/agent
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agent/ChangeLog19
-rw-r--r--agent/agent.h8
-rw-r--r--agent/call-pinentry.c52
-rw-r--r--agent/genkey.c156
-rw-r--r--agent/gpg-agent.c28
-rw-r--r--agent/trustlist.c72
6 files changed, 311 insertions, 24 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 4c7df8b6d..4b8d5d3b1 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,22 @@
+2007-08-27 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* findkey.c (O_BINARY): Make sure it is defined.
diff --git a/agent/agent.h b/agent/agent.h
index 22225f6a2..8531f395a 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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);
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
index 4631eadea..f29d3c4af 100644
--- a/agent/call-pinentry.c
+++ b/agent/call-pinentry.c
@@ -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)
diff --git a/agent/genkey.c b/agent/genkey.c
index e160f453f..09cd9f738 100644
--- a/agent/genkey.c
+++ b/agent/genkey.c
@@ -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;
}
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index cb0a67b73..453e9a9d5 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -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",
diff --git a/agent/trustlist.c b/agent/trustlist.c
index deb0d95cd..a154da899 100644
--- a/agent/trustlist.c
+++ b/agent/trustlist.c
@@ -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;
}