mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Add more passphrase policy rules.
(--max-passphrase-days).
This commit is contained in:
parent
15d0cb42a1
commit
f268889b8f
5
NEWS
5
NEWS
@ -7,8 +7,9 @@ Noteworthy changes in version 2.0.7
|
||||
* 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.
|
||||
* Add new options min-passphrase-nonalpha, check-passphrase-pattern,
|
||||
enforce-passphrase-constraints and max-passphrase-days to
|
||||
gpg-agent.
|
||||
|
||||
|
||||
Noteworthy changes in version 2.0.6 (2007-08-16)
|
||||
|
@ -1,3 +1,21 @@
|
||||
2007-08-28 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c (main): Add option --faked-system-time.
|
||||
|
||||
* protect-tool.c (read_and_unprotect): Print the protected-at date.
|
||||
|
||||
* agent.h (struct server_control_s): Add member IN_PASSWD.
|
||||
* command.c (cmd_passwd): Set it.
|
||||
* findkey.c (try_unprotect_cb): Use it.
|
||||
|
||||
* protect.c (do_encryption): Replace asprintf by xtryasprint.
|
||||
(agent_protect): Create the protected-at item.
|
||||
(agent_unprotect): Add optional arg PROTECTED_AT.
|
||||
(merge_lists): Add args CUTOFF and CUTLEN.
|
||||
(agent_unprotect): Use them.
|
||||
* findkey.c (try_unprotect_cb): Add code to test for expired keys.
|
||||
(unprotect): Allow changing the passphrase.
|
||||
|
||||
2007-08-27 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c: Add options --min-passphrase-nonalpha,
|
||||
|
@ -88,7 +88,12 @@ struct
|
||||
unsigned int min_passphrase_nonalpha;
|
||||
/* File name with a patternfile or NULL if not enabled. */
|
||||
const char *check_passphrase_pattern;
|
||||
|
||||
/* If not 0 the user is asked to change his passphrase after these
|
||||
number of days. */
|
||||
unsigned int max_passphrase_days;
|
||||
/* If set, a passphrase history will be written and checked at each
|
||||
passphrase change. */
|
||||
int enable_passhrase_history;
|
||||
|
||||
int running_detached; /* We are running detached from the tty. */
|
||||
|
||||
@ -153,6 +158,8 @@ struct server_control_s
|
||||
|
||||
int use_auth_call; /* Hack to send the PKAUTH command instead of the
|
||||
PKSIGN command to the scdaemon. */
|
||||
int in_passwd; /* Hack to inhibit enforced passphrase change
|
||||
during an explicit passwd command. */
|
||||
};
|
||||
|
||||
|
||||
@ -182,7 +189,7 @@ enum
|
||||
/* Values for the cache_mode arguments. */
|
||||
typedef enum
|
||||
{
|
||||
CACHE_MODE_IGNORE = 0, /* Special mode to by pass the cache. */
|
||||
CACHE_MODE_IGNORE = 0, /* Special mode to bypass the cache. */
|
||||
CACHE_MODE_ANY, /* Any mode except ignore matches. */
|
||||
CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */
|
||||
CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */
|
||||
@ -271,6 +278,7 @@ int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
|
||||
int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
gnupg_isotime_t protected_at,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_private_key_type (const unsigned char *privatekey);
|
||||
unsigned char *make_shadow_info (const char *serialno, const char *idstring);
|
||||
|
@ -1039,6 +1039,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
ctrl->in_passwd++;
|
||||
rc = agent_key_from_file (ctrl, ctrl->server_local->keydesc,
|
||||
grip, &shadow_info, CACHE_MODE_IGNORE, &s_skey);
|
||||
if (rc)
|
||||
@ -1050,6 +1051,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
|
||||
}
|
||||
else
|
||||
rc = agent_protect_and_store (ctrl, s_skey);
|
||||
ctrl->in_passwd--;
|
||||
|
||||
xfree (ctrl->server_local->keydesc);
|
||||
ctrl->server_local->keydesc = NULL;
|
||||
|
115
agent/findkey.c
115
agent/findkey.c
@ -1,5 +1,6 @@
|
||||
/* findkey.c - locate the secret key
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
/* findkey.c - Locate the secret key
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
|
||||
* 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -31,15 +32,20 @@
|
||||
#include <pth.h> /* (we use pth_sleep) */
|
||||
|
||||
#include "agent.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
/* Helper to pass data to the check callback of the unprotect function. */
|
||||
struct try_unprotect_arg_s {
|
||||
struct try_unprotect_arg_s
|
||||
{
|
||||
ctrl_t ctrl;
|
||||
const unsigned char *protected_key;
|
||||
unsigned char *unprotected_key;
|
||||
int change_required; /* Set by the callback to indicate that the
|
||||
user should chnage the passphrase. */
|
||||
};
|
||||
|
||||
|
||||
@ -132,10 +138,71 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
|
||||
{
|
||||
struct try_unprotect_arg_s *arg = pi->check_cb_arg;
|
||||
size_t dummy;
|
||||
gpg_error_t err;
|
||||
gnupg_isotime_t now, protected_at, tmptime;
|
||||
char *desc = NULL;
|
||||
|
||||
assert (!arg->unprotected_key);
|
||||
return agent_unprotect (arg->protected_key, pi->pin,
|
||||
&arg->unprotected_key, &dummy);
|
||||
|
||||
arg->change_required = 0;
|
||||
err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
|
||||
&arg->unprotected_key, &dummy);
|
||||
if (err)
|
||||
return err;
|
||||
if (!opt.max_passphrase_days || arg->ctrl->in_passwd)
|
||||
return 0; /* No regular passphrase change required. */
|
||||
|
||||
if (!*protected_at)
|
||||
{
|
||||
/* No protection date known - must force passphrase change. */
|
||||
desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A"
|
||||
"Please change it now."));
|
||||
if (!desc)
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
else
|
||||
{
|
||||
gnupg_get_isotime (now);
|
||||
gnupg_copy_time (tmptime, protected_at);
|
||||
err = add_days_to_isotime (tmptime, opt.max_passphrase_days);
|
||||
if (err)
|
||||
return err;
|
||||
if (strcmp (now, tmptime) > 0 )
|
||||
{
|
||||
/* Passphrase "expired". */
|
||||
desc = xtryasprintf
|
||||
(_("This passphrase has not been changed%%0A"
|
||||
"since %.4s-%.2s-%.2s. Please change it now."),
|
||||
protected_at, protected_at+4, protected_at+6);
|
||||
if (!desc)
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
}
|
||||
|
||||
if (desc)
|
||||
{
|
||||
/* Change required. */
|
||||
if (opt.enforce_passphrase_constraints)
|
||||
{
|
||||
err = agent_get_confirmation (arg->ctrl, desc,
|
||||
_("Change passphrase"), NULL);
|
||||
if (!err)
|
||||
arg->change_required = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = agent_get_confirmation (arg->ctrl, desc,
|
||||
_("Change passphrase"),
|
||||
_("I'll change it later"));
|
||||
if (!err)
|
||||
arg->change_required = 1;
|
||||
else if (gpg_err_code (err) == GPG_ERR_CANCELED)
|
||||
err = 0;
|
||||
}
|
||||
xfree (desc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -260,7 +327,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
||||
pw = agent_get_cache (hexgrip, cache_mode, &cache_marker);
|
||||
if (pw)
|
||||
{
|
||||
rc = agent_unprotect (*keybuf, pw, &result, &resultlen);
|
||||
rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
|
||||
agent_unlock_cache_entry (&cache_marker);
|
||||
if (!rc)
|
||||
{
|
||||
@ -272,7 +339,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
||||
}
|
||||
|
||||
/* If the pinentry is currently in use, we wait up to 60 seconds
|
||||
for it close and check the cache again. This solves a common
|
||||
for it to close and check the cache again. This solves a common
|
||||
situation where several requests for unprotecting a key have
|
||||
been made but the user is still entering the passphrase for
|
||||
the first request. Because all requests to agent_askpin are
|
||||
@ -294,7 +361,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
||||
/* Timeout - better call pinentry now the plain way. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
|
||||
if (!pi)
|
||||
return gpg_error_from_syserror ();
|
||||
@ -303,14 +370,46 @@ unprotect (ctrl_t ctrl, const char *desc_text,
|
||||
pi->max_digits = 8;
|
||||
pi->max_tries = 3;
|
||||
pi->check_cb = try_unprotect_cb;
|
||||
arg.ctrl = ctrl;
|
||||
arg.protected_key = *keybuf;
|
||||
arg.unprotected_key = NULL;
|
||||
arg.change_required = 0;
|
||||
pi->check_cb_arg = &arg;
|
||||
|
||||
rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
|
||||
if (!rc)
|
||||
{
|
||||
assert (arg.unprotected_key);
|
||||
if (arg.change_required)
|
||||
{
|
||||
size_t canlen, erroff;
|
||||
gcry_sexp_t s_skey;
|
||||
|
||||
assert (arg.unprotected_key);
|
||||
canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
|
||||
rc = gcry_sexp_sscan (&s_skey, &erroff,
|
||||
(char*)arg.unprotected_key, canlen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to build S-Exp (off=%u): %s\n",
|
||||
(unsigned int)erroff, gpg_strerror (rc));
|
||||
wipememory (arg.unprotected_key, canlen);
|
||||
xfree (arg.unprotected_key);
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
rc = agent_protect_and_store (ctrl, s_skey);
|
||||
gcry_sexp_release (s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("changing the passphrase failed: %s\n",
|
||||
gpg_strerror (rc));
|
||||
wipememory (arg.unprotected_key, canlen);
|
||||
xfree (arg.unprotected_key);
|
||||
xfree (pi);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
agent_put_cache (hexgrip, cache_mode, pi->pin, 0);
|
||||
xfree (*keybuf);
|
||||
*keybuf = arg.unprotected_key;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* pksign.c - Generate a keypair
|
||||
/* genkey.c - Generate a keypair
|
||||
* Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
|
@ -92,8 +92,11 @@ enum cmd_and_opt_values
|
||||
oMinPassphraseLen,
|
||||
oMinPassphraseNonalpha,
|
||||
oCheckPassphrasePattern,
|
||||
oMaxPassphraseDays,
|
||||
oEnablePassphraseHistory,
|
||||
oUseStandardSocket,
|
||||
oNoUseStandardSocket,
|
||||
oFakedSystemTime,
|
||||
|
||||
oIgnoreCacheForSigning,
|
||||
oAllowMarkTrusted,
|
||||
@ -137,6 +140,7 @@ static ARGPARSE_OPTS opts[] = {
|
||||
{ oScdaemonProgram, "scdaemon-program", 2 ,
|
||||
N_("|PGM|use PGM as the SCdaemon program") },
|
||||
{ oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
|
||||
{ oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
|
||||
|
||||
{ oDisplay, "display", 2, "@" },
|
||||
{ oTTYname, "ttyname", 2, "@" },
|
||||
@ -157,6 +161,8 @@ static ARGPARSE_OPTS opts[] = {
|
||||
{ oMinPassphraseLen, "min-passphrase-len", 4, "@" },
|
||||
{ oMinPassphraseNonalpha, "min-passphrase-nonalpha", 4, "@" },
|
||||
{ oCheckPassphrasePattern, "check-passphrase-pattern", 2, "@" },
|
||||
{ oMaxPassphraseDays, "max-passphrase-days", 4, "@" },
|
||||
{ oEnablePassphraseHistory, "enable-passphrase-history", 0, "@" },
|
||||
|
||||
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
|
||||
N_("do not use the PIN cache when signing")},
|
||||
@ -177,6 +183,7 @@ static ARGPARSE_OPTS opts[] = {
|
||||
#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
|
||||
#define MIN_PASSPHRASE_LEN (8)
|
||||
#define MIN_PASSPHRASE_NONALPHA (1)
|
||||
#define MAX_PASSPHRASE_DAYS (0)
|
||||
|
||||
/* The timer tick used for housekeeping stuff. For Windows we use a
|
||||
longer period as the SetWaitableTimer seems to signal earlier than
|
||||
@ -375,6 +382,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
|
||||
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
|
||||
opt.min_passphrase_nonalpha = MIN_PASSPHRASE_NONALPHA;
|
||||
opt.check_passphrase_pattern = NULL;
|
||||
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
|
||||
opt.enable_passhrase_history = 0;
|
||||
opt.ignore_cache_for_signing = 0;
|
||||
opt.allow_mark_trusted = 0;
|
||||
opt.disable_scdaemon = 0;
|
||||
@ -424,6 +433,12 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
|
||||
case oCheckPassphrasePattern:
|
||||
opt.check_passphrase_pattern = pargs->r.ret_str;
|
||||
break;
|
||||
case oMaxPassphraseDays:
|
||||
opt.max_passphrase_days = pargs->r.ret_ulong;
|
||||
break;
|
||||
case oEnablePassphraseHistory:
|
||||
opt.enable_passhrase_history = 1;
|
||||
break;
|
||||
|
||||
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
|
||||
|
||||
@ -646,6 +661,15 @@ main (int argc, char **argv )
|
||||
case oUseStandardSocket: standard_socket = 1; break;
|
||||
case oNoUseStandardSocket: standard_socket = 0; break;
|
||||
|
||||
case oFakedSystemTime:
|
||||
{
|
||||
time_t faked_time = isotime2epoch (pargs.r.ret_str);
|
||||
if (faked_time == (time_t)(-1))
|
||||
faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10);
|
||||
gnupg_set_time (faked_time, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case oKeepTTY: opt.keep_tty = 1; break;
|
||||
case oKeepDISPLAY: opt.keep_display = 1; break;
|
||||
|
||||
@ -753,6 +777,11 @@ main (int argc, char **argv )
|
||||
MIN_PASSPHRASE_NONALPHA);
|
||||
printf ("check-passphrase-pattern:%lu:\n",
|
||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("max-passphrase-days:%lu:%d:\n",
|
||||
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME,
|
||||
MAX_PASSPHRASE_DAYS);
|
||||
printf ("enable-passphrase-history:%lu:\n",
|
||||
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("no-grab:%lu:\n",
|
||||
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
|
||||
printf ("ignore-cache-for-signing:%lu:\n",
|
||||
|
@ -69,6 +69,7 @@ A protected key is like this:
|
||||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
(e #010001#)
|
||||
(protected mode (parms) encrypted_octet_string)
|
||||
(protected-at <isotimestamp>)
|
||||
)
|
||||
(uri http://foo.bar x-foo:whatever_you_want)
|
||||
(comment whatever)
|
||||
@ -79,7 +80,8 @@ In this scheme the encrypted_octet_string is encrypted according to
|
||||
the algorithm described after the keyword protected; most protection
|
||||
algorithms need some parameters, which are given in a list before the
|
||||
encrypted_octet_string. The result of the decryption process is a
|
||||
list of the secret key parameters.
|
||||
list of the secret key parameters. The protected-at expression is
|
||||
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
|
||||
|
||||
The only available protection mode for now is
|
||||
|
||||
@ -110,12 +112,13 @@ representation) after decryption:
|
||||
)
|
||||
|
||||
For padding reasons, random bytes are appended to this list - they can
|
||||
easily be stripped by looking for the end of the list.
|
||||
easily be stripped by looking for the end of the list.
|
||||
|
||||
The hash is calculated on the concatenation of the public key and
|
||||
secret key parameter lists: i.e it is required to hash the
|
||||
concatenation of these 6 canonical encoded lists for RSA, including
|
||||
the parenthesis and the algorithm keyword.
|
||||
the parenthesis, the algorithm keyword and (if used) the protected-at
|
||||
list.
|
||||
|
||||
(rsa
|
||||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
@ -124,6 +127,7 @@ the parenthesis and the algorithm keyword.
|
||||
(p #00e861b..[some bytes not shown]..f1#)
|
||||
(q #00f7a7c..[some bytes not shown]..61#)
|
||||
(u #304559a..[some bytes not shown]..9b#)
|
||||
(protected-at "18950523T000000")
|
||||
)
|
||||
|
||||
After decryption the hash must be recalculated and compared against
|
||||
|
@ -366,12 +366,14 @@ read_and_unprotect (const char *fname)
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
char *pw;
|
||||
|
||||
gnupg_isotime_t protected_at;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)), &result, &resultlen);
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
|
||||
protected_at, &result, &resultlen);
|
||||
release_passphrase (pw);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
@ -381,7 +383,12 @@ read_and_unprotect (const char *fname)
|
||||
log_error ("unprotecting the key failed: %s\n", gpg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("key protection done at %.4s-%.2s-%.2s %.2s:%.2s:%s\n",
|
||||
protected_at, protected_at+4, protected_at+6,
|
||||
protected_at+9, protected_at+11, protected_at+13);
|
||||
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
@ -883,7 +890,8 @@ export_p12_file (const char *fname)
|
||||
unsigned char *tmpkey;
|
||||
size_t tmplen;
|
||||
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)), &tmpkey, &tmplen);
|
||||
rc = agent_unprotect (key, (pw=get_passphrase (1, 0)),
|
||||
NULL, &tmpkey, &tmplen);
|
||||
release_passphrase (pw);
|
||||
if (rc)
|
||||
{
|
||||
|
141
agent/protect.c
141
agent/protect.c
@ -163,7 +163,7 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
||||
((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
|
||||
|
||||
We always append a full block of random bytes as padding but
|
||||
encrypt only what is needed for a full blocksize */
|
||||
encrypt only what is needed for a full blocksize. */
|
||||
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
||||
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
|
||||
enclen = outlen/blklen * blklen;
|
||||
@ -229,21 +229,13 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
||||
encrypted_octet_string)
|
||||
|
||||
in canoncical format of course. We use asprintf and %n modifier
|
||||
and spaces as palceholders. */
|
||||
asprintf (&p,
|
||||
"(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
|
||||
(int)strlen (modestr), modestr,
|
||||
&saltpos,
|
||||
blklen, &ivpos, blklen, "",
|
||||
enclen, &encpos, enclen, "");
|
||||
if (p)
|
||||
{ /* asprintf does not use our malloc system */
|
||||
char *psave = p;
|
||||
p = xtrymalloc (strlen (psave)+1);
|
||||
if (p)
|
||||
strcpy (p, psave);
|
||||
free (psave);
|
||||
}
|
||||
and dummy values as placeholders. */
|
||||
p = xtryasprintf
|
||||
("(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
|
||||
(int)strlen (modestr), modestr,
|
||||
&saltpos,
|
||||
blklen, &ivpos, blklen, "",
|
||||
enclen, &encpos, enclen, "");
|
||||
if (!p)
|
||||
{
|
||||
gpg_error_t tmperr = out_of_core ();
|
||||
@ -276,11 +268,19 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
size_t n;
|
||||
int c, infidx, i;
|
||||
unsigned char hashvalue[20];
|
||||
char timestamp_exp[35];
|
||||
unsigned char *protected;
|
||||
size_t protectedlen;
|
||||
int depth = 0;
|
||||
unsigned char *p;
|
||||
gcry_md_hd_t md;
|
||||
|
||||
/* Create an S-expression with the procted-at timestamp. */
|
||||
memcpy (timestamp_exp, "(12:protected-at15:", 19);
|
||||
gnupg_get_isotime (timestamp_exp+19);
|
||||
timestamp_exp[19+15] = ')';
|
||||
|
||||
/* Parse original key. */
|
||||
s = plainkey;
|
||||
if (*s != '(')
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
@ -345,8 +345,18 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
assert (!depth);
|
||||
real_end = s-1;
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
|
||||
hash_begin, hash_end - hash_begin + 1);
|
||||
|
||||
/* Hash the stuff. Because the timestamp_exp won't get protected,
|
||||
we can't simply hash a continuous buffer but need to use several
|
||||
md_writes. */
|
||||
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 );
|
||||
if (rc)
|
||||
return rc;
|
||||
gcry_md_write (md, hash_begin, hash_end - hash_begin);
|
||||
gcry_md_write (md, timestamp_exp, 35);
|
||||
gcry_md_write (md, ")", 1);
|
||||
memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20);
|
||||
gcry_md_close (md);
|
||||
|
||||
rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
|
||||
passphrase, hashvalue,
|
||||
@ -356,10 +366,12 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
|
||||
/* Now create the protected version of the key. Note that the 10
|
||||
extra bytes are for for the inserted "protected-" string (the
|
||||
beginning of the plaintext reads: "((11:private-key(" ). */
|
||||
beginning of the plaintext reads: "((11:private-key(" ). The 35
|
||||
term is the space for (12:protected-at15:<timestamp>). */
|
||||
*resultlen = (10
|
||||
+ (prot_begin-plainkey)
|
||||
+ protectedlen
|
||||
+ 35
|
||||
+ (real_end-prot_end));
|
||||
*result = p = xtrymalloc (*resultlen);
|
||||
if (!p)
|
||||
@ -374,10 +386,15 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
p += prot_begin - plainkey - 4;
|
||||
memcpy (p, protected, protectedlen);
|
||||
p += protectedlen;
|
||||
|
||||
memcpy (p, timestamp_exp, 35);
|
||||
p += 35;
|
||||
|
||||
memcpy (p, prot_end+1, real_end - prot_end);
|
||||
p += real_end - prot_end;
|
||||
assert ( p - *result == *resultlen);
|
||||
xfree (protected);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -457,13 +474,16 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
|
||||
/* Merge the parameter list contained in CLEARTEXT with the original
|
||||
protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
|
||||
Return the new list in RESULT and the MIC value in the 20 byte
|
||||
buffer SHA1HASH. */
|
||||
buffer SHA1HASH. CUTOFF and CUTLEN will receive the offset and the
|
||||
length of the resulting list which should go into the MIC
|
||||
calculation but then be removed. */
|
||||
static int
|
||||
merge_lists (const unsigned char *protectedkey,
|
||||
size_t replacepos,
|
||||
const unsigned char *cleartext,
|
||||
unsigned char *sha1hash,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
unsigned char **result, size_t *resultlen,
|
||||
size_t *cutoff, size_t *cutlen)
|
||||
{
|
||||
size_t n, newlistlen;
|
||||
unsigned char *newlist, *p;
|
||||
@ -473,6 +493,8 @@ merge_lists (const unsigned char *protectedkey,
|
||||
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
*cutoff = 0;
|
||||
*cutlen = 0;
|
||||
|
||||
if (replacepos < 26)
|
||||
return gpg_error (GPG_ERR_BUG);
|
||||
@ -522,7 +544,7 @@ merge_lists (const unsigned char *protectedkey,
|
||||
goto invalid_sexp;
|
||||
endpos = s;
|
||||
s++;
|
||||
/* short intermezzo: Get the MIC */
|
||||
/* Intermezzo: Get the MIC */
|
||||
if (*s != '(')
|
||||
goto invalid_sexp;
|
||||
s++;
|
||||
@ -539,13 +561,13 @@ merge_lists (const unsigned char *protectedkey,
|
||||
s += n;
|
||||
if (*s != ')')
|
||||
goto invalid_sexp;
|
||||
/* end intermezzo */
|
||||
/* End intermezzo */
|
||||
|
||||
/* append the parameter list */
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
/* skip overt the protected list element in the original list */
|
||||
/* Skip over the protected list element in the original list. */
|
||||
s = protectedkey + replacepos;
|
||||
assert (*s == '(');
|
||||
s++;
|
||||
@ -553,6 +575,22 @@ merge_lists (const unsigned char *protectedkey,
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
/* Record the position of the optional protected-at expression. */
|
||||
if (*s == '(')
|
||||
{
|
||||
const unsigned char *save_s = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (smatch (&s, n, "protected-at"))
|
||||
{
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
goto failure;
|
||||
*cutlen = s - save_s;
|
||||
}
|
||||
s = save_s;
|
||||
}
|
||||
startpos = s;
|
||||
i = 2; /* we are inside this level */
|
||||
rc = sskip (&s, &i);
|
||||
@ -561,9 +599,12 @@ merge_lists (const unsigned char *protectedkey,
|
||||
assert (s[-1] == ')');
|
||||
endpos = s; /* one behind the end of the list */
|
||||
|
||||
/* append the rest */
|
||||
/* Append the rest. */
|
||||
if (*cutlen)
|
||||
*cutoff = p - newlist;
|
||||
memcpy (p, startpos, endpos - startpos);
|
||||
p += endpos - startpos;
|
||||
|
||||
|
||||
/* ready */
|
||||
*result = newlist;
|
||||
@ -584,13 +625,16 @@ merge_lists (const unsigned char *protectedkey,
|
||||
|
||||
|
||||
/* Unprotect the key encoded in canonical format. We assume a valid
|
||||
S-Exp here. */
|
||||
S-Exp here. If a protected-at item is available, its value will
|
||||
be stored at protocted_at unless this is NULL. */
|
||||
int
|
||||
agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
gnupg_isotime_t protected_at,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int rc;
|
||||
const unsigned char *s;
|
||||
const unsigned char *protect_list;
|
||||
size_t n;
|
||||
int infidx, i;
|
||||
unsigned char sha1hash[20], sha1hash2[20];
|
||||
@ -601,6 +645,10 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
unsigned char *cleartext;
|
||||
unsigned char *final;
|
||||
size_t finallen;
|
||||
size_t cutoff, cutlen;
|
||||
|
||||
if (protected_at)
|
||||
*protected_at = 0;
|
||||
|
||||
s = protectedkey;
|
||||
if (*s != '(')
|
||||
@ -624,12 +672,44 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
if (!protect_info[infidx].algo)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
|
||||
|
||||
/* See wether we have a protected-at timestamp. */
|
||||
protect_list = s; /* Save for later. */
|
||||
if (protected_at)
|
||||
{
|
||||
while (*s == '(')
|
||||
{
|
||||
prot_begin = s;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
if (smatch (&s, n, "protected-at"))
|
||||
{
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
if (n != 15)
|
||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||
memcpy (protected_at, s, 15);
|
||||
protected_at[15] = 0;
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
i = 1;
|
||||
rc = sskip (&s, &i);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now find the list with the protected information. Here is an
|
||||
example for such a list:
|
||||
(protected openpgp-s2k3-sha1-aes-cbc
|
||||
((sha1 <salt> <count>) <Initialization_Vector>)
|
||||
<encrypted_data>)
|
||||
*/
|
||||
s = protect_list;
|
||||
for (;;)
|
||||
{
|
||||
if (*s != '(')
|
||||
@ -700,7 +780,7 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
return rc;
|
||||
|
||||
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
|
||||
sha1hash, &final, &finallen);
|
||||
sha1hash, &final, &finallen, &cutoff, &cutlen);
|
||||
/* Albeit cleartext has been allocated in secure memory and thus
|
||||
xfree will wipe it out, we do an extra wipe just in case
|
||||
somethings goes badly wrong. */
|
||||
@ -718,6 +798,13 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
xfree (final);
|
||||
return rc;
|
||||
}
|
||||
/* Now remove tha part which is included in the MIC but should not
|
||||
go into the final thing. */
|
||||
if (cutlen)
|
||||
{
|
||||
memmove (final+cutoff, final+cutoff+cutlen, finallen-cutoff-cutlen);
|
||||
finallen -= cutlen;
|
||||
}
|
||||
|
||||
*result = final;
|
||||
*resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
|
||||
|
@ -1,3 +1,11 @@
|
||||
2007-08-28 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gettime.c (check_isotime, add_isotime): New. Orginally written
|
||||
for DirMngr by me.
|
||||
(add_days_to_isotime): New.
|
||||
(date2jd, jd2date, days_per_month, days_per_year): New. Taken from
|
||||
my ancient (1988) code used in Wedit (time2.c).
|
||||
|
||||
2007-08-27 Werner Koch <wk@g10code.com>
|
||||
|
||||
* util.h (GNUPG_MODULE_NAME_CHECK_PATTERN): New.
|
||||
|
205
common/gettime.c
205
common/gettime.c
@ -30,6 +30,10 @@
|
||||
static unsigned long timewarp;
|
||||
static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
|
||||
|
||||
/* Correction used to map to real Julian days. */
|
||||
#define JD_DIFF 1721060L
|
||||
|
||||
|
||||
/* Wrapper for the time(3). We use this here so we can fake the time
|
||||
for tests */
|
||||
time_t
|
||||
@ -363,14 +367,215 @@ asctimestamp( u32 stamp )
|
||||
|
||||
|
||||
|
||||
static int
|
||||
days_per_year (int y)
|
||||
{
|
||||
int s ;
|
||||
|
||||
s = !(y % 4);
|
||||
if ( !(y % 100))
|
||||
if ((y%400))
|
||||
s = 0;
|
||||
return s ? 366 : 365;
|
||||
}
|
||||
|
||||
static int
|
||||
days_per_month (int y, int m)
|
||||
{
|
||||
int s;
|
||||
|
||||
switch(m)
|
||||
{
|
||||
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
|
||||
return 31 ;
|
||||
case 2:
|
||||
s = !(y % 4);
|
||||
if (!(y % 100))
|
||||
if ((y % 400))
|
||||
s = 0;
|
||||
return s? 29 : 28 ;
|
||||
case 4: case 6: case 9: case 11:
|
||||
return 30;
|
||||
}
|
||||
BUG();
|
||||
}
|
||||
|
||||
|
||||
/* Convert YEAR, MONTH and DAY into the Julian date. We assume that
|
||||
it is already noon; we dont; support dates before 1582-10-15. */
|
||||
static unsigned long
|
||||
date2jd (int year, int month, int day)
|
||||
{
|
||||
unsigned long jd;
|
||||
|
||||
jd = 365L * year + 31 * (month-1) + day + JD_DIFF;
|
||||
if (month < 3)
|
||||
year-- ;
|
||||
else
|
||||
jd -= (4 * month + 23) / 10;
|
||||
|
||||
jd += year / 4 - ((year / 100 + 1) *3) / 4;
|
||||
|
||||
return jd ;
|
||||
}
|
||||
|
||||
/* Convert a Julian date back to YEAR, MONTH and DAY. Return day of
|
||||
the year or 0 on error. This function uses some more or less
|
||||
arbitrary limits, most important is that days before 1582 are not
|
||||
supported. */
|
||||
static int
|
||||
jd2date (unsigned long jd, int *year, int *month, int *day)
|
||||
{
|
||||
int y, m, d;
|
||||
long delta;
|
||||
|
||||
if (!jd)
|
||||
return 0 ;
|
||||
if (jd < 1721425 || jd > 2843085)
|
||||
return 0;
|
||||
|
||||
y = (jd - JD_DIFF) / 366;
|
||||
d = m = 1;
|
||||
|
||||
while ((delta = jd - date2jd (y, m, d)) > days_per_year (y))
|
||||
y++;
|
||||
|
||||
m = (delta / 31) + 1;
|
||||
while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m))
|
||||
if (++m > 12)
|
||||
{
|
||||
m = 1;
|
||||
y++;
|
||||
}
|
||||
|
||||
d = delta + 1 ;
|
||||
if (d > days_per_month (y, m))
|
||||
{
|
||||
d = 1;
|
||||
m++;
|
||||
}
|
||||
if (m > 12)
|
||||
{
|
||||
m = 1;
|
||||
y++;
|
||||
}
|
||||
|
||||
if (year)
|
||||
*year = y;
|
||||
if (month)
|
||||
*month = m;
|
||||
if (day)
|
||||
*day = d ;
|
||||
|
||||
return (jd - date2jd (y, 1, 1)) + 1;
|
||||
}
|
||||
|
||||
|
||||
/* Check that the 15 bytes in ATIME represent a valid ISO time. Note
|
||||
that this function does not expect a string but a plain 15 byte
|
||||
isotime buffer. */
|
||||
gpg_error_t
|
||||
check_isotime (const gnupg_isotime_t atime)
|
||||
{
|
||||
int i;
|
||||
const char *s;
|
||||
|
||||
if (!*atime)
|
||||
return gpg_error (GPG_ERR_NO_VALUE);
|
||||
|
||||
for (s=atime, i=0; i < 8; i++, s++)
|
||||
if (!digitp (s))
|
||||
return gpg_error (GPG_ERR_INV_TIME);
|
||||
if (*s != 'T')
|
||||
return gpg_error (GPG_ERR_INV_TIME);
|
||||
for (s++, i=9; i < 15; i++, s++)
|
||||
if (!digitp (s))
|
||||
return gpg_error (GPG_ERR_INV_TIME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Add SECONDS to ATIME. SECONDS may not be negative and is limited
|
||||
to about the equivalent of 62 years which should be more then
|
||||
enough for our purposes. */
|
||||
gpg_error_t
|
||||
add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int year, month, day, hour, minute, sec, ndays;
|
||||
unsigned long jd;
|
||||
|
||||
err = check_isotime (atime);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (nseconds < 0 || nseconds >= (0x7fffffff - 61) )
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
year = atoi_4 (atime+0);
|
||||
month = atoi_2 (atime+4);
|
||||
day = atoi_2 (atime+6);
|
||||
hour = atoi_2 (atime+9);
|
||||
minute= atoi_2 (atime+11);
|
||||
sec = atoi_2 (atime+13);
|
||||
|
||||
if (year <= 1582) /* The julian date functions don't support this. */
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
sec += nseconds;
|
||||
minute += sec/60;
|
||||
sec %= 60;
|
||||
hour += minute/60;
|
||||
minute %= 60;
|
||||
ndays = hour/24;
|
||||
hour %= 24;
|
||||
|
||||
jd = date2jd (year, month, day) + ndays;
|
||||
jd2date (jd, &year, &month, &day);
|
||||
|
||||
if (year > 9999 || month > 12 || day > 31
|
||||
|| year < 0 || month < 1 || day < 1)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
|
||||
year, month, day, hour, minute, sec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
add_days_to_isotime (gnupg_isotime_t atime, int ndays)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int year, month, day, hour, minute, sec;
|
||||
unsigned long jd;
|
||||
|
||||
err = check_isotime (atime);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ndays < 0 || ndays >= 9999*366 )
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
year = atoi_4 (atime+0);
|
||||
month = atoi_2 (atime+4);
|
||||
day = atoi_2 (atime+6);
|
||||
hour = atoi_2 (atime+9);
|
||||
minute= atoi_2 (atime+11);
|
||||
sec = atoi_2 (atime+13);
|
||||
|
||||
if (year <= 1582) /* The julian date functions don't support this. */
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
jd = date2jd (year, month, day) + ndays;
|
||||
jd2date (jd, &year, &month, &day);
|
||||
|
||||
if (year > 9999 || month > 12 || day > 31
|
||||
|| year < 0 || month < 1 || day < 1)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d",
|
||||
year, month, day, hour, minute, sec);
|
||||
return 0;
|
||||
}
|
||||
|
@ -110,7 +110,9 @@ const char *strtimevalue (u32 stamp);
|
||||
const char *strtimestamp (u32 stamp); /* GMT */
|
||||
const char *isotimestamp (u32 stamp); /* GMT */
|
||||
const char *asctimestamp (u32 stamp); /* localized */
|
||||
|
||||
gpg_error_t add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds);
|
||||
gpg_error_t add_days_to_isotime (gnupg_isotime_t atime, int ndays);
|
||||
gpg_error_t check_isotime (const gnupg_isotime_t atime);
|
||||
|
||||
/* Copy one ISO date to another, this is inline so that we can do a
|
||||
sanity check. */
|
||||
|
@ -365,6 +365,15 @@ 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 --max-passphrase-days @var{n}
|
||||
@opindex max-passphrase-days
|
||||
Ask the user to change the passphrase if @var{n} days have passed since
|
||||
the last change. With @option{--enforce-passphrase-constraints} set the
|
||||
user may not bypass this check.
|
||||
|
||||
@item --enable-passphrase-history
|
||||
@opindex enable-passphrase-history
|
||||
This option does nothing yet.
|
||||
|
||||
@item --pinentry-program @var{filename}
|
||||
@opindex pinentry-program
|
||||
|
@ -1,3 +1,7 @@
|
||||
2007-08-28 Werner Koch <wk@g10code.com>
|
||||
|
||||
* de.po: Updated.
|
||||
|
||||
2007-08-16 Werner Koch <wk@g10code.com>
|
||||
|
||||
* pt_BR.po, es.po: Remove the "GNU" from the project ID. That
|
||||
|
@ -8,6 +8,7 @@ agent/gpg-agent.c
|
||||
agent/preset-passphrase.c
|
||||
agent/protect-tool.c
|
||||
agent/trustlist.c
|
||||
agent/findkey.c
|
||||
|
||||
common/exechelp.c
|
||||
common/http.c
|
||||
|
493
po/pt_BR.po
493
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
490
po/zh_CN.po
490
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
490
po/zh_TW.po
490
po/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,8 @@
|
||||
2007-08-28 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpgconf-comp.c <gpg-agent>: Add options --max-passphrase-days
|
||||
and --enable-passphrase-history.
|
||||
|
||||
2007-08-27 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-check-pattern.c: New
|
||||
|
@ -528,6 +528,14 @@ static gc_option_t gc_options_gpg_agent[] =
|
||||
GC_LEVEL_EXPERT,
|
||||
"gnupg", N_("|FILE|check new passphrases against pattern in FILE"),
|
||||
GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON },
|
||||
{ "max-passphrase-days", GC_OPT_FLAG_RUNTIME,
|
||||
GC_LEVEL_EXPERT, "gnupg",
|
||||
N_("|N|expire the passphrase after N days"),
|
||||
GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
|
||||
{ "enable-passphrases-history", GC_OPT_FLAG_RUNTIME,
|
||||
GC_LEVEL_EXPERT, "gnupg",
|
||||
N_("do not allow the reuse of old passphrases"),
|
||||
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
|
||||
|
||||
GC_OPTION_NULL
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user