summaryrefslogtreecommitdiffstats
path: root/sm/certchain.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2007-08-10 18:52:05 +0200
committerWerner Koch <wk@gnupg.org>2007-08-10 18:52:05 +0200
commit74d344a521c8a7a294b8da2cf2647e112fd5b310 (patch)
treec2cc77b642ad52a26ea4d99a05f82f725f536d11 /sm/certchain.c
parentFactored common gpgconf constants out (diff)
downloadgnupg2-74d344a521c8a7a294b8da2cf2647e112fd5b310.tar.xz
gnupg2-74d344a521c8a7a294b8da2cf2647e112fd5b310.zip
Implemented the chain model for X.509 validation.
Diffstat (limited to 'sm/certchain.c')
-rw-r--r--sm/certchain.c686
1 files changed, 507 insertions, 179 deletions
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: