Implemented the chain model for X.509 validation.

This commit is contained in:
Werner Koch 2007-08-10 16:52:05 +00:00
parent ebd36b6344
commit 74d344a521
60 changed files with 16887 additions and 12516 deletions

View File

@ -1,3 +1,7 @@
2007-08-08 Werner Koch <wk@g10code.com>
* configure.ac: Use AC_CANONICAL_HOST and not AC_CANONICAL_TARGET.
2007-07-09 Werner Koch <wk@g10code.com> 2007-07-09 Werner Koch <wk@g10code.com>
* configure.ac (AM_ICONV): Check for it even when building without * configure.ac (AM_ICONV): Check for it even when building without

11
NEWS
View File

@ -1,11 +1,14 @@
Noteworthy changes in version 2.0.6 Noteworthy changes in version 2.0.6
------------------------------------------------ ------------------------------------------------
* gpgsm does now grok --default-key. * GPGSM does now grok --default-key.
* GPGCONF is now aware of --default-key and --encrypt-to.
* GPGSM does again correctly print the serial number as well the the
various keyids. This was broken since 2.0.4.
* gpgconf is now aware of --default-key and --encrypt-to.
Noteworthy changes in version 2.0.5 (2007-07-05) Noteworthy changes in version 2.0.5 (2007-07-05)
------------------------------------------------ ------------------------------------------------

View File

@ -1,3 +1,8 @@
2007-08-06 Werner Koch <wk@g10code.com>
* trustlist.c (read_one_trustfile): Add flag "cm".
(agent_istrusted): Ditto.
2007-08-02 Werner Koch <wk@g10code.com> 2007-08-02 Werner Koch <wk@g10code.com>
* gpg-agent.c: Include gc-opt-flags.h and remove their definition * gpg-agent.c: Include gc-opt-flags.h and remove their definition

View File

@ -1,5 +1,5 @@
/* trustlist.c - Maintain the list of trusted keys /* trustlist.c - Maintain the list of trusted keys
* Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc. * Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -42,6 +42,7 @@ struct trustitem_s
int for_smime:1; /* Set by '*' or 'S' as first flag. */ int for_smime:1; /* Set by '*' or 'S' as first flag. */
int relax:1; /* Relax checking of root certificate int relax:1; /* Relax checking of root certificate
constraints. */ constraints. */
int cm:1; /* Use chain model for validation. */
} flags; } flags;
unsigned char fpr[20]; /* The binary fingerprint. */ unsigned char fpr[20]; /* The binary fingerprint. */
}; };
@ -267,6 +268,8 @@ read_one_trustfile (const char *fname, int allow_include,
} }
else if (n == 5 && !memcmp (p, "relax", 5)) else if (n == 5 && !memcmp (p, "relax", 5))
ti->flags.relax = 1; ti->flags.relax = 1;
else if (n == 2 && !memcmp (p, "cm", 2))
ti->flags.cm = 1;
else else
log_error ("flag `%.*s' in `%s', line %d ignored\n", log_error ("flag `%.*s' in `%s', line %d ignored\n",
n, p, fname, lnr); n, p, fname, lnr);
@ -396,6 +399,14 @@ agent_istrusted (ctrl_t ctrl, const char *fpr)
if (err) if (err)
return err; return err;
} }
else if (ti->flags.cm)
{
err = agent_write_status (ctrl,
"TRUSTLISTFLAG", "cm",
NULL);
if (err)
return err;
}
return 0; /* Trusted. */ return 0; /* Trusted. */
} }
} }

View File

@ -1,3 +1,10 @@
2007-08-07 Werner Koch <wk@g10code.com>
* tlv.c, tlv.h: Move from ../scd/.
* tlv.c (parse_sexp, parse_ber_header): Add ERRSOURCE arg and prefix
name with a _.
* tlv.h: Use macro to convey ERRSOURCE.
2007-08-02 Werner Koch <wk@g10code.com> 2007-08-02 Werner Koch <wk@g10code.com>
* gc-opt-flags.h: New. * gc-opt-flags.h: New.
@ -29,7 +36,7 @@
2007-07-04 Werner Koch <wk@g10code.com> 2007-07-04 Werner Koch <wk@g10code.com>
* estream.c (es_init_do): Do not throw an error if pth as already * estream.c (es_init_do): Do not throw an error if pth has already
been initialized. been initialized.
2007-06-26 Werner Koch <wk@g10code.com> 2007-06-26 Werner Koch <wk@g10code.com>

View File

@ -36,6 +36,7 @@ common_sources = \
gc-opt-flags.h \ gc-opt-flags.h \
keyserver.h \ keyserver.h \
sexp-parse.h \ sexp-parse.h \
tlv.c tlv.h \
init.c init.h \ init.c init.h \
sexputil.c \ sexputil.c \
sysutils.c sysutils.h \ sysutils.c sysutils.h \

View File

@ -72,10 +72,10 @@ gnupg_get_isotime (gnupg_isotime_t timebuf)
} }
/* set the time to NEWTIME so that gnupg_get_time returns a time /* Set the time to NEWTIME so that gnupg_get_time returns a time
starting with this one. With FREEZE set to 1 the returned time starting with this one. With FREEZE set to 1 the returned time
will never change. Just for completeness, a value of (time_t)-1 will never change. Just for completeness, a value of (time_t)-1
for NEWTIME gets you back to rality. Note that this is obviously for NEWTIME gets you back to reality. Note that this is obviously
not thread-safe but this is not required. */ not thread-safe but this is not required. */
void void
gnupg_set_time (time_t newtime, int freeze) gnupg_set_time (time_t newtime, int freeze)
@ -165,7 +165,7 @@ scan_isodatestr( const char *string )
return stamp; return stamp;
} }
/* Scan am ISO timestamp and return a epoch based timestamp. The only /* Scan am ISO timestamp and return an Epoch based timestamp. The only
supported format is "yyyymmddThhmmss" delimited by white space, nul, a supported format is "yyyymmddThhmmss" delimited by white space, nul, a
colon or a comma. Returns (time_t)(-1) for an invalid string. */ colon or a comma. Returns (time_t)(-1) for an invalid string. */
time_t time_t

View File

