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

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>
* 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
* 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.
*
@ -42,6 +42,7 @@ struct trustitem_s
int for_smime:1; /* Set by '*' or 'S' as first flag. */
int relax:1; /* Relax checking of root certificate
constraints. */
int cm:1; /* Use chain model for validation. */
} flags;
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))
ti->flags.relax = 1;
else if (n == 2 && !memcmp (p, "cm", 2))
ti->flags.cm = 1;
else
log_error ("flag `%.*s' in `%s', line %d ignored\n",
n, p, fname, lnr);
@ -396,6 +399,14 @@ agent_istrusted (ctrl_t ctrl, const char *fpr)
if (err)
return err;
}
else if (ti->flags.cm)
{
err = agent_write_status (ctrl,
"TRUSTLISTFLAG", "cm",
NULL);
if (err)
return err;
}
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>
* gc-opt-flags.h: New.
@ -29,7 +36,7 @@
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.
2007-06-26 Werner Koch <wk@g10code.com>

View File

@ -36,6 +36,7 @@ common_sources = \
gc-opt-flags.h \
keyserver.h \
sexp-parse.h \
tlv.c tlv.h \
init.c init.h \
sexputil.c \
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
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. */
void
gnupg_set_time (time_t newtime, int freeze)
@ -165,7 +165,7 @@ scan_isodatestr( const char *string )
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
colon or a comma. Returns (time_t)(-1) for an invalid string. */
time_t

View File

@ -29,7 +29,7 @@
#define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/
#define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/
typedef int gpg_error_t;
#define gpg_error(n) (n)
#define gpg_make_err(x,n) (n)
#else
#include <gpg-error.h>
#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
on success. */
gpg_error_t
parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr)
_parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr,
gpg_err_source_t errsource)
{
int c;
unsigned long tag;
@ -167,7 +168,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
/* Get the tag. */
if (!length)
return gpg_error (GPG_ERR_EOF);
return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
*r_class = (c & 0xc0) >> 6;
@ -181,7 +182,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
{
tag <<= 7;
if (!length)
return gpg_error (GPG_ERR_EOF);
return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
tag |= c & 0x7f;
@ -192,7 +193,7 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
/* Get the length. */
if (!length)
return gpg_error (GPG_ERR_EOF);
return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
if ( !(c & 0x80) )
@ -200,20 +201,20 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
else if (c == 0x80)
*r_ndef = 1;
else if (c == 0xff)
return gpg_error (GPG_ERR_BAD_BER);
return gpg_err_make (errsource, GPG_ERR_BAD_BER);
else
{
unsigned long len = 0;
int count = c & 0x7f;
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--)
{
len <<= 8;
if (!length)
return gpg_error (GPG_ERR_EOF);
return gpg_err_make (errsource, GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
len |= c & 0xff;
}
@ -254,8 +255,9 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
handle_error ();
*/
gpg_error_t
parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen)
_parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen,
gpg_err_source_t errsource)
{
const unsigned char *s;
size_t n, vlen;
@ -265,7 +267,7 @@ parse_sexp (unsigned char const **buf, size_t *buflen,
*tok = NULL;
*toklen = 0;
if (!n)
return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
return *depth ? gpg_err_make (errsource, GPG_ERR_INV_SEXP) : 0;
if (*s == '(')
{
s++; n--;
@ -277,7 +279,7 @@ parse_sexp (unsigned char const **buf, size_t *buflen,
if (*s == ')')
{
if (!*depth)
return gpg_error (GPG_ERR_INV_SEXP);
return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
*toklen = 1;
s++; n--;
(*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--)
vlen = vlen*10 + (*s - '0');
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP);
return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
s++; n--;
if (vlen > n)
return gpg_error (GPG_ERR_INV_SEXP);
return gpg_err_make (errsource, GPG_ERR_INV_SEXP);
*tok = s;
*toklen = 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
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
int *r_ndef, size_t *r_length, size_t *r_nhdr);
gpg_error_t _parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
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
@ -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
of the S-expression it is advisable to check DEPTH after a
successful return. */
gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
int *depth, unsigned char const **tok, size_t *toklen);
gpg_error_t _parse_sexp (unsigned char const **buf, size_t *buflen,
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 */
/* 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. */
static inline void
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_SRCDIR(sm/gpgsm.c)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_TARGET()
AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
AC_CANONICAL_HOST
AB_INIT
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>
* 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.
VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp>
<expire-timestamp> [ <sig-version> <reserved> <pubkey-algo>
<hash-algo> <sig-class> <primary-key-fpr> ]
<expire-timestamp> <sig-version> <reserved> <pubkey-algo>
<hash-algo> <sig-class> [ <primary-key-fpr> ]
The signature with the keyid is good. This is the same as
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
again for this purpose.
The optional parameters are used for OpenPGP and are not
available for CMS signatures.
The primary-key-fpr parameter is used for OpenPGP and not
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
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_NEVER <error token>
TRUST_MARGINAL
TRUST_FULLY
TRUST_ULTIMATE
For good signatures one of these status lines are emitted
to indicate how trustworthy the signature is. The error token
values are currently only emitted by gpgsm.
TRUST_NEVER <error token>
TRUST_MARGINAL [0 [<validation_model>]]
TRUST_FULLY [0 [<validation_model>]]
TRUST_ULTIMATE [0 [<validation_model>]]
For good signatures one of these status lines are emitted to
indicate how trustworthy the signature is. The error token
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_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
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
@c *******************************************
@ -550,7 +561,8 @@ encryption. For convenience the strings @code{3DES}, @code{AES} and
@opindex faked-system-time
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
1970.
1970. Alternativly @var{epoch} may be given as a full ISO time string
(e.g. "20070924T154812").
@item --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.
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.
@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>
* 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)");
}
if( (s=strusage(19)) ) { /* bug reports to ... */
char *s2;
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);
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>
* scdaemon.c: Include gc-opt-flags.h and remove their definition

View File

@ -38,7 +38,6 @@ scdaemon_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
tlv.c tlv.h \
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 \
# ccid-driver.c ccid-driver.h \
# iso7816.c iso7816.h \
# tlv.c tlv.h \
# atr.c atr.h \
# 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>
* 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]))
flags->relax = 1;
else if (!strncmp (line, "cm", 2) && (line[2] == ' ' || !line[2]))
flags->chain_model = 1;
}
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,
istrusted_status_cb, rootca_flags);
if (!rc)
rootca_flags->valid = 1;
return rc;
}

View File

@ -394,8 +394,10 @@ isvalid_status_cb (void *opaque, const char *line)
GPG_ERR_NO_CRL_KNOWN
GPG_ERR_CRL_TOO_OLD
With USE_OCSP set to true, the dirmngr is asked to do an OCSP
request first.
Values for USE_OCSP:
0 = Do CRL check.
1 = Do an OCSP check.
2 = Do an OCSP check using only the default responder.
*/
int
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
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
one connection per process only. */
@ -456,7 +459,9 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
NULL, NULL, NULL, NULL, NULL, NULL);
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;
xfree (certid);
@ -504,9 +509,10 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl,
rc = gpg_error (GPG_ERR_INV_CRL);
else
{
/* Note, the flag = 1: This avoids checking this
certificate over and over again. */
rc = gpgsm_validate_chain (ctrl, rspcert, NULL, 0, NULL, 1);
/* Note the no_dirmngr flag: This avoids checking
this certificate over and over again. */
rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL,
VALIDATE_FLAG_NO_DIRMNGR, NULL);
if (rc)
{
log_error ("invalid certificate used for CRL/OCSP: %s\n",

View File

@ -1,6 +1,6 @@
/* certchain.c - certificate chain validation
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
* 2006 Free Software Foundation, Inc.
* 2006, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -37,6 +37,7 @@
#include "keydb.h"
#include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
#include "i18n.h"
#include "tlv.h"
/* 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
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.32", /* certificatePolicies */
"2.5.29.37", /* extendedKeyUsage - handled by certlist.c */
"1.3.6.1.4.1.8301.3.5", /* validityModel - handled here. */
NULL
};
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. */
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,
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,
subject_cert, issuer_cert, ctrl->use_ocsp);
if (err)
if (opt.no_crl_check && !ctrl->use_ocsp)
return 0;
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
have used OCSP. */
case GPG_ERR_CERT_REVOKED:
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)
gpgsm_cert_log_name (NULL, subject_cert);
switch (gpg_err_code (err))
{
case GPG_ERR_CERT_REVOKED:
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_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;
}
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;
}
/* 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
in R_EXPTIME. With LISTMODE set to 1 a special listmode is
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
gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
int listmode, estream_t fp, unsigned int flags)
static int
do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg,
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 *subject = NULL;
KEYDB_HANDLE kh = NULL;
ksba_cert_t subject_cert = NULL, issuer_cert = NULL;
ksba_isotime_t current_time;
ksba_isotime_t check_time;
ksba_isotime_t exptime;
int any_expired = 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
from a qualified root certificate.
-1 = unknown, 0 = no, 1 = yes. */
int lm = listmode;
chain_item_t chain = NULL; /* A list of all certificates in the chain. */
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)
*r_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;
ksba_cert_ref (subject_cert);
maxdepth = 50;
depth = 0;
for (;;)
{
int is_root;
gpg_error_t istrusted_rc = -1;
struct rootca_flags_s rootca_flags;
/* 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)
{
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);
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));
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
associated with that specific root certificate. */
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. */
{
ksba_isotime_t not_before, not_after;
rc = ksba_cert_get_validity (subject_cert, 0, not_before);
if (!rc)
rc = ksba_cert_get_validity (subject_cert, 1, not_after);
if (rc)
{
do_list (1, lm, fp, _("certificate with invalid validity: %s"),
gpg_strerror (rc));
rc = gpg_error (GPG_ERR_BAD_CERT);
goto leave;
}
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, 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;
}
}
if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) )
rc = check_validity_period_cm (current_time, check_time, subject_cert,
exptime, listmode, listfp,
(depth && is_root)? -1: depth);
else
rc = check_validity_period (current_time, subject_cert,
exptime, listmode, listfp,
(depth && is_root)? -1: depth);
if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
{
any_expired = 1;
rc = 0;
}
else if (rc)
goto leave;
/* Assert that we understand all critical extensions. */
rc = unknown_criticals (subject_cert, listmode, fp);
rc = unknown_criticals (subject_cert, listmode, listfp);
if (rc)
goto leave;
/* Do a 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)
{
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 (!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
trusted for better diagnostics. */
do_list (1, lm, fp,
do_list (1, listmode, listfp,
_("self-signed certificate has a BAD signature"));
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);
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)
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)
{
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
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
will then be usable. We also check whether the agent
is at all enabled to allo marktrusted and don't call
it in this session again if it is not. */
will then be usable. */
if ( !any_expired
&& (!lm || !already_asked_marktrusted (subject_cert)))
{
static int no_more_questions; /* during this session. */
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);
}
&& (!listmode || !already_asked_marktrusted (subject_cert))
&& ask_marktrusted (ctrl, subject_cert, listmode) )
rc = 0;
}
else
{
@ -1019,12 +1271,14 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
goto leave;
/* 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
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,
&any_revoked, &any_no_crl,
&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;
break; /* Okay: a self-signed certicate is an end-point. */
}
} /* End is_root. */
/* Take care that the chain does not get too long. */
depth++;
if (depth > maxdepth)
if ((depth+1) > 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);
goto leave;
}
@ -1050,8 +1304,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
{
if (rc == -1)
{
do_list (0, lm, fp, _("issuer certificate not found"));
if (!lm)
do_list (0, listmode, listfp, _("issuer certificate not found"));
if (!listmode)
{
log_info ("issuer certificate: #/");
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);
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)
{
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
{
do_list (0, lm, fp, _("found another possible matching "
"CA certificate - trying again"));
do_list (0, listmode, listfp,
_("found another possible matching "
"CA certificate - trying again"));
ksba_cert_release (issuer_cert);
issuer_cert = tmp_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;
}
is_root = 0;
is_root = gpgsm_is_root_cert (issuer_cert);
istrusted_rc = -1;
/* Check that a CA is allowed to issue certificates. */
{
int chainlen;
rc = allowed_ca (issuer_cert, &chainlen, listmode, fp);
rc = allowed_ca (issuer_cert, &chainlen, listmode, listfp);
if (rc)
{
/* 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
overriding an error in the way is justified for
performance reasons. */
if (gpgsm_is_root_cert (issuer_cert))
if (is_root)
{
is_root = 1;
istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert,
&rootca_flags);
if (!istrusted_rc && rootca_flags.relax)
rootca_flags);
if (!istrusted_rc && rootca_flags->relax)
{
/* Ignore the error due to the relax flag. */
rc = 0;
@ -1161,9 +1416,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
}
if (rc)
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)"),
chainlen);
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
this test is done a second time later. This should eventually
be fixed. */
if ((flags & 1))
if ((flags & VALIDATE_FLAG_NO_DIRMNGR))
rc = 0;
else if (is_root && (opt.no_trusted_cert_crl_check
|| (!istrusted_rc && rootca_flags.relax)))
;
|| (!istrusted_rc && rootca_flags->relax)))
rc = 0;
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,
&any_revoked, &any_no_crl, &any_crl_too_old);
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)
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. */
keydb_search_reset (kh);
ksba_cert_release (subject_cert);
subject_cert = issuer_cert;
issuer_cert = NULL;
depth++;
} /* End chain traversal. */
if (!listmode)
@ -1238,7 +1511,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
leave:
/* 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
transient. */
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
constraints. We assume that the issuers certificate is already in
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;
}
if (opt.verbose)
log_info ("certificate is good\n");
log_info (_("certificate is good\n"));
}
leave:

View File

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

View File

@ -71,10 +71,10 @@ gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p!=':')
if (*p++ != ':')
es_fputs ("[Internal Error - invalid S-expression]", fp);
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"
" secret key for:\n"
"\"%s\"\n"
"S/N %s, ID %08lX, created %s" ),
"S/N %s, ID 0x%08lX, created %s" ),
subject? subject:"?",
sn? sn: "?",
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)
rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0);
rc = gpgsm_validate_chain (ctrl, cert, "", NULL,
0, NULL, 0, NULL);
if (!rc)
{
certlist_t cl = xtrycalloc (1, sizeof *cl);

View File

@ -195,6 +195,7 @@ enum cmd_and_opt_values {
oSetFilename,
oSetPolicyURL,
oUseEmbeddedFilename,
oValidationModel,
oComment,
oDefaultComment,
oThrowKeyid,
@ -302,6 +303,8 @@ static ARGPARSE_OPTS opts[] = {
{ oDisableOCSP, "disable-ocsp", 0, "@" },
{ oEnableOCSP, "enable-ocsp", 0, N_("check validity using OCSP")},
{ oValidationModel, "validation-model", 2, "@"},
{ oIncludeCerts, "include-certs", 1,
N_("|N|number of certificates to include") },
@ -423,7 +426,7 @@ static ARGPARSE_OPTS opts[] = {
{ oLCmessages, "lc-messages", 2, "@" },
{ oDirmngrProgram, "dirmngr-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, "@" },
@ -472,6 +475,8 @@ static int allow_special_filenames;
/* Default value for include-certs. */
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,
@ -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
main ( int argc, char **argv)
{
@ -772,9 +788,6 @@ main ( int argc, char **argv)
opt.def_cipher_algoid = "3DES"; /*des-EDE3-CBC*/
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 */
orig_argc = argc;
@ -1095,7 +1108,12 @@ main ( int argc, char **argv)
break;
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;
case oNoDefKeyring: default_keyring = 0; break;
@ -1174,7 +1192,8 @@ main ( int argc, char **argv)
case oNoRandomSeedFile: use_random_seed = 0; break;
case oEnableSpecialFilenames: allow_special_filenames =1; break;
case oValidationModel: parse_validation_model (pargs.r.ret_str); break;
case aDummy:
break;
@ -1201,7 +1220,11 @@ main ( int argc, char **argv)
if (log_get_errorcount(0))
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)
greeting = 0;
@ -1715,9 +1738,21 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl)
{
ctrl->include_certs = default_include_certs;
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
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
signer) */
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. */
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 chain_model:1; /* Root requires the use of the chain model. */
};
@ -194,6 +197,7 @@ struct rootca_flags_s
/*-- gpgsm.c --*/
void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/
void gpgsm_server (certlist_t default_recplist);
@ -253,7 +257,7 @@ char *gpgsm_format_keydesc (ksba_cert_t cert);
/*-- certcheck.c --*/
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,
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 */
int gpgsm_create_cms_signature (ctrl_t ctrl,
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 --*/
/* 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_is_root_cert (ksba_cert_t cert);
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 flags, unsigned int *retflags);
int gpgsm_basic_cert_check (ksba_cert_t cert);
/*-- certlist.c --*/

View File

@ -173,7 +173,7 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
*/
rc = gpgsm_basic_cert_check (cert);
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
&& gpg_err_code (rc) == GPG_ERR_MISSING_CERT) )
{

View File

@ -176,6 +176,9 @@ static struct
/* GnuPG extensions */
{ "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 }
};
@ -345,9 +348,10 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
const char *chain_id;
char *chain_id_buffer = NULL;
int is_root = 0;
char *kludge_uid;
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
valerr = 0;
@ -484,8 +488,15 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
print_key_data (cert, fp);
}
kludge_uid = NULL;
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_write_sanitized (fp, p, strlen (p), ":", NULL);
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 meta data back, we just check it the same way as the
code used to create the keybox meta data does */
char *pp = email_kludge (p);
if (pp)
kludge_uid = email_kludge (p);
if (kludge_uid)
{
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 ('\n', fp);
xfree (pp);
}
}
xfree (p);
}
xfree (kludge_uid);
}
@ -570,8 +582,11 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
ksba_name_t name, name2;
unsigned int reason;
es_fprintf (fp, " ID: 0x%08lX\n",
gpgsm_get_short_fingerprint (cert));
sexp = ksba_cert_get_serial (cert);
es_fputs ("Serial number: ", fp);
es_fputs (" S/N: ", fp);
gpgsm_print_serial (fp, sexp);
ksba_free (sexp);
es_putc ('\n', fp);
@ -887,7 +902,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
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)
es_fprintf (fp, " [certificate is good]\n");
else
@ -924,8 +939,11 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
unsigned int kusage;
char *string, *p, *pend;
es_fprintf (fp, " ID: 0x%08lX\n",
gpgsm_get_short_fingerprint (cert));
sexp = ksba_cert_get_serial (cert);
es_fputs ("Serial number: ", fp);
es_fputs (" S/N: ", fp);
gpgsm_print_serial (fp, sexp);
ksba_free (sexp);
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;
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",
&buffer, sizeof (buffer), &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;
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"))
{
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. */
rc = gpgsm_cert_use_sign_p (cert);
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)
goto leave;

View File

@ -1,5 +1,5 @@
/* 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.
*
@ -246,6 +246,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
char *msgdigest = NULL;
size_t msgdigestlen;
char *ctattr;
int info_pkalgo;
unsigned int verifyflags;
rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
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);
else
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));
@ -432,12 +434,14 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
gcry_md_close (md);
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);
}
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)
@ -460,7 +464,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
if (DBG_X509)
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;
@ -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);
tstr = strtimestamp_r (sigtime);
buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120);
sprintf (buf, "%s %s %s %s", fpr, tstr,
*sigtime? sigtime : "0",
*keyexptime? keyexptime : "0" );
buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr,
*sigtime? sigtime : "0",
*keyexptime? keyexptime : "0",
info_pkalgo, algo);
xfree (tstr);
xfree (fpr);
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);
}
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: