mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Implemented more gpg-agen options to support certain passphrase policies.
New tool gpg-check-pattern.
This commit is contained in:
parent
503f91e0ae
commit
15d0cb42a1
@ -1,3 +1,10 @@
|
|||||||
|
2007-08-27 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* configure.ac: Remove remaining support for internal regex.
|
||||||
|
Define DISABLE_REGEX automake conditional. Add option
|
||||||
|
--with-regex.
|
||||||
|
* autogen.sh [--build-w32]: Remove --disable-regex. Use --with-regex.
|
||||||
|
|
||||||
2007-08-16 Werner Koch <wk@g10code.com>
|
2007-08-16 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
Released 2.0.6.
|
Released 2.0.6.
|
||||||
|
6
NEWS
6
NEWS
@ -4,7 +4,11 @@ Noteworthy changes in version 2.0.7
|
|||||||
* Fixed encryption problem if duplicate certificates are in the
|
* Fixed encryption problem if duplicate certificates are in the
|
||||||
keybox.
|
keybox.
|
||||||
|
|
||||||
* Made it work on Windows Vista.
|
* Made it work on Windows Vista. Note that the entire Windows port
|
||||||
|
is still considered Beta.
|
||||||
|
|
||||||
|
* Add new options min-passphrase-nonalpha, check-passphrase-pattern
|
||||||
|
and enforce-passphrase-constraints to gpg-agent.
|
||||||
|
|
||||||
|
|
||||||
Noteworthy changes in version 2.0.6 (2007-08-16)
|
Noteworthy changes in version 2.0.6 (2007-08-16)
|
||||||
|
@ -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>
|
2007-08-22 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* findkey.c (O_BINARY): Make sure it is defined.
|
* findkey.c (O_BINARY): Make sure it is defined.
|
||||||
|
@ -80,8 +80,15 @@ struct
|
|||||||
unsigned long max_cache_ttl; /* Default. */
|
unsigned long max_cache_ttl; /* Default. */
|
||||||
unsigned long max_cache_ttl_ssh; /* for SSH. */
|
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. */
|
/* The require minmum length of a passphrase. */
|
||||||
unsigned int min_passphrase_len;
|
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. */
|
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);
|
const char *errtext);
|
||||||
int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok,
|
int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok,
|
||||||
const char *cancel);
|
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,
|
int agent_popup_message_start (ctrl_t ctrl,
|
||||||
const char *desc, const char *ok_btn);
|
const char *desc, const char *ok_btn);
|
||||||
void agent_popup_message_stop (ctrl_t ctrl);
|
void agent_popup_message_stop (ctrl_t ctrl);
|
||||||
|
@ -213,7 +213,9 @@ start_pinentry (ctrl_t ctrl)
|
|||||||
#endif
|
#endif
|
||||||
if (fflush (NULL))
|
if (fflush (NULL))
|
||||||
{
|
{
|
||||||
|
#ifndef HAVE_W32_SYSTEM
|
||||||
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
|
gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
|
||||||
|
#endif
|
||||||
log_error ("error flushing pending output: %s\n", strerror (errno));
|
log_error ("error flushing pending output: %s\n", strerror (errno));
|
||||||
/* At least Windows XP fails here with EBADF. According to docs
|
/* At least Windows XP fails here with EBADF. According to docs
|
||||||
and Wine an fflush(NULL) is the same as _flushall. However
|
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);
|
memset (&parm, 0, sizeof parm);
|
||||||
parm.size = pininfo->max_length;
|
parm.size = pininfo->max_length;
|
||||||
|
*pininfo->pin = 0; /* Reset the PIN. */
|
||||||
parm.buffer = (unsigned char*)pininfo->pin;
|
parm.buffer = (unsigned char*)pininfo->pin;
|
||||||
|
|
||||||
if (errtext)
|
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. */
|
/* The thread running the popup message. */
|
||||||
static void *
|
static void *
|
||||||
popup_message_thread (void *arg)
|
popup_message_thread (void *arg)
|
||||||
|
156
agent/genkey.c
156
agent/genkey.c
@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
#include "agent.h"
|
#include "agent.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
#include "exechelp.h"
|
||||||
|
#include "sysutils.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
store_key (gcry_sexp_t private, const char *passphrase, int force)
|
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
|
/* 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. */
|
asked to provide a different one. */
|
||||||
@ -78,7 +174,8 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
|
|||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
unsigned int minlen = opt.min_passphrase_len;
|
unsigned int minlen = opt.min_passphrase_len;
|
||||||
|
unsigned int minnonalpha = opt.min_passphrase_nonalpha;
|
||||||
|
|
||||||
if (!pw)
|
if (!pw)
|
||||||
pw = "";
|
pw = "";
|
||||||
|
|
||||||
@ -93,25 +190,60 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw)
|
|||||||
"be at least %u characters long.", minlen), minlen );
|
"be at least %u characters long.", minlen), minlen );
|
||||||
if (!desc)
|
if (!desc)
|
||||||
return gpg_error_from_syserror ();
|
return gpg_error_from_syserror ();
|
||||||
|
err = take_this_one_anyway (ctrl, desc);
|
||||||
err = agent_get_confirmation (ctrl, desc,
|
|
||||||
_("Take this one anyway"),
|
|
||||||
_("Enter new passphrase"));
|
|
||||||
xfree (desc);
|
xfree (desc);
|
||||||
if (err)
|
if (err)
|
||||||
return 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)
|
if (!*pw)
|
||||||
{
|
{
|
||||||
const char *desc = _("You have not entered a passphrase - "
|
const char *desc = (opt.enforce_passphrase_constraints?
|
||||||
"this is in general a bad idea!%0A"
|
_("You have not entered a passphrase!%0A"
|
||||||
"Please confirm that you do not want to "
|
"An empty passphrase is not allowed.") :
|
||||||
"have any protection on your key.");
|
_("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,
|
err = take_this_one_anyway2 (ctrl, desc,
|
||||||
_("Yes, protection is not needed"),
|
_("Yes, protection is not needed"));
|
||||||
_("Enter new passphrase"));
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,10 @@ enum cmd_and_opt_values
|
|||||||
oDefCacheTTLSSH,
|
oDefCacheTTLSSH,
|
||||||
oMaxCacheTTL,
|
oMaxCacheTTL,
|
||||||
oMaxCacheTTLSSH,
|
oMaxCacheTTLSSH,
|
||||||
|
oEnforcePassphraseConstraints,
|
||||||
oMinPassphraseLen,
|
oMinPassphraseLen,
|
||||||
|
oMinPassphraseNonalpha,
|
||||||
|
oCheckPassphrasePattern,
|
||||||
oUseStandardSocket,
|
oUseStandardSocket,
|
||||||
oNoUseStandardSocket,
|
oNoUseStandardSocket,
|
||||||
|
|
||||||
@ -149,7 +152,12 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
{ oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
|
{ oDefCacheTTLSSH, "default-cache-ttl-ssh", 4, "@" },
|
||||||
{ oMaxCacheTTL, "max-cache-ttl", 4, "@" },
|
{ oMaxCacheTTL, "max-cache-ttl", 4, "@" },
|
||||||
{ oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
|
{ oMaxCacheTTLSSH, "max-cache-ttl-ssh", 4, "@" },
|
||||||
|
|
||||||
|
{ oEnforcePassphraseConstraints, "enforce-passphrase-constraints", 0, "@"},
|
||||||
{ oMinPassphraseLen, "min-passphrase-len", 4, "@" },
|
{ oMinPassphraseLen, "min-passphrase-len", 4, "@" },
|
||||||
|
{ oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
|
||||||
|
{ oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
|
||||||
|
|
||||||
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
|
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
|
||||||
N_("do not use the PIN cache when signing")},
|
N_("do not use the PIN cache when signing")},
|
||||||
{ oAllowMarkTrusted, "allow-mark-trusted", 0,
|
{ 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 (120*60) /* 2 hours */
|
||||||
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
|
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
|
||||||
#define MIN_PASSPHRASE_LEN (8)
|
#define MIN_PASSPHRASE_LEN (8)
|
||||||
|
#define MIN_PASSPHRASE_NONALPHA (1)
|
||||||
|
|
||||||
/* The timer tick used for housekeeping stuff. For Windows we use a
|
/* The timer tick used for housekeeping stuff. For Windows we use a
|
||||||
longer period as the SetWaitableTimer seems to signal earlier than
|
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.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
|
||||||
opt.max_cache_ttl = MAX_CACHE_TTL;
|
opt.max_cache_ttl = MAX_CACHE_TTL;
|
||||||
opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
|
opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
|
||||||
|
opt.enforce_passphrase_constraints = 0;
|
||||||
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
|
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.ignore_cache_for_signing = 0;
|
||||||
opt.allow_mark_trusted = 0;
|
opt.allow_mark_trusted = 0;
|
||||||
opt.disable_scdaemon = 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 oMaxCacheTTL: opt.max_cache_ttl = pargs->r.ret_ulong; break;
|
||||||
case oMaxCacheTTLSSH: opt.max_cache_ttl_ssh = 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 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;
|
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 );
|
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
|
||||||
printf ("max-cache-ttl-ssh:%lu:%d:\n",
|
printf ("max-cache-ttl-ssh:%lu:%d:\n",
|
||||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
|
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",
|
printf ("min-passphrase-len:%lu:%d:\n",
|
||||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
|
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",
|
printf ("no-grab:%lu:\n",
|
||||||
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
||||||
printf ("ignore-cache-for-signing:%lu:\n",
|
printf ("ignore-cache-for-signing:%lu:\n",
|
||||||
|
@ -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
|
/* 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'.
|
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
|
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
|
actually gets inserted, the user is asked by means of the Pinentry
|
||||||
whether this is actual wants he want to do.
|
whether this is actual wants he want to do. */
|
||||||
*/
|
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
|
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 *desc;
|
||||||
char *fname;
|
char *fname;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
char *fprformatted;
|
||||||
|
|
||||||
|
|
||||||
/* Check whether we are at all allowed to modify the trustlist.
|
/* 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
|
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);
|
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||||
|
|
||||||
/* Insert a new one. */
|
/* Insert a new one. */
|
||||||
|
fprformatted = insert_colons (fpr);
|
||||||
|
if (!fprformatted)
|
||||||
|
return out_of_core ();
|
||||||
if (asprintf (&desc,
|
if (asprintf (&desc,
|
||||||
/* TRANSLATORS: This prompt is shown by the Pinentry
|
/* TRANSLATORS: This prompt is shown by the Pinentry
|
||||||
and has one special property: A "%%0A" is used by
|
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
|
plain % sign, you need to encode it as "%%25". The
|
||||||
second "%s" gets replaced by a hexdecimal
|
second "%s" gets replaced by a hexdecimal
|
||||||
fingerprint string whereas the first one receives
|
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"
|
_("Please verify that the certificate identified as:%%0A"
|
||||||
" \"%s\"%%0A"
|
" \"%s\"%%0A"
|
||||||
"has the fingerprint:%%0A"
|
"has the fingerprint:%%0A"
|
||||||
" %s"), name, fpr) < 0 )
|
" %s"), name, fprformatted) < 0 )
|
||||||
return out_of_core ();
|
{
|
||||||
|
xfree (fprformatted);
|
||||||
|
return out_of_core ();
|
||||||
|
}
|
||||||
|
|
||||||
/* TRANSLATORS: "Correct" is the label of a button and intended to
|
/* TRANSLATORS: "Correct" is the label of a button and intended to
|
||||||
be hit if the fingerprint matches the one of the CA. The other
|
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
|
gpgsm may stop asking further questions. We won't do this for
|
||||||
the second question of course. */
|
the second question of course. */
|
||||||
if (err)
|
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"
|
" \"%s\"%%0A"
|
||||||
"to correctly certify user certificates?"),
|
"to correctly certify user certificates?"),
|
||||||
name) < 0 )
|
name) < 0 )
|
||||||
return out_of_core ();
|
{
|
||||||
|
xfree (fprformatted);
|
||||||
|
return out_of_core ();
|
||||||
|
}
|
||||||
|
|
||||||
err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"));
|
err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"));
|
||||||
free (desc);
|
free (desc);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
{
|
||||||
|
xfree (fprformatted);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Now check again to avoid duplicates. We take the lock to make
|
/* 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
|
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))
|
if (!agent_istrusted (ctrl, fpr))
|
||||||
{
|
{
|
||||||
unlock_trusttable ();
|
unlock_trusttable ();
|
||||||
|
xfree (fprformatted);
|
||||||
return 0;
|
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));
|
log_error ("can't create `%s': %s\n", fname, gpg_strerror (err));
|
||||||
xfree (fname);
|
xfree (fname);
|
||||||
unlock_trusttable ();
|
unlock_trusttable ();
|
||||||
|
xfree (fprformatted);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
fputs (headerblurb, fp);
|
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));
|
log_error ("can't open `%s': %s\n", fname, gpg_strerror (err));
|
||||||
xfree (fname);
|
xfree (fname);
|
||||||
unlock_trusttable ();
|
unlock_trusttable ();
|
||||||
|
xfree (fprformatted);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append the key. */
|
/* Append the key. */
|
||||||
fputs ("\n# ", fp);
|
fputs ("\n# ", fp);
|
||||||
print_sanitized_string (fp, name, 0);
|
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))
|
if (ferror (fp))
|
||||||
err = gpg_error_from_syserror ();
|
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 ();
|
agent_reload_trustlist ();
|
||||||
xfree (fname);
|
xfree (fname);
|
||||||
unlock_trusttable ();
|
unlock_trusttable ();
|
||||||
|
xfree (fprformatted);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +91,9 @@ if test "$1" = "--build-w32"; then
|
|||||||
--with-libgcrypt-prefix=${w32root} \
|
--with-libgcrypt-prefix=${w32root} \
|
||||||
--with-libassuan-prefix=${w32root} \
|
--with-libassuan-prefix=${w32root} \
|
||||||
--with-zlib=${w32root} \
|
--with-zlib=${w32root} \
|
||||||
|
--with-regex=${w32root} \
|
||||||
--with-pth-prefix=${w32root} \
|
--with-pth-prefix=${w32root} \
|
||||||
--without-included-gettext \
|
--without-included-gettext "$@"
|
||||||
--disable-regex "$@"
|
|
||||||
rc=$?
|
rc=$?
|
||||||
exit $rc
|
exit $rc
|
||||||
fi
|
fi
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
2007-08-27 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* util.h (GNUPG_MODULE_NAME_CHECK_PATTERN): New.
|
||||||
|
* homedir.c (gnupg_module_name): Add it.
|
||||||
|
* exechelp.c (w32_fd_or_null) [W32]: New.
|
||||||
|
(gnupg_spawn_process_fd): New.
|
||||||
|
(gnupg_wait_process) [W32]: Close the handle after if the process has
|
||||||
|
returned.
|
||||||
|
|
||||||
2007-08-22 Werner Koch <wk@g10code.com>
|
2007-08-22 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
Updated estream from libestream.
|
Updated estream from libestream.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* exechelp.c - fork and exec helpers
|
/* exechelp.c - fork and exec helpers
|
||||||
* Copyright (C) 2004 Free Software Foundation, Inc.
|
* Copyright (C) 2004, 2007 Free Software Foundation, Inc.
|
||||||
*
|
*
|
||||||
* This file is part of GnuPG.
|
* This file is part of GnuPG.
|
||||||
*
|
*
|
||||||
@ -191,6 +191,23 @@ create_inheritable_pipe (int filedes[2])
|
|||||||
#endif /*HAVE_W32_SYSTEM*/
|
#endif /*HAVE_W32_SYSTEM*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_W32_SYSTEM
|
||||||
|
static HANDLE
|
||||||
|
w32_open_null (int for_write)
|
||||||
|
{
|
||||||
|
HANDLE hfile;
|
||||||
|
|
||||||
|
hfile = CreateFile ("nul",
|
||||||
|
for_write? GENERIC_WRITE : GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (hfile == INVALID_HANDLE_VALUE)
|
||||||
|
log_debug ("can't open `nul': %s\n", w32_strerror (-1));
|
||||||
|
return hfile;
|
||||||
|
}
|
||||||
|
#endif /*HAVE_W32_SYSTEM*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef HAVE_W32_SYSTEM
|
#ifndef HAVE_W32_SYSTEM
|
||||||
/* The exec core used right after the fork. This will never return. */
|
/* The exec core used right after the fork. This will never return. */
|
||||||
static void
|
static void
|
||||||
@ -257,9 +274,9 @@ do_exec (const char *pgmname, const char *argv[],
|
|||||||
stdin, write the output to OUTFILE, return a new stream in
|
stdin, write the output to OUTFILE, return a new stream in
|
||||||
STATUSFILE for stderr and the pid of the process in PID. The
|
STATUSFILE for stderr and the pid of the process in PID. The
|
||||||
arguments for the process are expected in the NULL terminated array
|
arguments for the process are expected in the NULL terminated array
|
||||||
ARGV. The program name itself should not be included there. if
|
ARGV. The program name itself should not be included there. If
|
||||||
PREEXEC is not NULL, that function will be called right before the
|
PREEXEC is not NULL, that function will be called right before the
|
||||||
exec.
|
exec. Calling gnupg_wait_process is required.
|
||||||
|
|
||||||
Returns 0 on success or an error code. */
|
Returns 0 on success or an error code. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
@ -439,6 +456,119 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Simplified version of gnupg_spawn_process. This function forks and
|
||||||
|
then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
|
||||||
|
and ERRFD to stderr (any of them may be -1 to connect them to
|
||||||
|
/dev/null). The arguments for the process are expected in the NULL
|
||||||
|
terminated array ARGV. The program name itself should not be
|
||||||
|
included there. Calling gnupg_wait_process is required.
|
||||||
|
|
||||||
|
Returns 0 on success or an error code. */
|
||||||
|
gpg_error_t
|
||||||
|
gnupg_spawn_process_fd (const char *pgmname, const char *argv[],
|
||||||
|
int infd, int outfd, int errfd, pid_t *pid)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_W32_SYSTEM
|
||||||
|
gpg_error_t err;
|
||||||
|
SECURITY_ATTRIBUTES sec_attr;
|
||||||
|
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
|
||||||
|
STARTUPINFO si;
|
||||||
|
char *cmdline;
|
||||||
|
int i;
|
||||||
|
HANDLE stdhd[3];
|
||||||
|
|
||||||
|
/* Setup return values. */
|
||||||
|
*pid = (pid_t)(-1);
|
||||||
|
|
||||||
|
/* Prepare security attributes. */
|
||||||
|
memset (&sec_attr, 0, sizeof sec_attr );
|
||||||
|
sec_attr.nLength = sizeof sec_attr;
|
||||||
|
sec_attr.bInheritHandle = FALSE;
|
||||||
|
|
||||||
|
/* Build the command line. */
|
||||||
|
err = build_w32_commandline (pgmname, argv, &cmdline);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
memset (&si, 0, sizeof si);
|
||||||
|
si.cb = sizeof (si);
|
||||||
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
|
||||||
|
stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE;
|
||||||
|
stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
|
||||||
|
stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE;
|
||||||
|
si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd);
|
||||||
|
si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd);
|
||||||
|
si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd);
|
||||||
|
|
||||||
|
log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
|
||||||
|
if (!CreateProcess (pgmname, /* Program to start. */
|
||||||
|
cmdline, /* Command line arguments. */
|
||||||
|
&sec_attr, /* Process security attributes. */
|
||||||
|
&sec_attr, /* Thread security attributes. */
|
||||||
|
TRUE, /* Inherit handles. */
|
||||||
|
(CREATE_DEFAULT_ERROR_MODE
|
||||||
|
| GetPriorityClass (GetCurrentProcess ())
|
||||||
|
| CREATE_SUSPENDED),
|
||||||
|
NULL, /* Environment. */
|
||||||
|
NULL, /* Use current drive/directory. */
|
||||||
|
&si, /* Startup information. */
|
||||||
|
&pi /* Returns process information. */
|
||||||
|
))
|
||||||
|
{
|
||||||
|
log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
|
||||||
|
err = gpg_error (GPG_ERR_GENERAL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err = 0;
|
||||||
|
xfree (cmdline);
|
||||||
|
for (i=0; i < 3; i++)
|
||||||
|
if (stdhd[i] != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle (stdhd[i]);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
|
||||||
|
" dwProcessID=%d dwThreadId=%d\n",
|
||||||
|
pi.hProcess, pi.hThread,
|
||||||
|
(int) pi.dwProcessId, (int) pi.dwThreadId);
|
||||||
|
|
||||||
|
/* Process has been created suspended; resume it now. */
|
||||||
|
ResumeThread (pi.hThread);
|
||||||
|
CloseHandle (pi.hThread);
|
||||||
|
|
||||||
|
*pid = handle_to_pid (pi.hProcess);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#else /* !HAVE_W32_SYSTEM */
|
||||||
|
gpg_error_t err;
|
||||||
|
|
||||||
|
#ifdef USE_GNU_PTH
|
||||||
|
*pid = pth_fork? pth_fork () : fork ();
|
||||||
|
#else
|
||||||
|
*pid = fork ();
|
||||||
|
#endif
|
||||||
|
if (*pid == (pid_t)(-1))
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
log_error (_("error forking process: %s\n"), strerror (errno));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*pid)
|
||||||
|
{
|
||||||
|
gcry_control (GCRYCTL_TERM_SECMEM);
|
||||||
|
/* Run child. */
|
||||||
|
do_exec (pgmname, argv, infd, outfd, errfd, NULL);
|
||||||
|
/*NOTREACHED*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#endif /* !HAVE_W32_SYSTEM */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Wait for the process identified by PID to terminate. PGMNAME should
|
/* Wait for the process identified by PID to terminate. PGMNAME should
|
||||||
be the same as suplieed to the spawn fucntion and is only used for
|
be the same as suplieed to the spawn fucntion and is only used for
|
||||||
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
|
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
|
||||||
@ -483,6 +613,7 @@ gnupg_wait_process (const char *pgmname, pid_t pid)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
ec = 0;
|
ec = 0;
|
||||||
|
CloseHandle (proc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -28,16 +28,31 @@
|
|||||||
arguments for the process are expected in the NULL terminated array
|
arguments for the process are expected in the NULL terminated array
|
||||||
ARGV. The program name itself should not be included there. if
|
ARGV. The program name itself should not be included there. if
|
||||||
PREEXEC is not NULL, that function will be called right before the
|
PREEXEC is not NULL, that function will be called right before the
|
||||||
exec. Returns 0 on success or an error code. */
|
exec. Calling gnupg_wait_process is required. Returns 0 on
|
||||||
|
success or an error code. */
|
||||||
gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[],
|
gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[],
|
||||||
FILE *infile, FILE *outfile,
|
FILE *infile, FILE *outfile,
|
||||||
void (*preexec)(void),
|
void (*preexec)(void),
|
||||||
FILE **statusfile, pid_t *pid);
|
FILE **statusfile, pid_t *pid);
|
||||||
|
|
||||||
|
|
||||||
|
/* Simplified version of gnupg_spawn_process. This function forks and
|
||||||
|
then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout
|
||||||
|
and ERRFD to stderr (any of them may be -1 to connect them to
|
||||||
|
/dev/null). The arguments for the process are expected in the NULL
|
||||||
|
terminated array ARGV. The program name itself should not be
|
||||||
|
included there. Calling gnupg_wait_process is required. Returns 0
|
||||||
|
on success or an error code. */
|
||||||
|
gpg_error_t gnupg_spawn_process_fd (const char *pgmname,
|
||||||
|
const char *argv[],
|
||||||
|
int infd, int outfd, int errfd,
|
||||||
|
pid_t *pid);
|
||||||
|
|
||||||
|
|
||||||
/* Wait for the process identified by PID to terminate. PGMNAME should
|
/* Wait for the process identified by PID to terminate. PGMNAME should
|
||||||
be the same as suplieed to the spawn fucntion and is only used for
|
be the same as supplied to the spawn fucntion and is only used for
|
||||||
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
|
diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL
|
||||||
any failures of the spawned program or other error codes.*/
|
for any failures of the spawned program or other error codes.*/
|
||||||
gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid);
|
gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid);
|
||||||
|
|
||||||
|
|
||||||
|
@ -369,6 +369,9 @@ gnupg_module_name (int which)
|
|||||||
X(libexecdir, "gpg-protect-tool");
|
X(libexecdir, "gpg-protect-tool");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case GNUPG_MODULE_NAME_CHECK_PATTERN:
|
||||||
|
X(libexecdir, "gpg-check-pattern");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BUG ();
|
BUG ();
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,7 @@ const char *dirmngr_socket_name (void);
|
|||||||
#define GNUPG_MODULE_NAME_SCDAEMON 3
|
#define GNUPG_MODULE_NAME_SCDAEMON 3
|
||||||
#define GNUPG_MODULE_NAME_DIRMNGR 4
|
#define GNUPG_MODULE_NAME_DIRMNGR 4
|
||||||
#define GNUPG_MODULE_NAME_PROTECT_TOOL 5
|
#define GNUPG_MODULE_NAME_PROTECT_TOOL 5
|
||||||
|
#define GNUPG_MODULE_NAME_CHECK_PATTERN 6
|
||||||
const char *gnupg_module_name (int which);
|
const char *gnupg_module_name (int which);
|
||||||
|
|
||||||
|
|
||||||
|
63
configure.ac
63
configure.ac
@ -1025,7 +1025,6 @@ GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Sanity check regex. Tests adapted from mutt.
|
# Sanity check regex. Tests adapted from mutt.
|
||||||
# FIXME: We should use the the regex from gnulib
|
|
||||||
#
|
#
|
||||||
AC_MSG_CHECKING([whether regular expression support is requested])
|
AC_MSG_CHECKING([whether regular expression support is requested])
|
||||||
AC_ARG_ENABLE(regex,
|
AC_ARG_ENABLE(regex,
|
||||||
@ -1035,20 +1034,28 @@ AC_ARG_ENABLE(regex,
|
|||||||
AC_MSG_RESULT($use_regex)
|
AC_MSG_RESULT($use_regex)
|
||||||
|
|
||||||
if test "$use_regex" = yes ; then
|
if test "$use_regex" = yes ; then
|
||||||
AC_MSG_CHECKING([whether the included regex lib is requested])
|
_cppflags="${CPPFLAGS}"
|
||||||
AC_ARG_WITH(included-regex,
|
_ldflags="${LDFLAGS}"
|
||||||
[ --with-included-regex use the included GNU regex library],
|
AC_ARG_WITH(regex,
|
||||||
[gnupg_cv_included_regex="$withval"],[gnupg_cv_included_regex=no])
|
AC_HELP_STRING([--with-regex=DIR],[look for regex in DIR]),
|
||||||
AC_MSG_RESULT($gnupg_cv_included_regex)
|
[
|
||||||
|
if test -d "$withval" ; then
|
||||||
|
CPPFLAGS="${CPPFLAGS} -I$withval/include"
|
||||||
|
LDFLAGS="${LDFLAGS} -L$withval/lib"
|
||||||
|
fi
|
||||||
|
],withval="")
|
||||||
|
|
||||||
if test $gnupg_cv_included_regex = no ; then
|
# Does the system have regex functions at all?
|
||||||
# Does the system have regex functions at all?
|
AC_SEARCH_LIBS([regcomp], [regex])
|
||||||
AC_CHECK_FUNC(regcomp,gnupg_cv_included_regex=no,
|
AC_CHECK_FUNC(regcomp, gnupg_cv_have_regex=yes, gnupg_cv_have_regex=no)
|
||||||
gnupg_cv_included_regex=yes)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $gnupg_cv_included_regex = no ; then
|
if test $gnupg_cv_have_regex = no; then
|
||||||
AC_CACHE_CHECK([whether your system's regexp library is broken],
|
use_regex=no
|
||||||
|
else
|
||||||
|
if test x"$cross_compiling" = xyes; then
|
||||||
|
AC_MSG_WARN([cross compiling; assuming regexp libray is not broken])
|
||||||
|
else
|
||||||
|
AC_CACHE_CHECK([whether your system's regexp library is broken],
|
||||||
[gnupg_cv_regex_broken],
|
[gnupg_cv_regex_broken],
|
||||||
AC_TRY_RUN([
|
AC_TRY_RUN([
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -1056,20 +1063,20 @@ if test "$use_regex" = yes ; then
|
|||||||
main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
|
main() { regex_t blah ; regmatch_t p; p.rm_eo = p.rm_eo; return regcomp(&blah, "foo.*bar", REG_NOSUB) || regexec (&blah, "foobar", 0, NULL, 0); }],
|
||||||
gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes))
|
gnupg_cv_regex_broken=no, gnupg_cv_regex_broken=yes, gnupg_cv_regex_broken=yes))
|
||||||
|
|
||||||
if test $gnupg_cv_regex_broken = yes ; then
|
if test $gnupg_cv_regex_broken = yes; then
|
||||||
AC_MSG_WARN(your regex is broken - using the included GNU regex instead.)
|
AC_MSG_WARN([your regex is broken - disabling regex use])
|
||||||
gnupg_cv_included_regex=yes
|
use_regex=no
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
CPPFLAGS="${_cppflags}"
|
||||||
if test $gnupg_cv_included_regex = yes; then
|
LDFLAGS="${_ldflags}"
|
||||||
AC_DEFINE(USE_INTERNAL_REGEX,1,[ Define if you want to use the included regex lib ])
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
AC_DEFINE(DISABLE_REGEX,1,[ Define to disable regular expression support ])
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(USE_INTERNAL_REGEX, test x"$gnupg_cv_included_regex" = xyes)
|
if test "$use_regex" != yes ; then
|
||||||
|
AC_DEFINE(DISABLE_REGEX,1, [Define to disable regular expression support])
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(DISABLE_REGEX, test x"$use_regex" != xyes)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1397,6 +1404,12 @@ echo "
|
|||||||
Default scdaemon: $show_gnupg_scdaemon_pgm
|
Default scdaemon: $show_gnupg_scdaemon_pgm
|
||||||
Default dirmngr: $show_gnupg_dirmngr_pgm
|
Default dirmngr: $show_gnupg_dirmngr_pgm
|
||||||
|
|
||||||
PKITS based tests: $run_pkits_tests
|
PKITS based tests: $run_pkits_tests"
|
||||||
|
if test x"$use_regex" != xyes ; then
|
||||||
|
echo "
|
||||||
|
Warning: No regular expression support available.
|
||||||
|
OpenPGP trust signatures won't work.
|
||||||
|
gpg-check-pattern will not be build.
|
||||||
"
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2007-08-27 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* examples/pwpattern.list: New.
|
||||||
|
|
||||||
2007-08-24 Werner Koch <wk@g10code.com>
|
2007-08-24 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* debugging.texi (Common Problems): Add "A root certifciate does
|
* debugging.texi (Common Problems): Add "A root certifciate does
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
## Process this file with automake to produce Makefile.in
|
## Process this file with automake to produce Makefile.in
|
||||||
|
|
||||||
examples = examples/README examples/scd-event examples/trustlist.txt \
|
examples = examples/README examples/scd-event examples/trustlist.txt \
|
||||||
examples/gpgconf.conf
|
examples/gpgconf.conf examples/pwpattern.list
|
||||||
|
|
||||||
EXTRA_DIST = DETAILS HACKING TRANSLATE OpenPGP KEYSERVER samplekeys.asc \
|
EXTRA_DIST = DETAILS HACKING TRANSLATE OpenPGP KEYSERVER samplekeys.asc \
|
||||||
gnupg-logo.eps gnupg-logo.pdf gnupg-logo.png \
|
gnupg-logo.eps gnupg-logo.pdf gnupg-logo.png \
|
||||||
|
48
doc/examples/pwpattern.list
Normal file
48
doc/examples/pwpattern.list
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# pwpattern.list -*- default-generic -*-
|
||||||
|
#
|
||||||
|
# This is an example for a pattern file as used by gpg-check-pattern.
|
||||||
|
# The file is line based with comment lines beginning on the *first*
|
||||||
|
# position with a '#'. Empty lines and lines with just spaces are
|
||||||
|
# ignored. The other lines may be verbatim patterns and match as they
|
||||||
|
# are (trailing spaces are ignored) or extended regular expressions
|
||||||
|
# indicated by a / in the first column and terminated by another / or
|
||||||
|
# end of line. All comparisons are case insensitive.
|
||||||
|
|
||||||
|
# Reject the usual metavariables. Usual not required because
|
||||||
|
# gpg-agent can be used to reject all passphrases shorter than 8
|
||||||
|
# charactes.
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
|
||||||
|
# As well as very common passwords. Note that gpg-agent can be used
|
||||||
|
# to reject them due to missing non-alpha characters.
|
||||||
|
password
|
||||||
|
passwort
|
||||||
|
passphrase
|
||||||
|
mantra
|
||||||
|
test
|
||||||
|
abc
|
||||||
|
egal
|
||||||
|
|
||||||
|
# German number plates.
|
||||||
|
/^[A-Z]{1,3}[ ]*-[ ]*[A-Z]{1,2}[ ]*[0-9]+/
|
||||||
|
|
||||||
|
# Dates (very limited, only ISO dates). */
|
||||||
|
/^[012][0-9][0-9][0-9]-[012][0-9]-[0123][0-9]$/
|
||||||
|
|
||||||
|
# Arbitrary strings
|
||||||
|
the quick brown fox jumps over the lazy dogs back
|
||||||
|
no-password
|
||||||
|
no password
|
||||||
|
|
||||||
|
12345678
|
||||||
|
123456789
|
||||||
|
1234567890
|
||||||
|
87654321
|
||||||
|
987654321
|
||||||
|
0987654321
|
||||||
|
qwertyuiop
|
||||||
|
qwertzuiop
|
||||||
|
asdfghjkl
|
||||||
|
zxcvbnm
|
@ -334,11 +334,38 @@ Set the maximum time a cache entry used for SSH keys is valid to @var{n}
|
|||||||
seconds. After this time a cache entry will get expired even if it has
|
seconds. After this time a cache entry will get expired even if it has
|
||||||
been accessed recently. The default are 2 hours (7200 seconds).
|
been accessed recently. The default are 2 hours (7200 seconds).
|
||||||
|
|
||||||
|
@item --enforce-passphrase-constraints
|
||||||
|
@opindex enforce-passphrase-constraints
|
||||||
|
Enforce the passphrase constraints by not allowing the user to bypass
|
||||||
|
them using the ``Take it anyway'' button.
|
||||||
|
|
||||||
@item --min-passphrase-len @var{n}
|
@item --min-passphrase-len @var{n}
|
||||||
@opindex min-passphrase-len
|
@opindex min-passphrase-len
|
||||||
Set the minimal length of a passphrase. When entering a new passphrase
|
Set the minimal length of a passphrase. When entering a new passphrase
|
||||||
shorter than this value a warning will be displayed. Defaults to 8.
|
shorter than this value a warning will be displayed. Defaults to 8.
|
||||||
|
|
||||||
|
@item --min-passphrase-nonalpha @var{n}
|
||||||
|
@opindex min-passphrase-nonalpha
|
||||||
|
Set the minimal number of digits or special characters required in a
|
||||||
|
passphrase. When entering a new passphrase with less than this number
|
||||||
|
of digits or special characters a warning will be displayed. Defaults
|
||||||
|
to 1.
|
||||||
|
|
||||||
|
@item --check-passphrase-pattern @var{file}
|
||||||
|
@opindex check-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.
|
||||||
|
|
||||||
|
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
|
||||||
|
enforce good passphrases. Users will soon figure up ways to bypass such
|
||||||
|
a policy. A better policy is to educate users on good security
|
||||||
|
behavior and optional to run a passphrase cracker regularly on all
|
||||||
|
users passphrases t catch the very simple ones.
|
||||||
|
|
||||||
|
|
||||||
@item --pinentry-program @var{filename}
|
@item --pinentry-program @var{filename}
|
||||||
@opindex pinentry-program
|
@opindex pinentry-program
|
||||||
Use program @var{filename} as the PIN entry. The default is installation
|
Use program @var{filename} as the PIN entry. The default is installation
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2007-08-27 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* trustdb.c (USE_INTERNAL_REGEX): Remove support.
|
||||||
|
|
||||||
2007-08-24 Werner Koch <wk@g10code.com>
|
2007-08-24 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* keyring.c (keyring_register_filename): Use same_file_p().
|
* keyring.c (keyring_register_filename): Use same_file_p().
|
||||||
|
@ -26,11 +26,7 @@
|
|||||||
|
|
||||||
#ifndef DISABLE_REGEX
|
#ifndef DISABLE_REGEX
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#ifdef USE_INTERNAL_REGEX
|
|
||||||
#include "_regex.h"
|
|
||||||
#else
|
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#endif
|
|
||||||
#endif /* !DISABLE_REGEX */
|
#endif /* !DISABLE_REGEX */
|
||||||
|
|
||||||
#include "gpg.h"
|
#include "gpg.h"
|
||||||
|
@ -339,7 +339,7 @@ make_filename( const char *first_part, ... )
|
|||||||
|
|
||||||
|
|
||||||
/* Compare whether the filenames are identical. This is a
|
/* Compare whether the filenames are identical. This is a
|
||||||
specialversion of strcmp() taking the semantics of filenames in
|
special version of strcmp() taking the semantics of filenames in
|
||||||
account. Note that this function works only on the supplied names
|
account. Note that this function works only on the supplied names
|
||||||
without considereing any context like the current directory. See
|
without considereing any context like the current directory. See
|
||||||
also same_file_p(). */
|
also same_file_p(). */
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2007-08-24 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* Makefile.am (common_libs): Swap libkeybox and jnlib.
|
||||||
|
|
||||||
2007-08-23 Werner Koch <wk@g10code.com>
|
2007-08-23 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* certlist.c (gpgsm_certs_identical_p): New.
|
* certlist.c (gpgsm_certs_identical_p): New.
|
||||||
|
@ -52,7 +52,7 @@ gpgsm_SOURCES = \
|
|||||||
qualified.c
|
qualified.c
|
||||||
|
|
||||||
|
|
||||||
common_libs = $(libcommon) ../jnlib/libjnlib.a ../kbx/libkeybox.a \
|
common_libs = $(libcommon) ../kbx/libkeybox.a ../jnlib/libjnlib.a \
|
||||||
../gl/libgnu.a
|
../gl/libgnu.a
|
||||||
|
|
||||||
gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \
|
gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2007-08-27 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* gpg-check-pattern.c: New
|
||||||
|
* Makefile.am (libexec_PROGRAMS): Add unless DISABLE_REGEX.
|
||||||
|
|
||||||
|
2007-08-24 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* gpgconf-comp.c <gpg-agent>: Add options --check-passphrase-pattern,
|
||||||
|
--min-passphrase-nonalpha and --enforce-passphrase-constraints and
|
||||||
|
move them into a new "passphrase policy" group.
|
||||||
|
(gc_component) [W32]: Enable dirmngr.
|
||||||
|
|
||||||
2007-08-21 Werner Koch <wk@g10code.com>
|
2007-08-21 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* gpgkey2ssh.c (key_to_blob): Use gnupg_tmpfile().
|
* gpgkey2ssh.c (key_to_blob): Use gnupg_tmpfile().
|
||||||
|
@ -46,6 +46,10 @@ if !HAVE_W32_SYSTEM
|
|||||||
bin_PROGRAMS += watchgnupg gpgparsemail
|
bin_PROGRAMS += watchgnupg gpgparsemail
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if !DISABLE_REGEX
|
||||||
|
libexec_PROGRAMS = gpg-check-pattern
|
||||||
|
endif
|
||||||
|
|
||||||
noinst_PROGRAMS = clean-sat mk-tdata make-dns-cert gpgsplit
|
noinst_PROGRAMS = clean-sat mk-tdata make-dns-cert gpgsplit
|
||||||
|
|
||||||
common_libs = $(libcommon) ../jnlib/libjnlib.a ../gl/libgnu.a
|
common_libs = $(libcommon) ../jnlib/libjnlib.a ../gl/libgnu.a
|
||||||
@ -86,6 +90,13 @@ gpgkey2ssh_LDADD = $(common_libs) \
|
|||||||
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
|
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV)
|
||||||
|
|
||||||
|
|
||||||
|
if !DISABLE_REGEX
|
||||||
|
gpg_check_pattern_SOURCES = gpg-check-pattern.c
|
||||||
|
gpg_check_pattern_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
|
||||||
|
gpg_check_pattern_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
|
||||||
|
$(LIBINTL) $(LIBICONV) $(W32SOCKLIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
# Make sure that all libs are build before we use them. This is
|
# Make sure that all libs are build before we use them. This is
|
||||||
# important for things like make -j2.
|
# important for things like make -j2.
|
||||||
$(PROGRAMS): $(common_libs) $(pwquery_libs)
|
$(PROGRAMS): $(common_libs) $(pwquery_libs)
|
||||||
|
504
tools/gpg-check-pattern.c
Normal file
504
tools/gpg-check-pattern.c
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
/* gpg-check-pattern.c - A tool to check passphrases against pattern.
|
||||||
|
* Copyright (C) 2007 Free Software Foundation, Inc.
|
||||||
|
*
|
||||||
|
* This file is part of GnuPG.
|
||||||
|
*
|
||||||
|
* GnuPG is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GnuPG is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
# include <locale.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LANGINFO_CODESET
|
||||||
|
# include <langinfo.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_DOSISH_SYSTEM
|
||||||
|
# include <fcntl.h> /* for setmode() */
|
||||||
|
#endif
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define JNLIB_NEED_LOG_LOGV
|
||||||
|
#include "util.h"
|
||||||
|
#include "i18n.h"
|
||||||
|
#include "sysutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum cmd_and_opt_values
|
||||||
|
{ aNull = 0,
|
||||||
|
oVerbose = 'v',
|
||||||
|
oArmor = 'a',
|
||||||
|
oPassphrase = 'P',
|
||||||
|
|
||||||
|
oProtect = 'p',
|
||||||
|
oUnprotect = 'u',
|
||||||
|
oNull = '0',
|
||||||
|
|
||||||
|
oNoVerbose = 500,
|
||||||
|
oCheck,
|
||||||
|
|
||||||
|
oHomedir
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The list of commands and options. */
|
||||||
|
static ARGPARSE_OPTS opts[] = {
|
||||||
|
|
||||||
|
{ 301, NULL, 0, N_("@Options:\n ") },
|
||||||
|
|
||||||
|
{ oVerbose, "verbose", 0, "verbose" },
|
||||||
|
|
||||||
|
{ oHomedir, "homedir", 2, "@" },
|
||||||
|
{ oCheck, "check", 0, "run only a syntax check on the patternfile" },
|
||||||
|
{ oNull, "null", 0, "input is expected to be null delimited" },
|
||||||
|
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Global options are accessed through the usual OPT structure. */
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
int verbose;
|
||||||
|
const char *homedir;
|
||||||
|
int checkonly;
|
||||||
|
int null;
|
||||||
|
} opt;
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PAT_NULL, /* Indicates end of the array. */
|
||||||
|
PAT_STRING, /* The pattern is a simple string. */
|
||||||
|
PAT_REGEX /* The pattern is an extended regualr expression. */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* An object to decibe an item of our pattern table. */
|
||||||
|
struct pattern_s
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
unsigned int lineno; /* Line number of the pattern file. */
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const char *string; /* Pointer to the actual string (nul termnated). */
|
||||||
|
size_t length; /* The length of this string (strlen). */
|
||||||
|
} s; /*PAT_STRING*/
|
||||||
|
struct {
|
||||||
|
/* We allocate the regex_t because this type is larger than what
|
||||||
|
we need for PAT_STRING and we expect only a few regex in a
|
||||||
|
patternfile. It would be a waste of core to have so many
|
||||||
|
unused stuff in the table. */
|
||||||
|
regex_t *regex;
|
||||||
|
} r; /*PAT_REGEX*/
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
typedef struct pattern_s pattern_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*** Local prototypes ***/
|
||||||
|
static char *read_file (const char *fname, size_t *r_length);
|
||||||
|
static pattern_t *parse_pattern_file (char *data, size_t datalen);
|
||||||
|
static void process (FILE *fp, pattern_t *patarray);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Info function for usage(). */
|
||||||
|
static const char *
|
||||||
|
my_strusage (int level)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case 11: p = "gpg-check-pattern (GnuPG)";
|
||||||
|
break;
|
||||||
|
case 13: p = VERSION; break;
|
||||||
|
case 17: p = PRINTABLE_OS_NAME; break;
|
||||||
|
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 40:
|
||||||
|
p = _("Usage: gpg-check-pattern [options] patternfile (-h for help)\n");
|
||||||
|
break;
|
||||||
|
case 41:
|
||||||
|
p = _("Syntax: gpg-check-pattern [options] patternfile\n"
|
||||||
|
"Check a passphrase given on stdin against the patternfile\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: p = NULL;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv )
|
||||||
|
{
|
||||||
|
ARGPARSE_ARGS pargs;
|
||||||
|
char *raw_pattern;
|
||||||
|
size_t raw_pattern_length;
|
||||||
|
pattern_t *patternarray;
|
||||||
|
|
||||||
|
set_strusage (my_strusage);
|
||||||
|
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||||
|
log_set_prefix ("gpg-check-pattern", 1);
|
||||||
|
|
||||||
|
/* Make sure that our subsystems are ready. */
|
||||||
|
init_common_subsystems ();
|
||||||
|
|
||||||
|
i18n_init ();
|
||||||
|
|
||||||
|
/* We need Libgcrypt for hashing. */
|
||||||
|
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||||
|
{
|
||||||
|
log_fatal ( _("%s is too old (need %s, have %s)\n"), "libgcrypt",
|
||||||
|
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_libgcrypt_logging ();
|
||||||
|
gcry_control (GCRYCTL_INIT_SECMEM, 4096, 0);
|
||||||
|
|
||||||
|
opt.homedir = default_homedir ();
|
||||||
|
|
||||||
|
pargs.argc = &argc;
|
||||||
|
pargs.argv = &argv;
|
||||||
|
pargs.flags= 1; /* (do not remove the args) */
|
||||||
|
while (arg_parse (&pargs, opts) )
|
||||||
|
{
|
||||||
|
switch (pargs.r_opt)
|
||||||
|
{
|
||||||
|
case oVerbose: opt.verbose++; break;
|
||||||
|
case oHomedir: opt.homedir = pargs.r.ret_str; break;
|
||||||
|
case oCheck: opt.checkonly = 1; break;
|
||||||
|
case oNull: opt.null = 1; break;
|
||||||
|
|
||||||
|
default : pargs.err = 2; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (log_get_errorcount(0))
|
||||||
|
exit (2);
|
||||||
|
|
||||||
|
if (argc != 1)
|
||||||
|
usage (1);
|
||||||
|
|
||||||
|
/* We read the entire pattern file into our memory and parse it
|
||||||
|
using a separate function. This allows us to eventual do the
|
||||||
|
reading while running setuid so that the pattern file can be
|
||||||
|
hidden from regular users. I am not sure whether this makes
|
||||||
|
sense, but lets be prepared for it. */
|
||||||
|
raw_pattern = read_file (*argv, &raw_pattern_length);
|
||||||
|
if (!raw_pattern)
|
||||||
|
exit (2);
|
||||||
|
|
||||||
|
patternarray = parse_pattern_file (raw_pattern, raw_pattern_length);
|
||||||
|
if (!patternarray)
|
||||||
|
exit (1);
|
||||||
|
if (opt.checkonly)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifdef HAVE_DOSISH_SYSTEM
|
||||||
|
setmode (fileno (stdin) , O_BINARY );
|
||||||
|
#endif
|
||||||
|
process (stdin, patternarray);
|
||||||
|
|
||||||
|
return log_get_errorcount(0)? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Read a file FNAME into a buffer and return that malloced buffer.
|
||||||
|
Caller must free the buffer. On error NULL is returned, on success
|
||||||
|
the valid length of the buffer is stored at R_LENGTH. The returned
|
||||||
|
buffer is guarnteed to be nul terminated. */
|
||||||
|
static char *
|
||||||
|
read_file (const char *fname, size_t *r_length)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *buf;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
if (!strcmp (fname, "-"))
|
||||||
|
{
|
||||||
|
size_t nread, bufsize = 0;
|
||||||
|
|
||||||
|
fp = stdin;
|
||||||
|
#ifdef HAVE_DOSISH_SYSTEM
|
||||||
|
setmode ( fileno(fp) , O_BINARY );
|
||||||
|
#endif
|
||||||
|
buf = NULL;
|
||||||
|
buflen = 0;
|
||||||
|
#define NCHUNK 8192
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bufsize += NCHUNK;
|
||||||
|
if (!buf)
|
||||||
|
buf = xmalloc (bufsize+1);
|
||||||
|
else
|
||||||
|
buf = xrealloc (buf, bufsize+1);
|
||||||
|
|
||||||
|
nread = fread (buf+buflen, 1, NCHUNK, fp);
|
||||||
|
if (nread < NCHUNK && ferror (fp))
|
||||||
|
{
|
||||||
|
log_error ("error reading `[stdin]': %s\n", strerror (errno));
|
||||||
|
xfree (buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buflen += nread;
|
||||||
|
}
|
||||||
|
while (nread == NCHUNK);
|
||||||
|
#undef NCHUNK
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
fp = fopen (fname, "rb");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
log_error ("can't open `%s': %s\n", fname, strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat (fileno(fp), &st))
|
||||||
|
{
|
||||||
|
log_error ("can't stat `%s': %s\n", fname, strerror (errno));
|
||||||
|
fclose (fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buflen = st.st_size;
|
||||||
|
buf = xmalloc (buflen+1);
|
||||||
|
if (fread (buf, buflen, 1, fp) != 1)
|
||||||
|
{
|
||||||
|
log_error ("error reading `%s': %s\n", fname, strerror (errno));
|
||||||
|
fclose (fp);
|
||||||
|
xfree (buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose (fp);
|
||||||
|
}
|
||||||
|
buf[buflen] = 0;
|
||||||
|
*r_length = buflen;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_regerror (int errcode, regex_t *compiled)
|
||||||
|
{
|
||||||
|
size_t length = regerror (errcode, compiled, NULL, 0);
|
||||||
|
char *buffer = xmalloc (length);
|
||||||
|
regerror (errcode, compiled, buffer, length);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the pattern given in the memory aread DATA/DATALEN and return
|
||||||
|
a new pattern array. The end of the array is indicated by a NULL
|
||||||
|
entry. On error an error message is printed and the fucntion
|
||||||
|
returns NULL. Note that the function modifies DATA and assumes
|
||||||
|
that data is nul terminated (even if this is one byte past
|
||||||
|
DATALEN). */
|
||||||
|
static pattern_t *
|
||||||
|
parse_pattern_file (char *data, size_t datalen)
|
||||||
|
{
|
||||||
|
char *p, *p2;
|
||||||
|
size_t n;
|
||||||
|
pattern_t *array;
|
||||||
|
size_t arraysize, arrayidx;
|
||||||
|
unsigned int lineno = 0;
|
||||||
|
|
||||||
|
/* Estimate the number of entries by counting the non-comment lines. */
|
||||||
|
arraysize = 0;
|
||||||
|
p = data;
|
||||||
|
for (n = datalen; n && (p2 = memchr (p, '\n', n)); p2++, n -= p2 - p, p = p2)
|
||||||
|
if (*p != '#')
|
||||||
|
arraysize++;
|
||||||
|
arraysize += 2; /* For the terminating NULL and a last line w/o a LF. */
|
||||||
|
|
||||||
|
array = xcalloc (arraysize, sizeof *array);
|
||||||
|
arrayidx = 0;
|
||||||
|
|
||||||
|
/* Loop over all lines. */
|
||||||
|
while (datalen && data)
|
||||||
|
{
|
||||||
|
lineno++;
|
||||||
|
p = data;
|
||||||
|
p2 = data = memchr (p, '\n', datalen);
|
||||||
|
if (p2)
|
||||||
|
{
|
||||||
|
*data++ = 0;
|
||||||
|
datalen -= data - p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
p2 = p + datalen;
|
||||||
|
assert (!*p2);
|
||||||
|
p2--;
|
||||||
|
while (isascii (*p) && isspace (*p))
|
||||||
|
p++;
|
||||||
|
if (*p == '#')
|
||||||
|
continue;
|
||||||
|
while (p2 > p && isascii (*p2) && isspace (*p2))
|
||||||
|
*p2-- = 0;
|
||||||
|
if (!*p)
|
||||||
|
continue;
|
||||||
|
assert (arrayidx < arraysize);
|
||||||
|
array[arrayidx].lineno = lineno;
|
||||||
|
if (*p == '/')
|
||||||
|
{
|
||||||
|
int rerr;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
array[arrayidx].type = PAT_REGEX;
|
||||||
|
if (*p && p[strlen(p)-1] == '/')
|
||||||
|
p[strlen(p)-1] = 0; /* Remove optional delimiter. */
|
||||||
|
array[arrayidx].u.r.regex = xcalloc (1, sizeof (regex_t));
|
||||||
|
rerr = regcomp (array[arrayidx].u.r.regex, p,
|
||||||
|
REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
||||||
|
if (rerr)
|
||||||
|
{
|
||||||
|
char *rerrbuf = get_regerror (rerr, array[arrayidx].u.r.regex);
|
||||||
|
log_error ("invalid r.e. at line %u: %s\n", lineno, rerrbuf);
|
||||||
|
xfree (rerrbuf);
|
||||||
|
if (!opt.checkonly)
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
array[arrayidx].type = PAT_STRING;
|
||||||
|
array[arrayidx].u.s.string = p;
|
||||||
|
array[arrayidx].u.s.length = strlen (p);
|
||||||
|
}
|
||||||
|
arrayidx++;
|
||||||
|
}
|
||||||
|
assert (arrayidx < arraysize);
|
||||||
|
array[arrayidx].type = PAT_NULL;
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check whether string macthes any of the pattern in PATARRAY and
|
||||||
|
returns the matching pattern item or NULL. */
|
||||||
|
static pattern_t *
|
||||||
|
match_p (const char *string, pattern_t *patarray)
|
||||||
|
{
|
||||||
|
pattern_t *pat;
|
||||||
|
|
||||||
|
if (!*string)
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("zero length input line - ignored\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pat = patarray; pat->type != PAT_NULL; pat++)
|
||||||
|
{
|
||||||
|
if (pat->type == PAT_STRING)
|
||||||
|
{
|
||||||
|
if (!strcasecmp (pat->u.s.string, string))
|
||||||
|
return pat;
|
||||||
|
}
|
||||||
|
else if (pat->type == PAT_REGEX)
|
||||||
|
{
|
||||||
|
int rerr;
|
||||||
|
|
||||||
|
rerr = regexec (pat->u.r.regex, string, 0, NULL, 0);
|
||||||
|
if (!rerr)
|
||||||
|
return pat;
|
||||||
|
else if (rerr != REG_NOMATCH)
|
||||||
|
{
|
||||||
|
char *rerrbuf = get_regerror (rerr, pat->u.r.regex);
|
||||||
|
log_error ("matching r.e. failed: %s\n", rerrbuf);
|
||||||
|
xfree (rerrbuf);
|
||||||
|
return pat; /* Better indicate a match on error. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BUG ();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Actual processing of the input. This fucntion does not return an
|
||||||
|
error code but exits as soon as a match has been found. */
|
||||||
|
static void
|
||||||
|
process (FILE *fp, pattern_t *patarray)
|
||||||
|
{
|
||||||
|
char buffer[2048];
|
||||||
|
size_t idx;
|
||||||
|
int c;
|
||||||
|
unsigned long lineno = 0;
|
||||||
|
pattern_t *pat;
|
||||||
|
|
||||||
|
idx = 0;
|
||||||
|
c = 0;
|
||||||
|
while (idx < sizeof buffer -1 && c != EOF )
|
||||||
|
{
|
||||||
|
if ((c = getc (fp)) != EOF)
|
||||||
|
buffer[idx] = c;
|
||||||
|
if ((c == '\n' && !opt.null) || (!c && opt.null) || c == EOF)
|
||||||
|
{
|
||||||
|
lineno++;
|
||||||
|
if (!opt.null)
|
||||||
|
{
|
||||||
|
while (idx && isascii (buffer[idx-1]) && isspace (buffer[idx-1]))
|
||||||
|
idx--;
|
||||||
|
}
|
||||||
|
buffer[idx] = 0;
|
||||||
|
pat = match_p (buffer, patarray);
|
||||||
|
if (pat)
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_error ("input line %lu matches pattern at line %u"
|
||||||
|
" - rejected\n",
|
||||||
|
lineno, pat->lineno);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
if (c != EOF)
|
||||||
|
{
|
||||||
|
log_error ("input line %lu too long - rejected\n", lineno+1);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
if (ferror (fp))
|
||||||
|
{
|
||||||
|
log_error ("input read error at line %lu: %s - rejected\n",
|
||||||
|
lineno+1, strerror (errno));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("no input line matches the pattern - accepted\n");
|
||||||
|
}
|
||||||
|
|
@ -505,14 +505,30 @@ static gc_option_t gc_options_gpg_agent[] =
|
|||||||
{ "allow-mark-trusted", GC_OPT_FLAG_RUNTIME,
|
{ "allow-mark-trusted", GC_OPT_FLAG_RUNTIME,
|
||||||
GC_LEVEL_ADVANCED, "gnupg", "allow clients to mark keys as \"trusted\"",
|
GC_LEVEL_ADVANCED, "gnupg", "allow clients to mark keys as \"trusted\"",
|
||||||
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
||||||
{ "min-passphrase-len", GC_OPT_FLAG_RUNTIME,
|
|
||||||
GC_LEVEL_EXPERT, "gnupg",
|
|
||||||
N_("|N|set minimal required length for new passphrases to N"),
|
|
||||||
GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
|
|
||||||
{ "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
|
{ "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
|
||||||
"gnupg", "do not grab keyboard and mouse",
|
"gnupg", "do not grab keyboard and mouse",
|
||||||
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
||||||
|
|
||||||
|
{ "Passphrase policy",
|
||||||
|
GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
|
||||||
|
"gnupg", N_("Options enforcing a passphrase policy") },
|
||||||
|
{ "enforce-passphrases-constraints", GC_OPT_FLAG_RUNTIME,
|
||||||
|
GC_LEVEL_EXPERT, "gnupg",
|
||||||
|
N_("do not allow to bypass the passphrase policy"),
|
||||||
|
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
||||||
|
{ "min-passphrase-len", GC_OPT_FLAG_RUNTIME,
|
||||||
|
GC_LEVEL_ADVANCED, "gnupg",
|
||||||
|
N_("|N|set minimal required length for new passphrases to N"),
|
||||||
|
GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
|
||||||
|
{ "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME,
|
||||||
|
GC_LEVEL_EXPERT, "gnupg",
|
||||||
|
N_("|N|require at least N non-alpha characters for a new passphrase"),
|
||||||
|
GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
|
||||||
|
{ "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME,
|
||||||
|
GC_LEVEL_EXPERT,
|
||||||
|
"gnupg", N_("|FILE|check new passphrases against pattern in FILE"),
|
||||||
|
GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON },
|
||||||
|
|
||||||
GC_OPTION_NULL
|
GC_OPTION_NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -915,9 +931,7 @@ static struct
|
|||||||
{ "gpg-agent", NULL, "GPG Agent", gc_options_gpg_agent },
|
{ "gpg-agent", NULL, "GPG Agent", gc_options_gpg_agent },
|
||||||
{ "scdaemon", NULL, "Smartcard Daemon", gc_options_scdaemon },
|
{ "scdaemon", NULL, "Smartcard Daemon", gc_options_scdaemon },
|
||||||
{ "gpgsm", NULL, "GPG for S/MIME", gc_options_gpgsm },
|
{ "gpgsm", NULL, "GPG for S/MIME", gc_options_gpgsm },
|
||||||
#ifndef HAVE_W32_SYSTEM
|
|
||||||
{ "dirmngr", NULL, "Directory Manager", gc_options_dirmngr }
|
{ "dirmngr", NULL, "Directory Manager", gc_options_dirmngr }
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user