@ -29,7 +29,7 @@
#define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/ #define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/
#define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/ #define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/
typedef int gpg_error_t; typedef int gpg_error_t;
#define gpg_error(n) (n) #define gpg_make_err(x,n) (n)
#else #else
#include <gpg-error.h> #include <gpg-error.h>
#endif #endif
@ -151,10 +151,11 @@ find_tlv_unchecked (const unsigned char *buffer, size_t length,
and the length part from the TLV triplet. Update BUFFER and SIZE and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */ on success. */
gpg_error_t gpg_error_t
parse_ber_header (unsigned char const **buffer, size_t *size, _parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag, int *r_class, int *r_tag,
int *r_constructed, int *r_ndef, int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr) size_t *r_length, size_t *r_nhdr,
gpg_err_source_t errsource)
{ {
int c; int c;
unsigned long tag; unsigned long tag;
@ -167,7 +168,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
/* Get the tag. */ /* Get the tag. */
if (!length) if (!length)
return gpg_error (GPG_ERR_EOF); return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr; c = *buf++; length--; ++*r_nhdr;
*r_class = (c & 0xc0) >> 6; *r_class = (c & 0xc0) >> 6;
@ -181,7 +182,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
{ {
tag <<= 7; tag <<= 7;
if (!length) if (!length)
return gpg_error (GPG_ERR_EOF); return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr; c = *buf++; length--; ++*r_nhdr;
tag |= c & 0x7f; tag |= c & 0x7f;
@ -192,7 +193,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
/* Get the length. */ /* Get the length. */
if (!length) if (!length)
return gpg_error (GPG_ERR_EOF); return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr; c = *buf++; length--; ++*r_nhdr;
if ( !(c & 0x80) ) if ( !(c & 0x80) )
@ -200,20 +201,20 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
else if (c == 0x80) else if (c == 0x80)
*r_ndef = 1; *r_ndef = 1;
else if (c == 0xff) else if (c == 0xff)
return gpg_error (GPG_ERR_BAD_BER); return gpg_err_make (errsource, GPG_ERR_BAD_BER);
else else
{ {
unsigned long len = 0; unsigned long len = 0;
int count = c & 0x7f; int count = c & 0x7f;
if (count > sizeof (len) || count > sizeof (size_t)) if (count > sizeof (len) || count > sizeof (size_t))
return gpg_error (GPG_ERR_BAD_BER); return gpg_err_make (errsource, GPG_ERR_BAD_BER);
for (; count; count--) for (; count; count--)
{ {
len <<= 8; len <<= 8;
if (!length) if (!length)
return gpg_error (GPG_ERR_EOF); return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr; c = *buf++; length--; ++*r_nhdr;
len |= c & 0xff; len |= c & 0xff;
} }
@ -254,8 +255,9 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
handle_error (); handle_error ();
*/ */
gpg_error_t gpg_error_t
parse_sexp (unsigned char const **buf, size_t *buflen, _parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen) int *depth, unsigned char const **tok, size_t *toklen,
gpg_err_source_t errsource)
{ {
const unsigned char *s; const unsigned char *s;
size_t n, vlen; size_t n, vlen;
@ -265,7 +267,7 @@ parse_sexp (unsigned char const **buf, size_t *buflen,
*tok = NULL; *tok = NULL;
*toklen = 0; *toklen = 0;
if (!n) if (!n)
return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0; return *depth ? gpg_err_make (errsource, GPG_ERR_INV_SEXP) : 0;
if (*s == '(') if (*s == '(')
{ {
s++; n--; s++; n--;
@ -277,7 +279,7 @@ parse_sexp (unsigned char const **buf, size_t *buflen,
if (*s == ')') if (*s == ')')
{ {
if (!*depth) if (!*depth)
return gpg_error (GPG_ERR_INV_SEXP); return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
*toklen = 1; *toklen = 1;
s++; n--; s++; n--;
(*depth)--; (*depth)--;
@ -288,10 +290,10 @@ parse_sexp (unsigned char const **buf, size_t *buflen,
for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
vlen = vlen*10 + (*s - '0'); vlen = vlen*10 + (*s - '0');
if (!n || *s != ':') if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP); return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
s++; n--; s++; n--;
if (vlen > n) if (vlen > n)
return gpg_error (GPG_ERR_INV_SEXP); return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
*tok = s; *tok = s;
*toklen = vlen; *toklen = vlen;
s += vlen; s += vlen;

View File

@ -80,11 +80,14 @@ const unsigned char *find_tlv_unchecked (const unsigned char *buffer,
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */ on success. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, gpg_error_t _parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag, int *r_class, int *r_tag,
int *r_constructed, int *r_constructed,
int *r_ndef, size_t *r_length, size_t *r_nhdr); int *r_ndef, size_t *r_length, size_t *r_nhdr,
gpg_err_source_t errsource);
#define parse_ber_header(a,b,c,d,e,f,g,h) \
_parse_ber_header ((a),(b),(c),(d),(e),(f),(g),(h),\
GPG_ERR_SOURCE_DEFAULT)
/* Return the next token of an canconical encoded S-expression. BUF /* Return the next token of an canconical encoded S-expression. BUF
@ -99,8 +102,11 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
reflect on return the actual depth of the tree. To detect the end reflect on return the actual depth of the tree. To detect the end
of the S-expression it is advisable to check DEPTH after a of the S-expression it is advisable to check DEPTH after a
successful return. */ successful return. */
gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, gpg_error_t _parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen); int *depth, unsigned char const **tok, size_t *toklen,
gpg_err_source_t errsource);
#define parse_sexp(a,b,c,d,e) \
_parse_sexp ((a),(b),(c),(d),(e), GPG_ERR_SOURCE_DEFAULT)

View File

@ -112,7 +112,7 @@ const char *isotimestamp (u32 stamp); /* GMT */
const char *asctimestamp (u32 stamp); /* localized */ const char *asctimestamp (u32 stamp); /* localized */
/* Copy one iso ddate to another, this is inline so that we can do a /* Copy one ISO date to another, this is inline so that we can do a
sanity check. */ sanity check. */
static inline void static inline void
gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s) gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s)

View File

@ -56,8 +56,8 @@ VERSION=$PACKAGE_VERSION
AC_CONFIG_AUX_DIR(scripts) AC_CONFIG_AUX_DIR(scripts)
AC_CONFIG_SRCDIR(sm/gpgsm.c) AC_CONFIG_SRCDIR(sm/gpgsm.c)
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
AC_CANONICAL_TARGET()
AM_INIT_AUTOMAKE($PACKAGE, $VERSION) AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
AC_CANONICAL_HOST
AB_INIT AB_INIT
AC_GNU_SOURCE AC_GNU_SOURCE

View File

@ -1,3 +1,7 @@
2007-08-09 Werner Koch <wk@g10code.com>
* gpgsm.texi (Certificate Options): Describe --validation-model.
2007-07-23 Werner Koch <wk@g10code.com> 2007-07-23 Werner Koch <wk@g10code.com>
* scdaemon.texi (Scdaemon Commands): Remove obsolete --print-atr. * scdaemon.texi (Scdaemon Commands): Remove obsolete --print-atr.

View File

@ -253,8 +253,8 @@ more arguments in future versions.
presence of the letter 'T' inside. presence of the letter 'T' inside.
VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp> VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp>
<expire-timestamp> [ <sig-version> <reserved> <pubkey-algo> <expire-timestamp> <sig-version> <reserved> <pubkey-algo>
<hash-algo> <sig-class> <primary-key-fpr> ] <hash-algo> <sig-class> [ <primary-key-fpr> ]
The signature with the keyid is good. This is the same as The signature with the keyid is good. This is the same as
GOODSIG but has the fingerprint as the argument. Both status GOODSIG but has the fingerprint as the argument. Both status
@ -269,8 +269,9 @@ more arguments in future versions.
useful to get back to the primary key without running gpg useful to get back to the primary key without running gpg
again for this purpose. again for this purpose.
The optional parameters are used for OpenPGP and are not The primary-key-fpr parameter is used for OpenPGP and not
available for CMS signatures. available for CMS signatures. The sig-version as well as the
sig class is not defined for CMS and currently set to 0 and 00.
Note, that *-TIMESTAMP may either be a number with seconds Note, that *-TIMESTAMP may either be a number with seconds
since epoch or an ISO 8601 string which can be detected by the since epoch or an ISO 8601 string which can be detected by the
@ -310,13 +311,21 @@ more arguments in future versions.
TRUST_UNDEFINED <error token> TRUST_UNDEFINED <error token>
TRUST_NEVER <error token> TRUST_NEVER <error token>
TRUST_MARGINAL TRUST_MARGINAL [0 [<validation_model>]]
TRUST_FULLY TRUST_FULLY [0 [<validation_model>]]
TRUST_ULTIMATE TRUST_ULTIMATE [0 [<validation_model>]]
For good signatures one of these status lines are emitted For good signatures one of these status lines are emitted to
to indicate how trustworthy the signature is. The error token indicate how trustworthy the signature is. The error token
values are currently only emitted by gpgsm. values are currently only emitted by gpgsm. VALIDATION_MODEL
describes the algorithm used to check the validity of the key.
The default is the standard gpg Web of Trust model respective
the standard X.509 model. The defined values are
"pgp" for the standard PGP WoT.
"shell" for the standard X.509 model.
"chain" for the chain model.
PKA_TRUST_GOOD <mailbox> PKA_TRUST_GOOD <mailbox>
PKA_TRUST_BAD <mailbox> PKA_TRUST_BAD <mailbox>

View File

@ -412,6 +412,17 @@ requests in Dirmngr's configuration too (option
@option{--allow-ocsp} and configure dirmngr properly. If you don't do @option{--allow-ocsp} and configure dirmngr properly. If you don't do
so you will get the error code @samp{Not supported}. so you will get the error code @samp{Not supported}.
@item --validation-model @var{name}
@opindex validation-model
This option changes the default validation model. The only possible
values are "shell" (which is the default) and "chain" which forces the
use of the chain model. The chain model is also used if an option in
the @file{trustlist.txt} or an attribute of the certificate requests it.
However the standard model (shell) is in that case always tried first.
@end table @end table
@c ******************************************* @c *******************************************
@ -550,7 +561,8 @@ encryption. For convenience the strings @code{3DES}, @code{AES} and
@opindex faked-system-time @opindex faked-system-time
This option is only useful for testing; it sets the system time back or This option is only useful for testing; it sets the system time back or
forth to @var{epoch} which is the number of seconds elapsed since the year forth to @var{epoch} which is the number of seconds elapsed since the year
1970. 1970. Alternativly @var{epoch} may be given as a full ISO time string
(e.g. "20070924T154812").
@item --with-ephemeral-keys @item --with-ephemeral-keys
@opindex with-ephemeral-keys @opindex with-ephemeral-keys

View File

@ -122,7 +122,7 @@ This should return the Root cert of the issuer. See note above.
@item By exact match on serial number and issuer's DN. @item By exact match on serial number and issuer's DN.
This is indicated by a hash mark, followed by the hexadecmal This is indicated by a hash mark, followed by the hexadecmal
representation of the serial number, the followed by a slash and the representation of the serial number, then followed by a slash and the
RFC-2253 encoded DN of the issuer. See note above. RFC-2253 encoded DN of the issuer. See note above.
@cartouche @cartouche

View File

@ -1,3 +1,8 @@
2007-08-09 Werner Koch <wk@g10code.com>
* argparse.c (show_help): Expand the @EMAIL@ macro in the package
bug reporting address.
2007-08-02 Werner Koch <wk@g10code.com> 2007-08-02 Werner Koch <wk@g10code.com>
* t-stringhelp.c (test_compare_filenames): New. * t-stringhelp.c (test_compare_filenames): New.

View File

@ -832,8 +832,21 @@ show_help( ARGPARSE_OPTS *opts, unsigned flags )
puts("\n(A single dash may be used instead of the double ones)"); puts("\n(A single dash may be used instead of the double ones)");
} }
if( (s=strusage(19)) ) { /* bug reports to ... */ if( (s=strusage(19)) ) { /* bug reports to ... */
char *s2;
putchar('\n'); putchar('\n');
fputs(s, stdout); s2 = strstr (s, "@EMAIL@");
if (s2)
{
if (s2-s)
fwrite (s, s2-s, 1, stdout);
fputs (PACKAGE_BUGREPORT, stdout);
s2 += 7;
if (*s2)
fputs (s2, stdout);
}
else
fputs(s, stdout);
} }
fflush(stdout); fflush(stdout);
exit(0); exit(0);

1025
po/be.po

File diff suppressed because it is too large Load Diff

1062
po/ca.po

File diff suppressed because it is too large Load Diff

1050
po/cs.po

File diff suppressed because it is too large Load Diff

1047
po/da.po

File diff suppressed because it is too large Load Diff

1028
po/de.po

File diff suppressed because it is too large Load Diff

1049
po/el.po

File diff suppressed because it is too large Load Diff

1060
po/eo.po

File diff suppressed because it is too large Load Diff

1041
po/es.po

File diff suppressed because it is too large Load Diff

1049
po/et.po

File diff suppressed because it is too large Load Diff

1049
po/fi.po

File diff suppressed because it is too large Load Diff

1050
po/fr.po

File diff suppressed because it is too large Load Diff

1062
po/gl.po

File diff suppressed because it is too large Load Diff

1049
po/hu.po

File diff suppressed because it is too large Load Diff

1049
po/id.po

File diff suppressed because it is too large Load Diff

1049
po/it.po

File diff suppressed because it is too large Load Diff

1058
po/ja.po

File diff suppressed because it is too large Load Diff

1050
po/nb.po

File diff suppressed because it is too large Load Diff

1062
po/pl.po

File diff suppressed because it is too large Load Diff

1049
po/pt.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1028
po/ro.po

File diff suppressed because it is too large Load Diff

1027
po/ru.po

File diff suppressed because it is too large Load Diff

1049
po/sk.po

File diff suppressed because it is too large Load Diff

1028
po/sv.po

File diff suppressed because it is too large Load Diff

1028
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,7 @@
2007-08-07 Werner Koch <wk@g10code.com>
* tlv.c, tlv.h: Move to ../common/.
2007-08-02 Werner Koch <wk@g10code.com> 2007-08-02 Werner Koch <wk@g10code.com>
* scdaemon.c: Include gc-opt-flags.h and remove their definition * scdaemon.c: Include gc-opt-flags.h and remove their definition

View File

@ -38,7 +38,6 @@ scdaemon_SOURCES = \
apdu.c apdu.h \ apdu.c apdu.h \
ccid-driver.c ccid-driver.h \ ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \ iso7816.c iso7816.h \
tlv.c tlv.h \
app.c app-common.h app-help.c $(card_apps) app.c app-common.h app-help.c $(card_apps)
@ -55,7 +54,6 @@ scdaemon_LDADD = $(libcommonpth) ../jnlib/libjnlib.a ../gl/libgnu.a \
# apdu.c apdu.h \ # apdu.c apdu.h \
# ccid-driver.c ccid-driver.h \ # ccid-driver.c ccid-driver.h \
# iso7816.c iso7816.h \ # iso7816.c iso7816.h \
# tlv.c tlv.h \
# atr.c atr.h \ # atr.c atr.h \
# app.c app-common.h app-help.c $(card_apps) # app.c app-common.h app-help.c $(card_apps)
# #

View File

@ -1,3 +1,65 @@
2007-08-09 Werner Koch <wk@g10code.com>
* gpgsm.c (main) [W32]: Enable CRL check by default.
(main): Update the default control structure after reading the
options.
(gpgsm_parse_validation_model, parse_validation_model): New.
(main): New option --validation-model.
* certchain.c (gpgsm_validate_chain): Implement this option.
* server.c (option_handler): Ditto.
* certchain.c (is_cert_still_valid): Reformatted. Add arg
FORCE_OCSP. Changed callers to set this flag when using the chain
model.
2007-08-08 Werner Koch <wk@g10code.com>
* certdump.c (gpgsm_print_serial): Fixed brown paper bag style bugs
which prefixed the output with a 3A and cut it off at a 00.
* keylist.c (list_cert_raw): Print the certificate ID first and
rename "Serial number" to "S/N".
(list_cert_std): Ditto.
2007-08-07 Werner Koch <wk@g10code.com>
* gpgsm.c (main): Allow a string for --faked-system-time.
2007-08-06 Werner Koch <wk@g10code.com>
Implementation of the chain model.
* gpgsm.h (struct rootca_flags_s): Define new members VALID and
CHAIN_MODEL.
* call-agent.c (gpgsm_agent_istrusted): Mark ROOTCA_FLAGS valid.
(istrusted_status_cb): Set CHAIN_MODEL.
* certchain.c (gpgsm_validate_chain): Replace LM alias by LISTMODE
and FP by LISTFP.
(gpgsm_validate_chain): Factor some code out to ...
(check_validity_period, ask_marktrusted): .. new.
(check_validity_cm_basic, check_validity_cm_main): New.
(do_validate_chain): New with all code from gpgsm_validate_chain.
New arg ROOTCA_FLAGS.
(gpgsm_validate_chain): Provide ROOTCA_FLAGS and fallback to chain
model. Add RETFLAGS arg and changed all callers to pass NULL. Add
CHECKTIME arg and changed all callers to pass a nil value.
(has_validity_model_chain): New.
* verify.c (gpgsm_verify): Check for chain model and return as
part of the trust status.
* gpgsm.h (VALIDATE_FLAG_NO_DIRMNGR): New.
(VALIDATE_FLAG_NO_DIRMNGR): New.
* call-dirmngr.c (gpgsm_dirmngr_isvalid): Use constant here.
2007-08-03 Werner Koch <wk@g10code.com>
* keylist.c (list_cert_colon): Avoid duplicate listing of kludge
uids.
* verify.c (gpgsm_verify): Make STATUS_VERIFY return the hash and
pk algo.
* certcheck.c (gpgsm_check_cms_signature): Add arg R_PKALGO.
2007-08-02 Werner Koch <wk@g10code.com> 2007-08-02 Werner Koch <wk@g10code.com>
* gpgsm.c (main): Factored GC_OPT_FLAGS out to gc-opt-flags.h. * gpgsm.c (main): Factored GC_OPT_FLAGS out to gc-opt-flags.h.

View File

@ -486,6 +486,8 @@ istrusted_status_cb (void *opaque, const char *line)
; ;
if (!strncmp (line, "relax", 5) && (line[5] == ' ' || !line[5])) if (!strncmp (line, "relax", 5) && (line[5] == ' ' || !line[5]))
flags->relax = 1; flags->relax = 1;
else if (!strncmp (line, "cm", 2) && (line[2] == ' ' || !line[2]))
flags->chain_model = 1;
} }
return 0; return 0;
} }
@ -521,6 +523,8 @@ gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert,
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
istrusted_status_cb, rootca_flags); istrusted_status_cb, rootca_flags);
if (!rc)
rootca_flags->valid = 1;
return rc; return rc;
} }

View File

@ -394,8 +394,10 @@ isvalid_status_cb (void *opaque, const char *line)
GPG_ERR_NO_CRL_KNOWN GPG_ERR_NO_CRL_KNOWN
GPG_ERR_CRL_TOO_OLD GPG_ERR_CRL_TOO_OLD
With USE_OCSP set to true, the dirmngr is asked to do an OCSP Values for USE_OCSP:
request first. 0 = Do CRL check.
1 = Do an OCSP check.
2 = Do an OCSP check using only the default responder.
*/ */
int int
gpgsm_dirmngr_isvalid (ctrl_t ctrl, gpgsm_dirmngr_isvalid (ctrl_t ctrl,
@ -445,7 +447,8 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
/* FIXME: If --disable-crl-checks has been set, we should pass an /* FIXME: If --disable-crl-checks has been set, we should pass an
option to dirmngr, so that no fallback CRL check is done after an option to dirmngr, so that no fallback CRL check is done after an
ocsp check. */ ocsp check. It is not a problem right now as dirmngr does not
fallback to CRL checking. */
/* It is sufficient to send the options only once because we have /* It is sufficient to send the options only once because we have
one connection per process only. */ one connection per process only. */
@ -456,7 +459,9 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
NULL, NULL, NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL, NULL, NULL);
did_options = 1; did_options = 1;
} }
snprintf (line, DIM(line)-1, "ISVALID %s", certid); snprintf (line, DIM(line)-1, "ISVALID%s %s",
use_ocsp == 2? " --only-ocsp --force-default-responder":"",
certid);
line[DIM(line)-1] = 0; line[DIM(line)-1] = 0;
xfree (certid); xfree (certid);
@ -504,9 +509,10 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
rc = gpg_error (GPG_ERR_INV_CRL); rc = gpg_error (GPG_ERR_INV_CRL);
else else
{ {
/* Note, the flag = 1: This avoids checking this /* Note the no_dirmngr flag: This avoids checking
certificate over and over again. */ this certificate over and over again. */
rc = gpgsm_validate_chain (ctrl, rspcert, NULL, 0, NULL, 1); rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL,
VALIDATE_FLAG_NO_DIRMNGR, NULL);
if (rc) if (rc)
{ {
log_error ("invalid certificate used for CRL/OCSP: %s\n", log_error ("invalid certificate used for CRL/OCSP: %s\n",

View File

@ -1,6 +1,6 @@
/* certchain.c - certificate chain validation /* certchain.c - certificate chain validation
* Copyright (C) 2001, 2002, 2003, 2004, 2005, * Copyright (C) 2001, 2002, 2003, 2004, 2005,
* 2006 Free Software Foundation, Inc. * 2006, 2007 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -37,6 +37,7 @@
#include "keydb.h" #include "keydb.h"
#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
#include "i18n.h" #include "i18n.h"
#include "tlv.h"
/* Object to keep track of certain root certificates. */ /* Object to keep track of certain root certificates. */
@ -141,6 +142,71 @@ compare_certs (ksba_cert_t a, ksba_cert_t b)
} }
/* Return true if CERT has the validityModel extensions and defines
the use of the chain model. */
static int
has_validation_model_chain (ksba_cert_t cert, int listmode, estream_t listfp)
{
gpg_error_t err;
int idx, yes;
const char *oid;
size_t off, derlen, objlen, hdrlen;
const unsigned char *der;
int class, tag, constructed, ndef;
char *oidbuf;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
&oid, NULL, &off, &derlen));idx++)
if (!strcmp (oid, "1.3.6.1.4.1.8301.3.5") )
break;
if (err)
return 0; /* Not found. */
der = ksba_cert_get_image (cert, NULL);
if (!der)
{
err = gpg_error (GPG_ERR_INV_OBJ); /* Oops */
goto leave;
}
der += off;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_SEQUENCE))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
derlen = objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OBJECT_ID))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
goto leave;
oidbuf = ksba_oid_to_str (der, objlen);
if (!oidbuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (opt.verbose)
do_list (0, listmode, listfp,
_("validation model requested by certificate: %s"),
!strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1")? _("chain") :
!strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.2")? _("shell") :
/* */ oidbuf);
yes = !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1");
ksba_free (oidbuf);
return yes;
leave:
log_error ("error parsing validityModel: %s\n", gpg_strerror (err));
return 0;
}
static int static int
unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp) unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
{ {
@ -155,6 +221,7 @@ unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
"2.5.29.19", /* basic Constraints */ "2.5.29.19", /* basic Constraints */
"2.5.29.32", /* certificatePolicies */ "2.5.29.32", /* certificatePolicies */
"2.5.29.37", /* extendedKeyUsage - handled by certlist.c */ "2.5.29.37", /* extendedKeyUsage - handled by certlist.c */
"1.3.6.1.4.1.8301.3.5", /* validityModel - handled here. */
NULL NULL
}; };
int rc = 0, i, idx, crit; int rc = 0, i, idx, crit;
@ -653,73 +720,310 @@ gpgsm_is_root_cert (ksba_cert_t cert)
/* This is a helper for gpgsm_validate_chain. */ /* This is a helper for gpgsm_validate_chain. */
static gpg_error_t static gpg_error_t
is_cert_still_valid (ctrl_t ctrl, int lm, estream_t fp, is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp,
ksba_cert_t subject_cert, ksba_cert_t issuer_cert, ksba_cert_t subject_cert, ksba_cert_t issuer_cert,
int *any_revoked, int *any_no_crl, int *any_crl_too_old) int *any_revoked, int *any_no_crl, int *any_crl_too_old)
{ {
if (!opt.no_crl_check || ctrl->use_ocsp) gpg_error_t err;
{
gpg_error_t err;
err = gpgsm_dirmngr_isvalid (ctrl, if (opt.no_crl_check && !ctrl->use_ocsp)
subject_cert, issuer_cert, ctrl->use_ocsp); return 0;
if (err)
err = gpgsm_dirmngr_isvalid (ctrl,
subject_cert, issuer_cert,
force_ocsp? 2 : !!ctrl->use_ocsp);
if (err)
{
if (!lm)
gpgsm_cert_log_name (NULL, subject_cert);
switch (gpg_err_code (err))
{ {
/* Fixme: We should change the wording because we may case GPG_ERR_CERT_REVOKED:
have used OCSP. */ do_list (1, lm, fp, _("certificate has been revoked"));
*any_revoked = 1;
/* Store that in the keybox so that key listings are able to
return the revoked flag. We don't care about error,
though. */
keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0,
~0, VALIDITY_REVOKED);
break;
case GPG_ERR_NO_CRL_KNOWN:
do_list (1, lm, fp, _("no CRL found for certificate"));
*any_no_crl = 1;
break;
case GPG_ERR_NO_DATA:
do_list (1, lm, fp, _("the status of the certificate is unknown"));
*any_no_crl = 1;
break;
case GPG_ERR_CRL_TOO_OLD:
do_list (1, lm, fp, _("the available CRL is too old"));
if (!lm) if (!lm)
gpgsm_cert_log_name (NULL, subject_cert); log_info (_("please make sure that the "
switch (gpg_err_code (err)) "\"dirmngr\" is properly installed\n"));
{ *any_crl_too_old = 1;
case GPG_ERR_CERT_REVOKED: break;
do_list (1, lm, fp, _("certificate has been revoked"));
*any_revoked = 1; default:
/* Store that in the keybox so that key listings are do_list (1, lm, fp, _("checking the CRL failed: %s"),
able to return the revoked flag. We don't care gpg_strerror (err));
about error, though. */ return err;
keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0,
~0, VALIDITY_REVOKED);
break;
case GPG_ERR_NO_CRL_KNOWN:
do_list (1, lm, fp, _("no CRL found for certificate"));
*any_no_crl = 1;
break;
case GPG_ERR_CRL_TOO_OLD:
do_list (1, lm, fp, _("the available CRL is too old"));
if (!lm)
log_info (_("please make sure that the "
"\"dirmngr\" is properly installed\n"));
*any_crl_too_old = 1;
break;
default:
do_list (1, lm, fp, _("checking the CRL failed: %s"),
gpg_strerror (err));
return err;
}
} }
} }
return 0; return 0;
} }
/* Helper for gpgsm_validate_chain to check the validity period of
SUBJECT_CERT. The caller needs to pass EXPTIME which will be
updated to the nearest expiration time seen. A DEPTH of 0 indicates
the target certifciate, -1 the final root certificate and other
values intermediate certificates. */
static gpg_error_t
check_validity_period (ksba_isotime_t current_time,
ksba_cert_t subject_cert,
ksba_isotime_t exptime,
int listmode, estream_t listfp, int depth)
{
gpg_error_t err;
ksba_isotime_t not_before, not_after;
err = ksba_cert_get_validity (subject_cert, 0, not_before);
if (!err)
err = ksba_cert_get_validity (subject_cert, 1, not_after);
if (err)
{
do_list (1, listmode, listfp,
_("certificate with invalid validity: %s"), gpg_strerror (err));
return gpg_error (GPG_ERR_BAD_CERT);
}
if (*not_after)
{
if (!*exptime)
gnupg_copy_time (exptime, not_after);
else if (strcmp (not_after, exptime) < 0 )
gnupg_copy_time (exptime, not_after);
}
if (*not_before && strcmp (current_time, not_before) < 0 )
{
do_list (1, listmode, listfp,
depth == 0 ? _("certificate not yet valid") :
depth == -1 ? _("root certificate not yet valid") :
/* other */ _("intermediate certificate not yet valid"));
if (!listmode)
{
log_info (" (valid from ");
gpgsm_dump_time (not_before);
log_printf (")\n");
}
return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
}
if (*not_after && strcmp (current_time, not_after) > 0 )
{
do_list (opt.ignore_expiration?0:1, listmode, listfp,
depth == 0 ? _("certificate has expired") :
depth == -1 ? _("root certificate has expired") :
/* other */ _("intermediate certificate has expired"));
if (!listmode)
{
log_info (" (expired at ");
gpgsm_dump_time (not_after);
log_printf (")\n");
}
if (opt.ignore_expiration)
log_info ("WARNING: ignoring expiration\n");
else
return gpg_error (GPG_ERR_CERT_EXPIRED);
}
return 0;
}
/* This is a variant of check_validity_period used with the chain
model. The dextra contraint here is that notBefore and notAfter
must exists and if the additional argument CHECK_TIME is given this
time is used to check the validity period of SUBJECT_CERT. */
static gpg_error_t
check_validity_period_cm (ksba_isotime_t current_time,
ksba_isotime_t check_time,
ksba_cert_t subject_cert,
ksba_isotime_t exptime,
int listmode, estream_t listfp, int depth)
{
gpg_error_t err;
ksba_isotime_t not_before, not_after;
err = ksba_cert_get_validity (subject_cert, 0, not_before);
if (!err)
err = ksba_cert_get_validity (subject_cert, 1, not_after);
if (err)
{
do_list (1, listmode, listfp,
_("certificate with invalid validity: %s"), gpg_strerror (err));
return gpg_error (GPG_ERR_BAD_CERT);
}
if (!*not_before || !*not_after)
{
do_list (1, listmode, listfp,
_("required certificate attributes missing: %s%s%s"),
!*not_before? "notBefore":"",
(!*not_before && !*not_after)? ", ":"",
!*not_before? "notAfter":"");
return gpg_error (GPG_ERR_BAD_CERT);
}
if (strcmp (not_before, not_after) > 0 )
{
do_list (1, listmode, listfp,
_("certificate with invalid validity"));
log_info (" (valid from ");
gpgsm_dump_time (not_before);
log_printf (" expired at ");
gpgsm_dump_time (not_after);
log_printf (")\n");
return gpg_error (GPG_ERR_BAD_CERT);
}
if (!*exptime)
gnupg_copy_time (exptime, not_after);
else if (strcmp (not_after, exptime) < 0 )
gnupg_copy_time (exptime, not_after);
if (strcmp (current_time, not_before) < 0 )
{
do_list (1, listmode, listfp,
depth == 0 ? _("certificate not yet valid") :
depth == -1 ? _("root certificate not yet valid") :
/* other */ _("intermediate certificate not yet valid"));
if (!listmode)
{
log_info (" (valid from ");
gpgsm_dump_time (not_before);
log_printf (")\n");
}
return gpg_error (GPG_ERR_CERT_TOO_YOUNG);
}
if (*check_time
&& (strcmp (check_time, not_before) < 0
|| strcmp (check_time, not_after) > 0))
{
/* Note that we don't need a case for the root certificate
because its own consitency has already been checked. */
do_list(opt.ignore_expiration?0:1, listmode, listfp,
depth == 0 ?
_("signature not created during lifetime of certificate") :
depth == 1 ?
_("certificate not created during lifetime of issuer") :
_("intermediate certificate not created during lifetime "
"of issuer"));
if (!listmode)
{
log_info (depth== 0? _(" ( signature created at ") :
/* */ _(" (certificate created at ") );
gpgsm_dump_time (check_time);
log_printf (")\n");
log_info (depth==0? _(" (certificate valid from ") :
/* */ _(" ( issuer valid from ") );
gpgsm_dump_time (not_before);
log_info (" to ");
gpgsm_dump_time (not_after);
log_printf (")\n");
}
if (opt.ignore_expiration)
log_info ("WARNING: ignoring expiration\n");
else
return gpg_error (GPG_ERR_CERT_EXPIRED);
}
return 0;
}
/* Ask the user whether he wants to mark the certificate CERT trusted.
Returns true if the CERT is the trusted. We also check whether the
agent is at all enabled to allow marktrusted and don't call it in
this session again if it is not. */
static int
ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode)
{
static int no_more_questions;
int rc;
char *fpr;
int success = 0;
fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
xfree (fpr);
if (no_more_questions)
rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
rc = gpgsm_agent_marktrusted (ctrl, cert);
if (!rc)
{
log_info (_("root certificate has now been marked as trusted\n"));
success = 1;
}
else if (!listmode)
{
gpgsm_dump_cert ("issuer", cert);
log_info ("after checking the fingerprint, you may want "
"to add it manually to the list of trusted certificates.\n");
}
if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED)
{
if (!no_more_questions)
log_info (_("interactive marking as trusted "
"not enabled in gpg-agent\n"));
no_more_questions = 1;
}
else if (gpg_err_code (rc) == GPG_ERR_CANCELED)
{
log_info (_("interactive marking as trusted "
"disabled for this session\n"));
no_more_questions = 1;
}
else
set_already_asked_marktrusted (cert);
return success;
}
/* Validate a chain and optionally return the nearest expiration time /* Validate a chain and optionally return the nearest expiration time
in R_EXPTIME. With LISTMODE set to 1 a special listmode is in R_EXPTIME. With LISTMODE set to 1 a special listmode is
activated where only information about the certificate is printed activated where only information about the certificate is printed
to FP and no output is send to the usual log stream. to LISTFP and no output is send to the usual log stream. If
CHECKTIME_ARG is set, it is used only in the chain model instead of the
current time.
Defined flag bits: 0 - do not do any dirmngr isvalid checks. Defined flag bits
VALIDATE_FLAG_NO_DIRMNGR - Do not do any dirmngr isvalid checks.
VALIDATE_FLAG_CHAIN_MODEL - Check according to chain model.
*/ */
int static int
gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
int listmode, estream_t fp, unsigned int flags) ksba_isotime_t r_exptime,
int listmode, estream_t listfp, unsigned int flags,
struct rootca_flags_s *rootca_flags)
{ {
int rc = 0, depth = 0, maxdepth; int rc = 0, depth, maxdepth;
char *issuer = NULL; char *issuer = NULL;
char *subject = NULL; char *subject = NULL;
KEYDB_HANDLE kh = NULL; KEYDB_HANDLE kh = NULL;
ksba_cert_t subject_cert = NULL, issuer_cert = NULL; ksba_cert_t subject_cert = NULL, issuer_cert = NULL;
ksba_isotime_t current_time; ksba_isotime_t current_time;
ksba_isotime_t check_time;
ksba_isotime_t exptime; ksba_isotime_t exptime;
int any_expired = 0; int any_expired = 0;
int any_revoked = 0; int any_revoked = 0;
@ -729,11 +1033,26 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
int is_qualified = -1; /* Indicates whether the certificate stems int is_qualified = -1; /* Indicates whether the certificate stems
from a qualified root certificate. from a qualified root certificate.
-1 = unknown, 0 = no, 1 = yes. */ -1 = unknown, 0 = no, 1 = yes. */
int lm = listmode;
chain_item_t chain = NULL; /* A list of all certificates in the chain. */ chain_item_t chain = NULL; /* A list of all certificates in the chain. */
gnupg_get_isotime (current_time); gnupg_get_isotime (current_time);
if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
{
if (!strcmp (checktime_arg, "19700101T000000"))
{
do_list (1, listmode, listfp,
_("WARNING: creation time of signature not known - "
"assuming current time"));
gnupg_copy_time (check_time, current_time);
}
else
gnupg_copy_time (check_time, checktime_arg);
}
else
*check_time = 0;
if (r_exptime) if (r_exptime)
*r_exptime = 0; *r_exptime = 0;
*exptime = 0; *exptime = 0;
@ -758,12 +1077,12 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
subject_cert = cert; subject_cert = cert;
ksba_cert_ref (subject_cert); ksba_cert_ref (subject_cert);
maxdepth = 50; maxdepth = 50;
depth = 0;
for (;;) for (;;)
{ {
int is_root; int is_root;
gpg_error_t istrusted_rc = -1; gpg_error_t istrusted_rc = -1;
struct rootca_flags_s rootca_flags;
/* Put the certificate on our list. */ /* Put the certificate on our list. */
{ {
@ -788,13 +1107,16 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
if (!issuer) if (!issuer)
{ {
do_list (1, lm, fp, _("no issuer found in certificate")); do_list (1, listmode, listfp, _("no issuer found in certificate"));
rc = gpg_error (GPG_ERR_BAD_CERT); rc = gpg_error (GPG_ERR_BAD_CERT);
goto leave; goto leave;
} }
/* Is this a self-issued certificate (i.e. the root certificate)? */ /* Is this a self-issued certificate (i.e. the root
certificate)? This is actually the same test as done by
gpgsm_is_root_cert but here we want to keep the issuer and
subject for later use. */
is_root = (subject && !strcmp (issuer, subject)); is_root = (subject && !strcmp (issuer, subject));
if (is_root) if (is_root)
{ {
@ -804,71 +1126,41 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
check right here so that we can access special flags check right here so that we can access special flags
associated with that specific root certificate. */ associated with that specific root certificate. */
istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert,
&rootca_flags); rootca_flags);
/* If the chain model extended attribute is used, make sure
that our chain model flag is set. */
if (has_validation_model_chain (subject_cert, listmode, listfp))
rootca_flags->chain_model = 1;
} }
/* Check the validity period. */ /* Check the validity period. */
{ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
ksba_isotime_t not_before, not_after; rc = check_validity_period_cm (current_time, check_time, subject_cert,
exptime, listmode, listfp,
rc = ksba_cert_get_validity (subject_cert, 0, not_before); (depth && is_root)? -1: depth);
if (!rc) else
rc = ksba_cert_get_validity (subject_cert, 1, not_after); rc = check_validity_period (current_time, subject_cert,
if (rc) exptime, listmode, listfp,
{ (depth && is_root)? -1: depth);
do_list (1, lm, fp, _("certificate with invalid validity: %s"), if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
gpg_strerror (rc)); {
rc = gpg_error (GPG_ERR_BAD_CERT); any_expired = 1;
goto leave; rc = 0;
} }
else if (rc)
if (*not_after) goto leave;
{
if (!*exptime)
gnupg_copy_time (exptime, not_after);
else if (strcmp (not_after, exptime) < 0 )
gnupg_copy_time (exptime, not_after);
}
if (*not_before && strcmp (current_time, not_before) < 0 )
{
do_list (1, lm, fp, _("certificate not yet valid"));
if (!lm)
{
log_info ("(valid from ");
gpgsm_dump_time (not_before);
log_printf (")\n");
}
rc = gpg_error (GPG_ERR_CERT_TOO_YOUNG);
goto leave;
}
if (*not_after && strcmp (current_time, not_after) > 0 )
{
do_list (opt.ignore_expiration?0:1, lm, fp,
_("certificate has expired"));
if (!lm)
{
log_info ("(expired at ");
gpgsm_dump_time (not_after);
log_printf (")\n");
}
if (opt.ignore_expiration)
log_info ("WARNING: ignoring expiration\n");
else
any_expired = 1;
}
}
/* Assert that we understand all critical extensions. */ /* Assert that we understand all critical extensions. */
rc = unknown_criticals (subject_cert, listmode, fp); rc = unknown_criticals (subject_cert, listmode, listfp);
if (rc) if (rc)
goto leave; goto leave;
/* Do a policy check. */ /* Do a policy check. */
if (!opt.no_policy_check) if (!opt.no_policy_check)
{ {
rc = check_cert_policy (subject_cert, listmode, fp); rc = check_cert_policy (subject_cert, listmode, listfp);
if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH)
{ {
any_no_policy_match = 1; any_no_policy_match = 1;
@ -879,7 +1171,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
} }
/* Is this a self-issued certificate? */ /* If this is the root certificate we are at the end of the chain. */
if (is_root) if (is_root)
{ {
if (!istrusted_rc) if (!istrusted_rc)
@ -888,7 +1180,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
{ {
/* We only check the signature if the certificate is not /* We only check the signature if the certificate is not
trusted for better diagnostics. */ trusted for better diagnostics. */
do_list (1, lm, fp, do_list (1, listmode, listfp,
_("self-signed certificate has a BAD signature")); _("self-signed certificate has a BAD signature"));
if (DBG_X509) if (DBG_X509)
{ {
@ -898,9 +1190,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
: GPG_ERR_BAD_CERT); : GPG_ERR_BAD_CERT);
goto leave; goto leave;
} }
if (!rootca_flags.relax) if (!rootca_flags->relax)
{ {
rc = allowed_ca (subject_cert, NULL, listmode, fp); rc = allowed_ca (subject_cert, NULL, listmode, listfp);
if (rc) if (rc)
goto leave; goto leave;
} }
@ -957,57 +1249,17 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
; ;
else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED)
{ {
do_list (0, lm, fp, _("root certificate is not marked trusted")); do_list (0, listmode, listfp,
_("root certificate is not marked trusted"));
/* If we already figured out that the certificate is /* If we already figured out that the certificate is
expired it does not make much sense to ask the user expired it does not make much sense to ask the user
whether we wants to trust the root certificate. He whether we wants to trust the root certificate. We
should do this only if the certificate under question should do this only if the certificate under question
will then be usable. We also check whether the agent will then be usable. */
is at all enabled to allo marktrusted and don't call
it in this session again if it is not. */
if ( !any_expired if ( !any_expired
&& (!lm || !already_asked_marktrusted (subject_cert))) && (!listmode || !already_asked_marktrusted (subject_cert))
{ && ask_marktrusted (ctrl, subject_cert, listmode) )
static int no_more_questions; /* during this session. */ rc = 0;
int rc2;
char *fpr = gpgsm_get_fingerprint_string (subject_cert,
GCRY_MD_SHA1);
log_info (_("fingerprint=%s\n"), fpr? fpr : "?");
xfree (fpr);
if (no_more_questions)
rc2 = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
rc2 = gpgsm_agent_marktrusted (ctrl, subject_cert);
if (!rc2)
{
log_info (_("root certificate has now"
" been marked as trusted\n"));
rc = 0;
}
else if (!lm)
{
gpgsm_dump_cert ("issuer", subject_cert);
log_info ("after checking the fingerprint, you may want "
"to add it manually to the list of trusted "
"certificates.\n");
}
if (gpg_err_code (rc2) == GPG_ERR_NOT_SUPPORTED)
{
if (!no_more_questions)
log_info (_("interactive marking as trusted "
"not enabled in gpg-agent\n"));
no_more_questions = 1;
}
else if (gpg_err_code (rc2) == GPG_ERR_CANCELED)
{
log_info (_("interactive marking as trusted "
"disabled for this session\n"));
no_more_questions = 1;
}
else
set_already_asked_marktrusted (subject_cert);
}
} }
else else
{ {
@ -1019,12 +1271,14 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
goto leave; goto leave;
/* Check for revocations etc. */ /* Check for revocations etc. */
if ((flags & 1)) if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
; ;
else if (opt.no_trusted_cert_crl_check || rootca_flags.relax) else if (opt.no_trusted_cert_crl_check || rootca_flags->relax)
; ;
else else
rc = is_cert_still_valid (ctrl, lm, fp, rc = is_cert_still_valid (ctrl,
(flags & VALIDATE_FLAG_CHAIN_MODEL),
listmode, listfp,
subject_cert, subject_cert, subject_cert, subject_cert,
&any_revoked, &any_no_crl, &any_revoked, &any_no_crl,
&any_crl_too_old); &any_crl_too_old);
@ -1032,13 +1286,13 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
goto leave; goto leave;
break; /* Okay: a self-signed certicate is an end-point. */ break; /* Okay: a self-signed certicate is an end-point. */
} } /* End is_root. */
/* Take care that the chain does not get too long. */ /* Take care that the chain does not get too long. */
depth++; if ((depth+1) > maxdepth)
if (depth > maxdepth)
{ {
do_list (1, lm, fp, _("certificate chain too long\n")); do_list (1, listmode, listfp, _("certificate chain too long\n"));
rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
goto leave; goto leave;
} }
@ -1050,8 +1304,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
{ {
if (rc == -1) if (rc == -1)
{ {
do_list (0, lm, fp, _("issuer certificate not found")); do_list (0, listmode, listfp, _("issuer certificate not found"));
if (!lm) if (!listmode)
{ {
log_info ("issuer certificate: #/"); log_info ("issuer certificate: #/");
gpgsm_dump_string (issuer); gpgsm_dump_string (issuer);
@ -1083,7 +1337,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
rc = gpgsm_check_cert_sig (issuer_cert, subject_cert); rc = gpgsm_check_cert_sig (issuer_cert, subject_cert);
if (rc) if (rc)
{ {
do_list (0, lm, fp, _("certificate has a BAD signature")); do_list (0, listmode, listfp, _("certificate has a BAD signature"));
if (DBG_X509) if (DBG_X509)
{ {
gpgsm_dump_cert ("signing issuer", issuer_cert); gpgsm_dump_cert ("signing issuer", issuer_cert);
@ -1113,8 +1367,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
} }
else else
{ {
do_list (0, lm, fp, _("found another possible matching " do_list (0, listmode, listfp,
"CA certificate - trying again")); _("found another possible matching "
"CA certificate - trying again"));
ksba_cert_release (issuer_cert); ksba_cert_release (issuer_cert);
issuer_cert = tmp_cert; issuer_cert = tmp_cert;
goto try_another_cert; goto try_another_cert;
@ -1128,14 +1383,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
goto leave; goto leave;
} }
is_root = 0; is_root = gpgsm_is_root_cert (issuer_cert);
istrusted_rc = -1; istrusted_rc = -1;
/* Check that a CA is allowed to issue certificates. */ /* Check that a CA is allowed to issue certificates. */
{ {
int chainlen; int chainlen;
rc = allowed_ca (issuer_cert, &chainlen, listmode, fp); rc = allowed_ca (issuer_cert, &chainlen, listmode, listfp);
if (rc) if (rc)
{ {
/* Not allowed. Check whether this is a trusted root /* Not allowed. Check whether this is a trusted root
@ -1146,12 +1402,11 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
certificates carry proper BasicContraints our way of certificates carry proper BasicContraints our way of
overriding an error in the way is justified for overriding an error in the way is justified for
performance reasons. */ performance reasons. */
if (gpgsm_is_root_cert (issuer_cert)) if (is_root)
{ {
is_root = 1;
istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert,
&rootca_flags); rootca_flags);
if (!istrusted_rc && rootca_flags.relax) if (!istrusted_rc && rootca_flags->relax)
{ {
/* Ignore the error due to the relax flag. */ /* Ignore the error due to the relax flag. */
rc = 0; rc = 0;
@ -1161,9 +1416,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
} }
if (rc) if (rc)
goto leave; goto leave;
if (chainlen >= 0 && (depth - 1) > chainlen) if (chainlen >= 0 && depth > chainlen)
{ {
do_list (1, lm, fp, do_list (1, listmode, listfp,
_("certificate chain longer than allowed by CA (%d)"), _("certificate chain longer than allowed by CA (%d)"),
chainlen); chainlen);
rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
@ -1188,13 +1443,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
/* Check for revocations etc. Note that for a root certificate /* Check for revocations etc. Note that for a root certificate
this test is done a second time later. This should eventually this test is done a second time later. This should eventually
be fixed. */ be fixed. */
if ((flags & 1)) if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
rc = 0; rc = 0;
else if (is_root && (opt.no_trusted_cert_crl_check else if (is_root && (opt.no_trusted_cert_crl_check
|| (!istrusted_rc && rootca_flags.relax))) || (!istrusted_rc && rootca_flags->relax)))
; rc = 0;
else else
rc = is_cert_still_valid (ctrl, lm, fp, rc = is_cert_still_valid (ctrl,
(flags & VALIDATE_FLAG_CHAIN_MODEL),
listmode, listfp,
subject_cert, issuer_cert, subject_cert, issuer_cert,
&any_revoked, &any_no_crl, &any_crl_too_old); &any_revoked, &any_no_crl, &any_crl_too_old);
if (rc) if (rc)
@ -1202,13 +1459,29 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
if (opt.verbose && !listmode) if (opt.verbose && !listmode)
log_info ("certificate is good\n"); log_info (depth == 0 ? _("certificate is good\n") :
!is_root ? _("intermediate certificate is good\n") :
/* other */ _("root certificate is good\n"));
/* Under the chain model the next check time is the creation
time of the subject certificate. */
if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
{
rc = ksba_cert_get_validity (subject_cert, 0, check_time);
if (rc)
{
/* That will never happen as we have already checked
this above. */
BUG ();
}
}
/* For the next round the current issuer becomes the new subject. */ /* For the next round the current issuer becomes the new subject. */
keydb_search_reset (kh); keydb_search_reset (kh);
ksba_cert_release (subject_cert); ksba_cert_release (subject_cert);
subject_cert = issuer_cert; subject_cert = issuer_cert;
issuer_cert = NULL; issuer_cert = NULL;
depth++;
} /* End chain traversal. */ } /* End chain traversal. */
if (!listmode) if (!listmode)
@ -1238,7 +1511,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
leave: leave:
/* If we have traversed a complete chain up to the root we will /* If we have traversed a complete chain up to the root we will
reset the ephemeral flag for all these certificates. his is done reset the ephemeral flag for all these certificates. This is done
regardless of any error because those errors may only be regardless of any error because those errors may only be
transient. */ transient. */
if (chain && chain->is_root) if (chain && chain->is_root)
@ -1306,6 +1579,61 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
} }
/* Validate a certifcate chain. For a description see the
do_validate_chain. This function is a wrapper to handle a root
certificate with the chain_model flag set. If RETFLAGS is not
NULL, flags indicating now the verification was done are stored
there. The only defined flag for RETFLAGS is
VALIDATE_FLAG_CHAIN_MODEL.
If you are verifying a signature you should set CHECKTIME to the
creation time of the signature. If your are verifying a
certificate, set it nil (i.e. the empty string). If the creation
date of the signature is not known use the special date
"19700101T000000" which is treated in a special way here. */
int
gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
ksba_isotime_t r_exptime,
int listmode, estream_t listfp, unsigned int flags,
unsigned int *retflags)
{
int rc;
struct rootca_flags_s rootca_flags;
unsigned int dummy_retflags;
if (!retflags)
retflags = &dummy_retflags;
if (ctrl->validation_model == 1)
flags |= VALIDATE_FLAG_CHAIN_MODEL;
*retflags = (flags & VALIDATE_FLAG_CHAIN_MODEL);
memset (&rootca_flags, 0, sizeof rootca_flags);
rc = do_validate_chain (ctrl, cert, checktime,
r_exptime, listmode, listfp, flags,
&rootca_flags);
if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED
&& !(flags & VALIDATE_FLAG_CHAIN_MODEL)
&& (rootca_flags.valid && rootca_flags.chain_model))
{
do_list (0, listmode, listfp, _("switching to chain model"));
rc = do_validate_chain (ctrl, cert, checktime,
r_exptime, listmode, listfp,
(flags |= VALIDATE_FLAG_CHAIN_MODEL),
&rootca_flags);
*retflags |= VALIDATE_FLAG_CHAIN_MODEL;
}
if (opt.verbose)
do_list (0, listmode, listfp, _("validation model used: %s"),
(*retflags & VALIDATE_FLAG_CHAIN_MODEL)?
_("chain model"):_("shell model"));
return rc;
}
/* Check that the given certificate is valid but DO NOT check any /* Check that the given certificate is valid but DO NOT check any
constraints. We assume that the issuers certificate is already in constraints. We assume that the issuers certificate is already in
the DB and that this one is valid; which it should be because it the DB and that this one is valid; which it should be because it
@ -1399,7 +1727,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
goto leave; goto leave;
} }
if (opt.verbose) if (opt.verbose)
log_info ("certificate is good\n"); log_info (_("certificate is good\n"));
} }
leave: leave:

View File

@ -343,13 +343,17 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
int int
gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
gcry_md_hd_t md, int algo) gcry_md_hd_t md, int mdalgo, int *r_pkalgo)
{ {
int rc; int rc;
ksba_sexp_t p; ksba_sexp_t p;
gcry_mpi_t frame; gcry_mpi_t frame;
gcry_sexp_t s_sig, s_hash, s_pkey; gcry_sexp_t s_sig, s_hash, s_pkey;
size_t n; size_t n;
int pkalgo;
if (r_pkalgo)
*r_pkalgo = 0;
n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); n = gcry_sexp_canon_len (sigval, 0, NULL, NULL);
if (!n) if (!n)
@ -385,8 +389,10 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
return rc; return rc;
} }
pkalgo = pk_algo_from_sexp (s_pkey);
rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), if (r_pkalgo)
*r_pkalgo = pkalgo;
rc = do_encode_md (md, mdalgo, pkalgo,
gcry_pk_get_nbits (s_pkey), s_pkey, &frame); gcry_pk_get_nbits (s_pkey), s_pkey, &frame);
if (rc) if (rc)
{ {

View File

@ -71,10 +71,10 @@ gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
p++; p++;
n = strtoul (p, &endp, 10); n = strtoul (p, &endp, 10);
p = endp; p = endp;
if (*p!=':') if (*p++ != ':')
es_fputs ("[Internal Error - invalid S-expression]", fp); es_fputs ("[Internal Error - invalid S-expression]", fp);
else else
es_write_hexstring (fp, p, strlen (p), 0, NULL); es_write_hexstring (fp, p, n, 0, NULL);
} }
} }
@ -936,7 +936,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
_("Please enter the passphrase to unlock the" _("Please enter the passphrase to unlock the"
" secret key for:\n" " secret key for:\n"
"\"%s\"\n" "\"%s\"\n"
"S/N %s, ID %08lX, created %s" ), "S/N %s, ID 0x%08lX, created %s" ),
subject? subject:"?", subject? subject:"?",
sn? sn: "?", sn? sn: "?",
gpgsm_get_short_fingerprint (cert), gpgsm_get_short_fingerprint (cert),

View File

@ -374,7 +374,8 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
} }
} }
if (!rc) if (!rc)
rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); rc = gpgsm_validate_chain (ctrl, cert, "", NULL,
0, NULL, 0, NULL);
if (!rc) if (!rc)
{ {
certlist_t cl = xtrycalloc (1, sizeof *cl); certlist_t cl = xtrycalloc (1, sizeof *cl);

View File

@ -195,6 +195,7 @@ enum cmd_and_opt_values {
oSetFilename, oSetFilename,
oSetPolicyURL, oSetPolicyURL,
oUseEmbeddedFilename, oUseEmbeddedFilename,
oValidationModel,
oComment, oComment,
oDefaultComment, oDefaultComment,
oThrowKeyid, oThrowKeyid,
@ -302,6 +303,8 @@ static ARGPARSE_OPTS opts[] = {
{ oDisableOCSP, "disable-ocsp", 0, "@" }, { oDisableOCSP, "disable-ocsp", 0, "@" },
{ oEnableOCSP, "enable-ocsp", 0, N_("check validity using OCSP")}, { oEnableOCSP, "enable-ocsp", 0, N_("check validity using OCSP")},
{ oValidationModel, "validation-model", 2, "@"},
{ oIncludeCerts, "include-certs", 1, { oIncludeCerts, "include-certs", 1,
N_("|N|number of certificates to include") }, N_("|N|number of certificates to include") },
@ -423,7 +426,7 @@ static ARGPARSE_OPTS opts[] = {
{ oLCmessages, "lc-messages", 2, "@" }, { oLCmessages, "lc-messages", 2, "@" },
{ oDirmngrProgram, "dirmngr-program", 2 , "@" }, { oDirmngrProgram, "dirmngr-program", 2 , "@" },
{ oProtectToolProgram, "protect-tool-program", 2 , "@" }, { oProtectToolProgram, "protect-tool-program", 2 , "@" },
{ oFakedSystemTime, "faked-system-time", 4, "@" }, /* (epoch time) */ { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
{ oNoBatch, "no-batch", 0, "@" }, { oNoBatch, "no-batch", 0, "@" },
@ -472,6 +475,8 @@ static int allow_special_filenames;
/* Default value for include-certs. */ /* Default value for include-certs. */
static int default_include_certs = 1; /* Only include the signer's cert. */ static int default_include_certs = 1; /* Only include the signer's cert. */
/* Whether the chain mode shall be used for validation. */
static int default_validation_model;
static char *build_list (const char *text, static char *build_list (const char *text,
@ -700,6 +705,17 @@ do_add_recipient (ctrl_t ctrl, const char *name,
} }
static void
parse_validation_model (const char *model)
{
int i = gpgsm_parse_validation_model (model);
if (i == -1)
log_error (_("unknown validation model `%s'\n"), model);
else
default_validation_model = i;
}
int int
main ( int argc, char **argv) main ( int argc, char **argv)
{ {
@ -772,9 +788,6 @@ main ( int argc, char **argv)
opt.def_cipher_algoid = "3DES"; /*des-EDE3-CBC*/ opt.def_cipher_algoid = "3DES"; /*des-EDE3-CBC*/
opt.homedir = default_homedir (); opt.homedir = default_homedir ();
#ifdef HAVE_W32_SYSTEM
opt.no_crl_check = 1;
#endif
/* First check whether we have a config file on the commandline */ /* First check whether we have a config file on the commandline */
orig_argc = argc; orig_argc = argc;
@ -1095,7 +1108,12 @@ main ( int argc, char **argv)
break; break;
case oFakedSystemTime: case oFakedSystemTime:
gnupg_set_time ( (time_t)pargs.r.ret_ulong, 0); {
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; break;
case oNoDefKeyring: default_keyring = 0; break; case oNoDefKeyring: default_keyring = 0; break;
@ -1174,7 +1192,8 @@ main ( int argc, char **argv)
case oNoRandomSeedFile: use_random_seed = 0; break; case oNoRandomSeedFile: use_random_seed = 0; break;
case oEnableSpecialFilenames: allow_special_filenames =1; break; case oEnableSpecialFilenames: allow_special_filenames =1; break;
case oValidationModel: parse_validation_model (pargs.r.ret_str); break;
case aDummy: case aDummy:
break; break;
@ -1201,7 +1220,11 @@ main ( int argc, char **argv)
if (log_get_errorcount(0)) if (log_get_errorcount(0))
gpgsm_exit(2); gpgsm_exit(2);
/* Now that we have the optiosn parsed we need to update the default
control structure. */
gpgsm_init_default_ctrl (&ctrl);
if (nogreeting) if (nogreeting)
greeting = 0; greeting = 0;
@ -1715,9 +1738,21 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl)
{ {
ctrl->include_certs = default_include_certs; ctrl->include_certs = default_include_certs;
ctrl->use_ocsp = opt.enable_ocsp; ctrl->use_ocsp = opt.enable_ocsp;
ctrl->validation_model = default_validation_model;
} }
int
gpgsm_parse_validation_model (const char *model)
{
if (!ascii_strcasecmp (model, "shell") )
return 0;
else if ( !ascii_strcasecmp (model, "chain") )
return 1;
else
return -1;
}
/* Check whether the filename has the form "-&nnnn", where n is a /* Check whether the filename has the form "-&nnnn", where n is a
non-zero number. Returns this number or -1 if it is not the case. */ non-zero number. Returns this number or -1 if it is not the case. */

View File

@ -164,6 +164,7 @@ struct server_control_s
certificates up the chain (0 = none, 1 = only certificates up the chain (0 = none, 1 = only
signer) */ signer) */
int use_ocsp; /* Set to true if OCSP should be used. */ int use_ocsp; /* Set to true if OCSP should be used. */
int validation_model; /* Set to 1 for the chain model. */
}; };
@ -185,8 +186,10 @@ typedef struct certlist_s *certlist_t;
/* A structure carrying information about trusted root certificates. */ /* A structure carrying information about trusted root certificates. */
struct rootca_flags_s struct rootca_flags_s
{ {
unsigned int valid:1; /* The rest of the structure has valid
information. */
unsigned int relax:1; /* Relax checking of root certificates. */ unsigned int relax:1; /* Relax checking of root certificates. */
unsigned int chain_model:1; /* Root requires the use of the chain model. */
}; };
@ -194,6 +197,7 @@ struct rootca_flags_s
/*-- gpgsm.c --*/ /*-- gpgsm.c --*/
void gpgsm_exit (int rc); void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl); void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/ /*-- server.c --*/
void gpgsm_server (certlist_t default_recplist); void gpgsm_server (certlist_t default_recplist);
@ -253,7 +257,7 @@ char *gpgsm_format_keydesc (ksba_cert_t cert);
/*-- certcheck.c --*/ /*-- certcheck.c --*/
int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert);
int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval,
gcry_md_hd_t md, int hash_algo); gcry_md_hd_t md, int hash_algo, int *r_pkalgo);
/* fixme: move create functions to another file */ /* fixme: move create functions to another file */
int gpgsm_create_cms_signature (ctrl_t ctrl, int gpgsm_create_cms_signature (ctrl_t ctrl,
ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo,
@ -261,12 +265,19 @@ int gpgsm_create_cms_signature (ctrl_t ctrl,
/*-- certchain.c --*/ /*-- certchain.c --*/
/* Flags used with gpgsm_validate_chain. */
#define VALIDATE_FLAG_NO_DIRMNGR 1
#define VALIDATE_FLAG_CHAIN_MODEL 2
int gpgsm_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next); int gpgsm_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next);
int gpgsm_is_root_cert (ksba_cert_t cert); int gpgsm_is_root_cert (ksba_cert_t cert);
int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert,
ksba_isotime_t checktime,
ksba_isotime_t r_exptime, ksba_isotime_t r_exptime,
int listmode, estream_t listfp, int listmode, estream_t listfp,
unsigned int flags); unsigned int flags, unsigned int *retflags);
int gpgsm_basic_cert_check (ksba_cert_t cert); int gpgsm_basic_cert_check (ksba_cert_t cert);
/*-- certlist.c --*/ /*-- certlist.c --*/

View File

@ -173,7 +173,7 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
*/ */
rc = gpgsm_basic_cert_check (cert); rc = gpgsm_basic_cert_check (cert);
if (!rc && ctrl->with_validation) if (!rc && ctrl->with_validation)
rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
if (!rc || (!ctrl->with_validation if (!rc || (!ctrl->with_validation
&& gpg_err_code (rc) == GPG_ERR_MISSING_CERT) ) && gpg_err_code (rc) == GPG_ERR_MISSING_CERT) )
{ {

View File

@ -176,6 +176,9 @@ static struct
/* GnuPG extensions */ /* GnuPG extensions */
{ "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" }, { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
/* Extensions used by the Bundesnetzagentur. */
{ "1.3.6.1.4.1.8301.3.5", "validityModel" },
{ NULL } { NULL }
}; };
@ -345,9 +348,10 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
const char *chain_id; const char *chain_id;
char *chain_id_buffer = NULL; char *chain_id_buffer = NULL;
int is_root = 0; int is_root = 0;
char *kludge_uid;
if (ctrl->with_validation) if (ctrl->with_validation)
valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL, 0); valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL);
else else
valerr = 0; valerr = 0;
@ -484,8 +488,15 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
print_key_data (cert, fp); print_key_data (cert, fp);
} }
kludge_uid = NULL;
for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
{ {
/* In the case that the same email address is in the subecj DN
as weel as in an alternate subject name we avoid printing it
a second time. */
if (kludge_uid && !strcmp (kludge_uid, p))
continue;
es_fprintf (fp, "uid:%s::::::::", truststring); es_fprintf (fp, "uid:%s::::::::", truststring);
es_write_sanitized (fp, p, strlen (p), ":", NULL); es_write_sanitized (fp, p, strlen (p), ":", NULL);
es_putc (':', fp); es_putc (':', fp);
@ -497,19 +508,20 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
the keydb. But as long as we don't have a way to pass the keydb. But as long as we don't have a way to pass
the meta data back, we just check it the same way as the the meta data back, we just check it the same way as the
code used to create the keybox meta data does */ code used to create the keybox meta data does */
char *pp = email_kludge (p); kludge_uid = email_kludge (p);
if (pp) if (kludge_uid)
{ {
es_fprintf (fp, "uid:%s::::::::", truststring); es_fprintf (fp, "uid:%s::::::::", truststring);
es_write_sanitized (fp, pp, strlen (pp), ":", NULL); es_write_sanitized (fp, kludge_uid, strlen (kludge_uid),
":", NULL);
es_putc (':', fp); es_putc (':', fp);
es_putc (':', fp); es_putc (':', fp);
es_putc ('\n', fp); es_putc ('\n', fp);
xfree (pp);
} }
} }
xfree (p); xfree (p);
} }
xfree (kludge_uid);
} }
@ -570,8 +582,11 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
ksba_name_t name, name2; ksba_name_t name, name2;
unsigned int reason; unsigned int reason;
es_fprintf (fp, " ID: 0x%08lX\n",
gpgsm_get_short_fingerprint (cert));
sexp = ksba_cert_get_serial (cert); sexp = ksba_cert_get_serial (cert);
es_fputs ("Serial number: ", fp); es_fputs (" S/N: ", fp);
gpgsm_print_serial (fp, sexp); gpgsm_print_serial (fp, sexp);
ksba_free (sexp); ksba_free (sexp);
es_putc ('\n', fp); es_putc ('\n', fp);
@ -887,7 +902,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
if (with_validation) if (with_validation)
{ {
err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
if (!err) if (!err)
es_fprintf (fp, " [certificate is good]\n"); es_fprintf (fp, " [certificate is good]\n");
else else
@ -924,8 +939,11 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
unsigned int kusage; unsigned int kusage;
char *string, *p, *pend; char *string, *p, *pend;
es_fprintf (fp, " ID: 0x%08lX\n",
gpgsm_get_short_fingerprint (cert));
sexp = ksba_cert_get_serial (cert); sexp = ksba_cert_get_serial (cert);
es_fputs ("Serial number: ", fp); es_fputs (" S/N: ", fp);
gpgsm_print_serial (fp, sexp); gpgsm_print_serial (fp, sexp);
ksba_free (sexp); ksba_free (sexp);
es_putc ('\n', fp); es_putc ('\n', fp);
@ -1088,7 +1106,7 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
size_t buflen; size_t buflen;
char buffer[1]; char buffer[1];
err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL);
tmperr = ksba_cert_get_user_data (cert, "is_qualified", tmperr = ksba_cert_get_user_data (cert, "is_qualified",
&buffer, sizeof (buffer), &buflen); &buffer, sizeof (buffer), &buflen);
if (!tmperr && buflen) if (!tmperr && buflen)

View File

@ -244,6 +244,14 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
int i = *value? atoi (value) : 0; int i = *value? atoi (value) : 0;
ctrl->with_validation = i; ctrl->with_validation = i;
} }
else if (!strcmp (key, "validation-model"))
{
int i = gpgsm_parse_validation_model (value);
if ( i >= 0 && i <= 1 )
ctrl->validation_model = i;
else
return gpg_error (GPG_ERR_ASS_PARAMETER);
}
else if (!strcmp (key, "with-key-data")) else if (!strcmp (key, "with-key-data"))
{ {
opt.with_key_data = 1; opt.with_key_data = 1;

View File

@ -380,7 +380,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
check that the signer's certificate is usable and valid. */ check that the signer's certificate is usable and valid. */
rc = gpgsm_cert_use_sign_p (cert); rc = gpgsm_cert_use_sign_p (cert);
if (!rc) if (!rc)
rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
if (rc) if (rc)
goto leave; goto leave;

View File

@ -1,5 +1,5 @@
/* verify.c - Verify a messages signature /* verify.c - Verify a messages signature
* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. * Copyright (C) 2001, 2002, 2003, 2007 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -246,6 +246,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
char *msgdigest = NULL; char *msgdigest = NULL;
size_t msgdigestlen; size_t msgdigestlen;
char *ctattr; char *ctattr;
int info_pkalgo;
unsigned int verifyflags;
rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA
@ -388,7 +390,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
gpgsm_dump_time (sigtime); gpgsm_dump_time (sigtime);
else else
log_printf (_("[date not given]")); log_printf (_("[date not given]"));
log_printf (_(" using certificate ID %08lX\n"), log_printf (_(" using certificate ID 0x%08lX\n"),
gpgsm_get_short_fingerprint (cert)); gpgsm_get_short_fingerprint (cert));
@ -432,12 +434,14 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
gcry_md_close (md); gcry_md_close (md);
goto next_signer; goto next_signer;
} }
rc = gpgsm_check_cms_signature (cert, sigval, md, algo); rc = gpgsm_check_cms_signature (cert, sigval, md, algo,
&info_pkalgo);
gcry_md_close (md); gcry_md_close (md);
} }
else else
{ {
rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo); rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo,
&info_pkalgo);
} }
if (rc) if (rc)
@ -460,7 +464,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
if (DBG_X509) if (DBG_X509)
log_debug ("signature okay - checking certs\n"); log_debug ("signature okay - checking certs\n");
rc = gpgsm_validate_chain (ctrl, cert, keyexptime, 0, NULL, 0); rc = gpgsm_validate_chain (ctrl, cert,
*sigtime? sigtime : "19700101T000000",
keyexptime, 0,
NULL, 0, &verifyflags);
{ {
char *fpr, *buf, *tstr; char *fpr, *buf, *tstr;
@ -477,10 +484,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
tstr = strtimestamp_r (sigtime); tstr = strtimestamp_r (sigtime);
buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120); buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr,
sprintf (buf, "%s %s %s %s", fpr, tstr, *sigtime? sigtime : "0",
*sigtime? sigtime : "0", *keyexptime? keyexptime : "0",
*keyexptime? keyexptime : "0" ); info_pkalgo, algo);
xfree (tstr); xfree (tstr);
xfree (fpr); xfree (fpr);
gpgsm_status (ctrl, STATUS_VALIDSIG, buf); gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
@ -512,7 +519,32 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
ksba_free (p); ksba_free (p);
} }
gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL); /* Print a note if this is a qualified signature. */
{
size_t qualbuflen;
char qualbuffer[1];
rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer,
sizeof (qualbuffer), &qualbuflen);
if (!rc && qualbuflen)
{
if (*qualbuffer)
{
log_info (_("This is a qualified signature\n"));
if (!opt.qualsig_approval)
log_info
(_("Note, that this software is not officially approved "
"to create or verify such signatures.\n"));
}
}
else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("get_user_data(is_qualified) failed: %s\n",
gpg_strerror (rc));
}
gpgsm_status (ctrl, STATUS_TRUST_FULLY,
(verifyflags & VALIDATE_FLAG_CHAIN_MODEL)?
"0 chain": "0 shell");
next_signer: next_signer: