mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
Implemented the chain model for X.509 validation.
This commit is contained in:
parent
ebd36b6344
commit
74d344a521
60 changed files with 16887 additions and 12516 deletions
686
sm/certchain.c
686
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: 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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue