Add more passphrase policy rules.

(--max-passphrase-days).
This commit is contained in:
Werner Koch 2007-08-28 17:48:13 +00:00
parent 15d0cb42a1
commit f268889b8f
45 changed files with 8136 additions and 5736 deletions

5
NEWS
View File

@ -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)

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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",

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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.

View File

@ -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;
}

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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

488
po/be.po

File diff suppressed because it is too large Load Diff

493
po/ca.po

File diff suppressed because it is too large Load Diff

490
po/cs.po

File diff suppressed because it is too large Load Diff

493
po/da.po

File diff suppressed because it is too large Load Diff

487
po/de.po

File diff suppressed because it is too large Load Diff

493
po/el.po

File diff suppressed because it is too large Load Diff

493
po/eo.po

File diff suppressed because it is too large Load Diff

493
po/es.po

File diff suppressed because it is too large Load Diff

493
po/et.po

File diff suppressed because it is too large Load Diff

493
po/fi.po

File diff suppressed because it is too large Load Diff

494
po/fr.po

File diff suppressed because it is too large Load Diff

493
po/gl.po

File diff suppressed because it is too large Load Diff

493
po/hu.po

File diff suppressed because it is too large Load Diff

493
po/id.po

File diff suppressed because it is too large Load Diff

493
po/it.po

File diff suppressed because it is too large Load Diff

490
po/ja.po

File diff suppressed because it is too large Load Diff

489
po/nb.po

File diff suppressed because it is too large Load Diff

493
po/pl.po

File diff suppressed because it is too large Load Diff

493
po/pt.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

493
po/ro.po

File diff suppressed because it is too large Load Diff

490
po/ru.po

File diff suppressed because it is too large Load Diff

493
po/sk.po

File diff suppressed because it is too large Load Diff

492
po/sv.po

File diff suppressed because it is too large Load Diff

490
po/tr.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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
};