diff --git a/agent/agent.h b/agent/agent.h index 23982626d..2bdee97c8 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -126,8 +126,11 @@ struct /* 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. */ + /* File name with a patternfile or NULL if not enabled. If the + * second one is set, it is used for symmetric only encryption + * instead of the former. */ const char *check_passphrase_pattern; + const char *check_sym_passphrase_pattern; /* If not 0 the user is asked to change his passphrase after these number of days. */ @@ -302,6 +305,7 @@ struct pin_entry_info_s int min_digits; /* min. number of digits required or 0 for freeform entry */ int max_digits; /* max. number of allowed digits allowed*/ int max_tries; /* max. number of allowed tries. */ + unsigned int constraints_flags; /* CHECK_CONSTRAINTS_... */ int failed_tries; /* Number of tries so far failed. */ int with_qualitybar; /* Set if the quality bar should be displayed. */ int with_repeat; /* Request repetition of the passphrase. */ @@ -524,7 +528,11 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, membuf_t *outbuf, int *r_padding); /*-- genkey.c --*/ -int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty, +#define CHECK_CONSTRAINTS_NOT_EMPTY 1 +#define CHECK_CONSTRAINTS_NEW_SYMKEY 2 + +int check_passphrase_constraints (ctrl_t ctrl, const char *pw, + unsigned int flags, char **failed_constraint); gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, char **r_passphrase); diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index ed37363da..585ddefac 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -91,6 +91,7 @@ struct entry_parm_s size_t size; unsigned char *buffer; int status; + unsigned int constraints_flags; }; @@ -865,10 +866,17 @@ generate_pin (void) /* Handle inquiries. */ +struct inq_cb_parm_s +{ + assuan_context_t ctx; + unsigned int flags; /* CHECK_CONSTRAINTS_... */ +}; + + static gpg_error_t inq_cb (void *opaque, const char *line) { - assuan_context_t ctx = opaque; + struct inq_cb_parm_s *parm = opaque; gpg_error_t err; const char *s; char *pin; @@ -884,10 +892,10 @@ inq_cb (void *opaque, const char *line) else { percent = estimate_passphrase_quality (pin); - if (check_passphrase_constraints (NULL, pin, 0, NULL)) + if (check_passphrase_constraints (NULL, pin, parm->flags, NULL)) percent = -percent; snprintf (numbuf, sizeof numbuf, "%d", percent); - err = assuan_send_data (ctx, numbuf, strlen (numbuf)); + err = assuan_send_data (parm->ctx, numbuf, strlen (numbuf)); xfree (pin); } } @@ -909,14 +917,14 @@ inq_cb (void *opaque, const char *line) err = gpg_error_from_syserror (); else { - if (check_passphrase_constraints (NULL, pin, 0, &errtext)) + if (check_passphrase_constraints (NULL, pin, parm->flags, &errtext)) { if (errtext) { /* Unescape the percent-escaped errtext because assuan_send_data escapes it again. */ errtextlen = percent_unescape_inplace (errtext, 0); - err = assuan_send_data (ctx, errtext, errtextlen); + err = assuan_send_data (parm->ctx, errtext, errtextlen); } else { @@ -926,7 +934,7 @@ inq_cb (void *opaque, const char *line) } else { - err = assuan_send_data (ctx, NULL, 0); + err = assuan_send_data (parm->ctx, NULL, 0); } xfree (errtext); xfree (pin); @@ -945,11 +953,11 @@ inq_cb (void *opaque, const char *line) err = gpg_error (GPG_ERR_GENERAL); goto leave; } - if (!check_passphrase_constraints (NULL, pin, 0, NULL)) + if (!check_passphrase_constraints (NULL, pin, parm->flags, NULL)) { - assuan_begin_confidential (ctx); - err = assuan_send_data (ctx, pin, strlen (pin)); - assuan_end_confidential (ctx); + assuan_begin_confidential (parm->ctx); + err = assuan_send_data (parm->ctx, pin, strlen (pin)); + assuan_end_confidential (parm->ctx); xfree (pin); goto leave; } @@ -1333,14 +1341,18 @@ do_getpin (ctrl_t ctrl, struct entry_parm_s *parm) int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL); gnupg_fd_t sock_watched = ctrl->thread_startup.fd; npth_t thread; + struct inq_cb_parm_s inq_cb_parm; rc = watch_sock_start (&sock_watched, &thread); if (rc) return rc; + inq_cb_parm.ctx = entry_ctx; + inq_cb_parm.flags = parm->constraints_flags; + assuan_begin_confidential (entry_ctx); rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm, - inq_cb, entry_ctx, + inq_cb, &inq_cb_parm, pinentry_status_cb, &parm->status); assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag); /* Most pinentries out in the wild return the old Assuan error code @@ -1493,6 +1505,7 @@ agent_askpin (ctrl_t ctrl, parm.size = pininfo->max_length; *pininfo->pin = 0; /* Reset the PIN. */ parm.buffer = (unsigned char*)pininfo->pin; + parm.constraints_flags = pininfo->constraints_flags; if (errtext) { diff --git a/agent/command.c b/agent/command.c index 8ce7a15d3..dd1a2f122 100644 --- a/agent/command.c +++ b/agent/command.c @@ -1871,6 +1871,8 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) pi->max_tries = 3; pi->with_qualitybar = opt_qualbar; pi->with_repeat = opt_repeat; + pi->constraints_flags = (CHECK_CONSTRAINTS_NOT_EMPTY + | CHECK_CONSTRAINTS_NEW_SYMKEY); pi2->max_length = MAX_PASSPHRASE_LEN + 1; pi2->max_tries = 3; pi2->check_cb = reenter_passphrase_cmp_cb; @@ -1891,7 +1893,9 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) xfree (entry_errtext); entry_errtext = NULL; /* We don't allow an empty passpharse in this mode. */ - if (check_passphrase_constraints (ctrl, pi->pin, 1, &entry_errtext)) + if (check_passphrase_constraints (ctrl, pi->pin, + pi->constraints_flags, + &entry_errtext)) { pi->failed_tries = 0; pi2->failed_tries = 0; @@ -1952,7 +1956,10 @@ cmd_get_passphrase (assuan_context_t ctx, char *line) int i; if (opt_check - && check_passphrase_constraints (ctrl, response,0,&entry_errtext)) + && check_passphrase_constraints + (ctrl, response, + (opt_newsymkey? CHECK_CONSTRAINTS_NEW_SYMKEY:0), + &entry_errtext)) { goto next_try; } diff --git a/agent/genkey.c b/agent/genkey.c index c7cfc6910..3ed63f663 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -89,9 +89,11 @@ nonalpha_count (const char *s) /* Check PW against a list of pattern. Return 0 if PW does not match - these pattern. */ + these pattern. If CHECK_CONSTRAINTS_NEW_SYMKEY is set in flags and + --check-sym-passphrase-pattern has been configured, use the pattern + file from that option. */ static int -check_passphrase_pattern (ctrl_t ctrl, const char *pw) +do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) { gpg_error_t err = 0; const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN); @@ -99,9 +101,17 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw) const char *argv[10]; pid_t pid; int result, i; + const char *pattern; (void)ctrl; + pattern = opt.check_passphrase_pattern; + if ((flags & CHECK_CONSTRAINTS_NEW_SYMKEY) + && opt.check_sym_passphrase_pattern) + pattern = opt.check_sym_passphrase_pattern; + if (!pattern) + return 1; /* Oops - Assume password should not be used */ + infp = gnupg_tmpfile (); if (!infp) { @@ -124,7 +134,7 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw) i = 0; argv[i++] = "--null"; argv[i++] = "--", - argv[i++] = opt.check_passphrase_pattern, + argv[i++] = pattern, argv[i] = NULL; log_assert (i < sizeof argv); @@ -156,12 +166,17 @@ take_this_one_anyway (ctrl_t ctrl, const char *desc, const char *anyway_btn) /* 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. If FAILED_CONSTRAINT is set, a - message describing the problem is returned in - *FAILED_CONSTRAINT. */ + * passphrase is suitable and true if it is not and the user should be + * asked to provide a different one. If FAILED_CONSTRAINT is set, a + * message describing the problem is returned at FAILED_CONSTRAINT. + * The FLAGS are: + * CHECK_CONSTRAINTS_NOT_EMPTY + * Do not allow an empty passphrase + * CHECK_CONSTRAINTS_NEW_SYMKEY + * Hint that the passphrase is used for a new symmetric key. + */ int -check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty, +check_passphrase_constraints (ctrl_t ctrl, const char *pw, unsigned int flags, char **failed_constraint) { gpg_error_t err = 0; @@ -170,6 +185,7 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty, char *msg1 = NULL; char *msg2 = NULL; char *msg3 = NULL; + int no_empty = !!(flags & CHECK_CONSTRAINTS_NOT_EMPTY); if (ctrl && ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) return 0; @@ -247,8 +263,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty, 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)) + if (*pw + && (opt.check_passphrase_pattern || opt.check_sym_passphrase_pattern) + && do_check_passphrase_pattern (ctrl, pw, flags)) { if (!failed_constraint) { diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 30d7cebaa..dd60847e6 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -113,6 +113,7 @@ enum cmd_and_opt_values oMinPassphraseLen, oMinPassphraseNonalpha, oCheckPassphrasePattern, + oCheckSymPassphrasePattern, oMaxPassphraseDays, oEnablePassphraseHistory, oDisableExtendedKeyFormat, @@ -265,6 +266,8 @@ static gpgrt_opt_t opts[] = { " characters for a new passphrase")), ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern", N_("|FILE|check new passphrases against pattern in FILE")), + ARGPARSE_s_s (oCheckSymPassphrasePattern, "check-sym-passphrase-pattern", + "@"), ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days", N_("|N|expire the passphrase after N days")), ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", @@ -862,6 +865,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) opt.min_passphrase_len = MIN_PASSPHRASE_LEN; opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA; opt.check_passphrase_pattern = NULL; + opt.check_sym_passphrase_pattern = NULL; opt.max_passphrase_days = MAX_PASSPHRASE_DAYS; opt.enable_passphrase_history = 0; opt.enable_extended_key_format = 1; @@ -942,6 +946,9 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread) case oCheckPassphrasePattern: opt.check_passphrase_pattern = pargs->r.ret_str; break; + case oCheckSymPassphrasePattern: + opt.check_sym_passphrase_pattern = pargs->r.ret_str; + break; case oMaxPassphraseDays: opt.max_passphrase_days = pargs->r.ret_ulong; break; @@ -1440,6 +1447,8 @@ main (int argc, char **argv) GC_OPT_FLAG_DEFAULT, MIN_PASSPHRASE_NONALPHA); es_printf ("check-passphrase-pattern:%lu:\n", GC_OPT_FLAG_DEFAULT); + es_printf ("check-sym-passphrase-pattern:%lu:\n", + GC_OPT_FLAG_DEFAULT); es_printf ("max-passphrase-days:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, MAX_PASSPHRASE_DAYS); es_printf ("ssh-fingerprint-digest:%lu:\"%s:\n", diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index a061283d6..5413a88ac 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -205,14 +205,14 @@ if used in an options file. @item -v -@item --verbose +@itemx --verbose @opindex verbose Outputs additional information while running. You can increase the verbosity by giving several verbose commands to @command{gpg-agent}, such as @samp{-vv}. @item -q -@item --quiet +@itemx --quiet @opindex quiet Try to be as quiet as possible. @@ -429,11 +429,15 @@ of digits or special characters a warning will be displayed. Defaults to 1. @item --check-passphrase-pattern @var{file} +@itemx --check-sym-passphrase-pattern @var{file} @opindex check-passphrase-pattern +@opindex check-sym-passphrase-pattern Check the passphrase against the pattern given in @var{file}. When entering a new passphrase matching one of these pattern a warning will -be displayed. @var{file} should be an absolute filename. The default is -not to use any pattern file. +be displayed. @var{file} should be an absolute filename. The default +is not to use any pattern file. The second version of this option is +only used when creating a new symmetric key to allow the use of +different patterns for such passphrases. Security note: It is known that checking a passphrase against a list of pattern or even against a complete dictionary is not very effective to diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 20ec00a11..d8e75739f 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -351,6 +351,8 @@ static known_option_t known_options_gpg_agent[] = { "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, { "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, /**/ GC_ARG_TYPE_FILENAME }, + { "check-sym-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, + /**/ GC_ARG_TYPE_FILENAME }, { "max-passphrase-days", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, { "enable-passphrase-history", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, { "pinentry-timeout", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED },