agent: New option --check-sym-passphrase-pattern.

* agent/gpg-agent.c (oCheckSymPassphrasePattern): New.
(opts): Add --check-sym-passphrase-pattern.
(parse_rereadable_options): Set option.
(main): Return option info.
* tools/gpgconf-comp.c: Add new option.
* agent/agent.h (opt): Add var check_sym_passphrase_pattern.
(struct pin_entry_info_s): Add var constraints_flags.
(CHECK_CONSTRAINTS_NOT_EMPTY): New to replace a hardwired 1.
(CHECK_CONSTRAINTS_NEW_SYMKEY): New.
* agent/genkey.c (check_passphrase_pattern): Rename to ...
(do_check_passphrase_pattern): this to make code reading
easier. Handle the --check-sym-passphrase-pattern option.
(check_passphrase_constraints): Replace arg no_empty by a generic
flags arg.  Also handle --check-sym-passphrase-pattern here.
* agent/command.c (cmd_get_passphrase): In --newsymkey mode pass
CHECK_CONSTRAINTS_NEW_SYMKEY flag.
* agent/call-pinentry.c (struct entry_parm_s): Add constraints_flags.
(struct inq_cb_parm_s): New.
(inq_cb): Use new struct for parameter passing.  Pass flags to teh
constraints checking.
(do_getpin): Pass constraints flag down.
(agent_askpin): Take constrainst flag from the supplied pinentry
struct.
--

Requirements for a passphrase to protect a private key and for a
passphrase used for symmetric encryption are different.  Thus a
the use of a different pattern file will be useful.  Note that a
pattern file can be used to replace the other passphrase constraints
options and thus we don't need to duplicate them for symmetric
encryption.

GnuPG-bug-id: 5517
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-08-13 13:42:31 +02:00
parent 22c5461b4a
commit 7c45a69eb9
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
7 changed files with 89 additions and 29 deletions

View File

@ -126,8 +126,11 @@ struct
/* The minimum number of non-alpha characters in a passphrase. */ /* The minimum number of non-alpha characters in a passphrase. */
unsigned int min_passphrase_nonalpha; 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_passphrase_pattern;
const char *check_sym_passphrase_pattern;
/* If not 0 the user is asked to change his passphrase after these /* If not 0 the user is asked to change his passphrase after these
number of days. */ 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 min_digits; /* min. number of digits required or 0 for freeform entry */
int max_digits; /* max. number of allowed digits allowed*/ int max_digits; /* max. number of allowed digits allowed*/
int max_tries; /* max. number of allowed tries. */ int max_tries; /* max. number of allowed tries. */
unsigned int constraints_flags; /* CHECK_CONSTRAINTS_... */
int failed_tries; /* Number of tries so far failed. */ int failed_tries; /* Number of tries so far failed. */
int with_qualitybar; /* Set if the quality bar should be displayed. */ int with_qualitybar; /* Set if the quality bar should be displayed. */
int with_repeat; /* Request repetition of the passphrase. */ 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); membuf_t *outbuf, int *r_padding);
/*-- genkey.c --*/ /*-- 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); char **failed_constraint);
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
char **r_passphrase); char **r_passphrase);

View File

@ -91,6 +91,7 @@ struct entry_parm_s
size_t size; size_t size;
unsigned char *buffer; unsigned char *buffer;
int status; int status;
unsigned int constraints_flags;
}; };
@ -865,10 +866,17 @@ generate_pin (void)
/* Handle inquiries. */ /* Handle inquiries. */
struct inq_cb_parm_s
{
assuan_context_t ctx;
unsigned int flags; /* CHECK_CONSTRAINTS_... */
};
static gpg_error_t static gpg_error_t
inq_cb (void *opaque, const char *line) inq_cb (void *opaque, const char *line)
{ {
assuan_context_t ctx = opaque; struct inq_cb_parm_s *parm = opaque;
gpg_error_t err; gpg_error_t err;
const char *s; const char *s;
char *pin; char *pin;
@ -884,10 +892,10 @@ inq_cb (void *opaque, const char *line)
else else
{ {
percent = estimate_passphrase_quality (pin); percent = estimate_passphrase_quality (pin);
if (check_passphrase_constraints (NULL, pin, 0, NULL)) if (check_passphrase_constraints (NULL, pin, parm->flags, NULL))
percent = -percent; percent = -percent;
snprintf (numbuf, sizeof numbuf, "%d", 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); xfree (pin);
} }
} }
@ -909,14 +917,14 @@ inq_cb (void *opaque, const char *line)
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
else else
{ {
if (check_passphrase_constraints (NULL, pin, 0, &errtext)) if (check_passphrase_constraints (NULL, pin, parm->flags, &errtext))
{ {
if (errtext) if (errtext)
{ {
/* Unescape the percent-escaped errtext because /* Unescape the percent-escaped errtext because
assuan_send_data escapes it again. */ assuan_send_data escapes it again. */
errtextlen = percent_unescape_inplace (errtext, 0); errtextlen = percent_unescape_inplace (errtext, 0);
err = assuan_send_data (ctx, errtext, errtextlen); err = assuan_send_data (parm->ctx, errtext, errtextlen);
} }
else else
{ {
@ -926,7 +934,7 @@ inq_cb (void *opaque, const char *line)
} }
else else
{ {
err = assuan_send_data (ctx, NULL, 0); err = assuan_send_data (parm->ctx, NULL, 0);
} }
xfree (errtext); xfree (errtext);
xfree (pin); xfree (pin);
@ -945,11 +953,11 @@ inq_cb (void *opaque, const char *line)
err = gpg_error (GPG_ERR_GENERAL); err = gpg_error (GPG_ERR_GENERAL);
goto leave; goto leave;
} }
if (!check_passphrase_constraints (NULL, pin, 0, NULL)) if (!check_passphrase_constraints (NULL, pin, parm->flags, NULL))
{ {
assuan_begin_confidential (ctx); assuan_begin_confidential (parm->ctx);
err = assuan_send_data (ctx, pin, strlen (pin)); err = assuan_send_data (parm->ctx, pin, strlen (pin));
assuan_end_confidential (ctx); assuan_end_confidential (parm->ctx);
xfree (pin); xfree (pin);
goto leave; 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); int saveflag = assuan_get_flag (entry_ctx, ASSUAN_CONFIDENTIAL);
gnupg_fd_t sock_watched = ctrl->thread_startup.fd; gnupg_fd_t sock_watched = ctrl->thread_startup.fd;
npth_t thread; npth_t thread;
struct inq_cb_parm_s inq_cb_parm;
rc = watch_sock_start (&sock_watched, &thread); rc = watch_sock_start (&sock_watched, &thread);
if (rc) if (rc)
return rc; return rc;
inq_cb_parm.ctx = entry_ctx;
inq_cb_parm.flags = parm->constraints_flags;
assuan_begin_confidential (entry_ctx); assuan_begin_confidential (entry_ctx);
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm, rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, parm,
inq_cb, entry_ctx, inq_cb, &inq_cb_parm,
pinentry_status_cb, &parm->status); pinentry_status_cb, &parm->status);
assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag); assuan_set_flag (entry_ctx, ASSUAN_CONFIDENTIAL, saveflag);
/* Most pinentries out in the wild return the old Assuan error code /* 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; parm.size = pininfo->max_length;
*pininfo->pin = 0; /* Reset the PIN. */ *pininfo->pin = 0; /* Reset the PIN. */
parm.buffer = (unsigned char*)pininfo->pin; parm.buffer = (unsigned char*)pininfo->pin;
parm.constraints_flags = pininfo->constraints_flags;
if (errtext) if (errtext)
{ {

View File

@ -1871,6 +1871,8 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
pi->max_tries = 3; pi->max_tries = 3;
pi->with_qualitybar = opt_qualbar; pi->with_qualitybar = opt_qualbar;
pi->with_repeat = opt_repeat; 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_length = MAX_PASSPHRASE_LEN + 1;
pi2->max_tries = 3; pi2->max_tries = 3;
pi2->check_cb = reenter_passphrase_cmp_cb; pi2->check_cb = reenter_passphrase_cmp_cb;
@ -1891,7 +1893,9 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
xfree (entry_errtext); xfree (entry_errtext);
entry_errtext = NULL; entry_errtext = NULL;
/* We don't allow an empty passpharse in this mode. */ /* 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; pi->failed_tries = 0;
pi2->failed_tries = 0; pi2->failed_tries = 0;
@ -1952,7 +1956,10 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
int i; int i;
if (opt_check 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; goto next_try;
} }

View File

@ -89,9 +89,11 @@ nonalpha_count (const char *s)
/* Check PW against a list of pattern. Return 0 if PW does not match /* 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 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; gpg_error_t err = 0;
const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN); 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]; const char *argv[10];
pid_t pid; pid_t pid;
int result, i; int result, i;
const char *pattern;
(void)ctrl; (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 (); infp = gnupg_tmpfile ();
if (!infp) if (!infp)
{ {
@ -124,7 +134,7 @@ check_passphrase_pattern (ctrl_t ctrl, const char *pw)
i = 0; i = 0;
argv[i++] = "--null"; argv[i++] = "--null";
argv[i++] = "--", argv[i++] = "--",
argv[i++] = opt.check_passphrase_pattern, argv[i++] = pattern,
argv[i] = NULL; argv[i] = NULL;
log_assert (i < sizeof argv); 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 /* 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 * 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 * asked to provide a different one. If FAILED_CONSTRAINT is set, a
message describing the problem is returned in * message describing the problem is returned at FAILED_CONSTRAINT.
*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 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) char **failed_constraint)
{ {
gpg_error_t err = 0; 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 *msg1 = NULL;
char *msg2 = NULL; char *msg2 = NULL;
char *msg3 = NULL; char *msg3 = NULL;
int no_empty = !!(flags & CHECK_CONSTRAINTS_NOT_EMPTY);
if (ctrl && ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK) if (ctrl && ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
return 0; 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. 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 The warning message is generic to give the user no hint on how to
circumvent this list. */ circumvent this list. */
if (*pw && opt.check_passphrase_pattern && if (*pw
check_passphrase_pattern (ctrl, pw)) && (opt.check_passphrase_pattern || opt.check_sym_passphrase_pattern)
&& do_check_passphrase_pattern (ctrl, pw, flags))
{ {
if (!failed_constraint) if (!failed_constraint)
{ {

View File

@ -113,6 +113,7 @@ enum cmd_and_opt_values
oMinPassphraseLen, oMinPassphraseLen,
oMinPassphraseNonalpha, oMinPassphraseNonalpha,
oCheckPassphrasePattern, oCheckPassphrasePattern,
oCheckSymPassphrasePattern,
oMaxPassphraseDays, oMaxPassphraseDays,
oEnablePassphraseHistory, oEnablePassphraseHistory,
oDisableExtendedKeyFormat, oDisableExtendedKeyFormat,
@ -265,6 +266,8 @@ static gpgrt_opt_t opts[] = {
" characters for a new passphrase")), " characters for a new passphrase")),
ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern", ARGPARSE_s_s (oCheckPassphrasePattern, "check-passphrase-pattern",
N_("|FILE|check new passphrases against pattern in FILE")), N_("|FILE|check new passphrases against pattern in FILE")),
ARGPARSE_s_s (oCheckSymPassphrasePattern, "check-sym-passphrase-pattern",
"@"),
ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days", ARGPARSE_s_u (oMaxPassphraseDays, "max-passphrase-days",
N_("|N|expire the passphrase after N days")), N_("|N|expire the passphrase after N days")),
ARGPARSE_s_n (oEnablePassphraseHistory, "enable-passphrase-history", 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_len = MIN_PASSPHRASE_LEN;
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA; opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
opt.check_passphrase_pattern = NULL; opt.check_passphrase_pattern = NULL;
opt.check_sym_passphrase_pattern = NULL;
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS; opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
opt.enable_passphrase_history = 0; opt.enable_passphrase_history = 0;
opt.enable_extended_key_format = 1; opt.enable_extended_key_format = 1;
@ -942,6 +946,9 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
case oCheckPassphrasePattern: case oCheckPassphrasePattern:
opt.check_passphrase_pattern = pargs->r.ret_str; opt.check_passphrase_pattern = pargs->r.ret_str;
break; break;
case oCheckSymPassphrasePattern:
opt.check_sym_passphrase_pattern = pargs->r.ret_str;
break;
case oMaxPassphraseDays: case oMaxPassphraseDays:
opt.max_passphrase_days = pargs->r.ret_ulong; opt.max_passphrase_days = pargs->r.ret_ulong;
break; break;
@ -1440,6 +1447,8 @@ main (int argc, char **argv)
GC_OPT_FLAG_DEFAULT, MIN_PASSPHRASE_NONALPHA); GC_OPT_FLAG_DEFAULT, MIN_PASSPHRASE_NONALPHA);
es_printf ("check-passphrase-pattern:%lu:\n", es_printf ("check-passphrase-pattern:%lu:\n",
GC_OPT_FLAG_DEFAULT); GC_OPT_FLAG_DEFAULT);
es_printf ("check-sym-passphrase-pattern:%lu:\n",
GC_OPT_FLAG_DEFAULT);
es_printf ("max-passphrase-days:%lu:%d:\n", es_printf ("max-passphrase-days:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT, MAX_PASSPHRASE_DAYS); GC_OPT_FLAG_DEFAULT, MAX_PASSPHRASE_DAYS);
es_printf ("ssh-fingerprint-digest:%lu:\"%s:\n", es_printf ("ssh-fingerprint-digest:%lu:\"%s:\n",

View File

@ -205,14 +205,14 @@ if used in an options file.
@item -v @item -v
@item --verbose @itemx --verbose
@opindex verbose @opindex verbose
Outputs additional information while running. Outputs additional information while running.
You can increase the verbosity by giving several You can increase the verbosity by giving several
verbose commands to @command{gpg-agent}, such as @samp{-vv}. verbose commands to @command{gpg-agent}, such as @samp{-vv}.
@item -q @item -q
@item --quiet @itemx --quiet
@opindex quiet @opindex quiet
Try to be as quiet as possible. Try to be as quiet as possible.
@ -429,11 +429,15 @@ of digits or special characters a warning will be displayed. Defaults
to 1. to 1.
@item --check-passphrase-pattern @var{file} @item --check-passphrase-pattern @var{file}
@itemx --check-sym-passphrase-pattern @var{file}
@opindex check-passphrase-pattern @opindex check-passphrase-pattern
@opindex check-sym-passphrase-pattern
Check the passphrase against the pattern given in @var{file}. When Check the passphrase against the pattern given in @var{file}. When
entering a new passphrase matching one of these pattern a warning will entering a new passphrase matching one of these pattern a warning will
be displayed. @var{file} should be an absolute filename. The default is be displayed. @var{file} should be an absolute filename. The default
not to use any pattern file. 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 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 pattern or even against a complete dictionary is not very effective to

View File

@ -351,6 +351,8 @@ static known_option_t known_options_gpg_agent[] =
{ "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, { "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT },
{ "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, { "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
/**/ GC_ARG_TYPE_FILENAME }, /**/ 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 }, { "max-passphrase-days", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT },
{ "enable-passphrase-history", 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 }, { "pinentry-timeout", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED },