aboutsummaryrefslogtreecommitdiffstats
path: root/sm
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2007-08-10 16:52:05 +0000
committerWerner Koch <[email protected]>2007-08-10 16:52:05 +0000
commit74d344a521c8a7a294b8da2cf2647e112fd5b310 (patch)
treec2cc77b642ad52a26ea4d99a05f82f725f536d11 /sm
parentFactored common gpgconf constants out (diff)
downloadgnupg-74d344a521c8a7a294b8da2cf2647e112fd5b310.tar.gz
gnupg-74d344a521c8a7a294b8da2cf2647e112fd5b310.zip
Implemented the chain model for X.509 validation.
Diffstat (limited to 'sm')
-rw-r--r--sm/ChangeLog62
-rw-r--r--sm/call-agent.c4
-rw-r--r--sm/call-dirmngr.c20
-rw-r--r--sm/certchain.c686
-rw-r--r--sm/certcheck.c12
-rw-r--r--sm/certdump.c6
-rw-r--r--sm/certlist.c3
-rw-r--r--sm/gpgsm.c49
-rw-r--r--sm/gpgsm.h17
-rw-r--r--sm/import.c2
-rw-r--r--sm/keylist.c36
-rw-r--r--sm/server.c8
-rw-r--r--sm/sign.c2
-rw-r--r--sm/verify.c52
14 files changed, 735 insertions, 224 deletions
diff --git a/sm/ChangeLog b/sm/ChangeLog
index 910056a74..5e51dae7a 100644
--- a/sm/ChangeLog
+++ b/sm/ChangeLog
@@ -1,3 +1,65 @@
+2007-08-09 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * 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 <[email protected]>
+
+ * gpgsm.c (main): Allow a string for --faked-system-time.
+
+2007-08-06 Werner Koch <[email protected]>
+
+ 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 <[email protected]>
+
+ * 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 <[email protected]>
* gpgsm.c (main): Factored GC_OPT_FLAGS out to gc-opt-flags.h.
diff --git a/sm/call-agent.c b/sm/call-agent.c
index da952dc09..88447bd63 100644
--- a/sm/call-agent.c
+++ b/sm/call-agent.c
@@ -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;
}
diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c
index 9273ffb21..3beb57799 100644
--- a/sm/call-dirmngr.c
+++ b/sm/call-dirmngr.c
@@ -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",
diff --git a/sm/certchain.c b/sm/certchain.c
index 7ce7e6889..fc6e28de7 100644
--- a/sm/certchain.c
+++ b/sm/certchain.c
@@ -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
- Defined flag bits: 0 - do not do any dirmngr isvalid checks.
+ 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:
diff --git a/sm/certcheck.c b/sm/certcheck.c
index 5375408ac..7f26f80dc 100644
--- a/sm/certcheck.c
+++ b/sm/certcheck.c
@@ -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)
{
diff --git a/sm/certdump.c b/sm/certdump.c
index b8f35286a..c8b9958ae 100644
--- a/sm/certdump.c
+++ b/sm/certdump.c
@@ -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),
diff --git a/sm/certlist.c b/sm/certlist.c
index e7112f39f..5c08e317c 100644
--- a/sm/certlist.c
+++ b/sm/certlist.c
@@ -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);
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 3a1c6d811..69f1532fd 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -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. */
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 5a41a3d5d..108c4fe43 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -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 --*/
diff --git a/sm/import.c b/sm/import.c
index deebccfbc..8d42c9272 100644
--- a/sm/import.c
+++ b/sm/import.c
@@ -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) )
{
diff --git a/sm/keylist.c b/sm/keylist.c
index 9ec7cd137..604f9d986 100644
--- a/sm/keylist.c
+++ b/sm/keylist.c
@@ -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)
diff --git a/sm/server.c b/sm/server.c
index 9896728aa..34326c5eb 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -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;
diff --git a/sm/sign.c b/sm/sign.c
index a8908cf62..d617e7239 100644
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -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;
diff --git a/sm/verify.c b/sm/verify.c
index e44568872..4e92c11d8 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -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: