Commit d0fff69d authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Initial indirect CRL support.

parent 8c9bd893
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -4,6 +4,14 @@

 Changes between 0.9.8i and 0.9.9  [xx XXX xxxx]

  *) Initial indirect CRL support. Currently only supported in the CRLs
     passed directly and not via lookup. Process certificate issuer
     CRL entry extension and lookup CRL entries by bother issuer name
     and serial number. Check and proces CRL issuer entry in IDP extension.

     This work was sponsored by Google.
     [Steve Henson]

  *) Add support for distinct certificate and CRL paths. The CRL issuer
     certificate is validated separately in this case. Only enabled if
     an extended CRL support flag is set: this flag will enable additional
+2 −1
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ struct x509_crl_method_st
	int flags;
	int (*crl_init)(X509_CRL *crl);
	int (*crl_free)(X509_CRL *crl);
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret, ASN1_INTEGER *ser);
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
				ASN1_INTEGER *ser, X509_NAME *issuer);
	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk);
	};
+130 −12
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ ASN1_SEQUENCE(X509_REVOKED) = {

static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r);
static int def_crl_lookup(X509_CRL *crl,
		X509_REVOKED **ret, ASN1_INTEGER *serial);
		X509_REVOKED **ret, ASN1_INTEGER *serial, X509_NAME *issuer);

static X509_CRL_METHOD int_crl_meth =
	{
@@ -119,6 +119,72 @@ ASN1_SEQUENCE_enc(X509_CRL_INFO, enc, crl_inf_cb) = {
	ASN1_EXP_SEQUENCE_OF_OPT(X509_CRL_INFO, extensions, X509_EXTENSION, 0)
} ASN1_SEQUENCE_END_enc(X509_CRL_INFO, X509_CRL_INFO)

/* Set CRL entry issuer according to CRL certificate issuer extension.
 * Check for unhandled critical CRL entry extensions.
 */

static int crl_set_issuers(X509_CRL *crl)
	{

	int i, j;
	GENERAL_NAMES *gens, *gtmp;
	STACK_OF(X509_REVOKED) *revoked;

	revoked = X509_CRL_get_REVOKED(crl);

	gens = NULL;
	for (i = 0; i < sk_X509_REVOKED_num(revoked); i++)
		{
		X509_REVOKED *rev = sk_X509_REVOKED_value(revoked, i);
		STACK_OF(X509_EXTENSION) *exts;
		X509_EXTENSION *ext;
		gtmp = X509_REVOKED_get_ext_d2i(rev, 
						NID_certificate_issuer,
						&j, NULL);
		if (!gtmp && (j != -1))
			{
			crl->flags |= EXFLAG_INVALID;
			return 1;
			}

		if (gtmp)
			{
			gens = gtmp;
			if (!crl->issuers)
				{
				crl->issuers = sk_GENERAL_NAMES_new_null();
				if (!crl->issuers)
					return 0;
				}
			if (!sk_GENERAL_NAMES_push(crl->issuers, gtmp))
				return 0;
			}
		rev->issuer = gens;

		/* Check for critical CRL entry extensions */

		exts = rev->extensions;

		for (j = 0; j < sk_X509_EXTENSION_num(exts); j++)
			{
			ext = sk_X509_EXTENSION_value(exts, j);
			if (ext->critical > 0)
				{
				if (OBJ_obj2nid(ext->object) ==
					NID_certificate_issuer)
					continue;
				crl->flags |= EXFLAG_CRITICAL;
				break;
				}
			}


		}

	return 1;

	}

/* The X509_CRL structure needs a bit of customisation. Cache some extensions
 * and hash of the whole CRL.
 */
@@ -139,6 +205,7 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
		crl->idp_flags = 0;
		crl->meth = default_crl_method;
		crl->meth_data = NULL;
		crl->issuers = NULL;
		break;

		case ASN1_OP_D2I_POST:
@@ -176,6 +243,11 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
				break;
				}
			}


		if (!crl_set_issuers(crl))
			return 0;

		if (crl->meth->crl_init)
			{
			if (crl->meth->crl_init(crl) == 0)
@@ -193,6 +265,7 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
			AUTHORITY_KEYID_free(crl->akid);
		if (crl->idp)
			ISSUING_DIST_POINT_free(crl->idp);
		sk_GENERAL_NAMES_pop_free(crl->issuers, GENERAL_NAMES_free);
		break;
		}
	return 1;
@@ -284,7 +357,16 @@ int X509_CRL_get0_by_serial(X509_CRL *crl,
		X509_REVOKED **ret, ASN1_INTEGER *serial)
	{
	if (crl->meth->crl_lookup)
		return crl->meth->crl_lookup(crl, ret, serial);
		return crl->meth->crl_lookup(crl, ret, serial, NULL);
	return 0;
	}

int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x)
	{
	if (crl->meth->crl_lookup)
		return crl->meth->crl_lookup(crl, ret,
						X509_get_serialNumber(x),
						X509_get_issuer_name(x));
	return 0;
	}

@@ -294,10 +376,39 @@ static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
		crl->sig_alg, crl->signature,crl->crl,r));
	}

static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm,
						X509_REVOKED *rev)
	{
	int i;

	if (!rev->issuer)
		{
		if (!nm)
			return 1;
		if (!X509_NAME_cmp(nm, X509_CRL_get_issuer(crl)))
			return 1;
		return 0;
		}

	if (!nm)
		nm = X509_CRL_get_issuer(crl);

	for (i = 0; i < sk_GENERAL_NAME_num(rev->issuer); i++)
		{
		GENERAL_NAME *gen = sk_GENERAL_NAME_value(rev->issuer, i);
		if (gen->type != GEN_DIRNAME)
			continue;
		if (!X509_NAME_cmp(nm, gen->d.directoryName))
			return 1;
		}
	return 0;

	}

static int def_crl_lookup(X509_CRL *crl,
		X509_REVOKED **ret, ASN1_INTEGER *serial)
		X509_REVOKED **ret, ASN1_INTEGER *serial, X509_NAME *issuer)
	{
	X509_REVOKED rtmp;
	X509_REVOKED rtmp, *rev;
	int idx;
	rtmp.serialNumber = serial;
	/* Sort revoked into serial number order if not already sorted.
@@ -310,15 +421,21 @@ static int def_crl_lookup(X509_CRL *crl,
		CRYPTO_w_unlock(CRYPTO_LOCK_X509_CRL);
		}
	idx = sk_X509_REVOKED_find(crl->crl->revoked, &rtmp);
	/* If found assume revoked: want something cleverer than
	 * this to handle entry extensions in V2 CRLs.
	 */
	if(idx >= 0)
	if(idx < 0)
		return 0;
	/* Need to look for matching name */
	for(;idx < sk_X509_REVOKED_num(crl->crl->revoked); idx++)
		{
		rev = sk_X509_REVOKED_value(crl->crl->revoked, idx);
		if (ASN1_INTEGER_cmp(rev->serialNumber, serial))
			return 0;
		if (crl_revoked_issuer_match(crl, issuer, rev))
			{
			if (ret)
			*ret = sk_X509_REVOKED_value(crl->crl->revoked, idx);
				*ret = rev;
			return 1;
			}
		}
	return 0;
	}

@@ -333,7 +450,8 @@ void X509_CRL_set_default_method(const X509_CRL_METHOD *meth)
X509_CRL_METHOD *X509_CRL_METHOD_new(
	int (*crl_init)(X509_CRL *crl),
	int (*crl_free)(X509_CRL *crl),
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret, ASN1_INTEGER *ser),
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
				ASN1_INTEGER *ser, X509_NAME *issuer),
	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk))
	{
	X509_CRL_METHOD *m;
+6 −1
Original line number Diff line number Diff line
@@ -434,6 +434,8 @@ struct x509_revoked_st
	ASN1_INTEGER *serialNumber;
	ASN1_TIME *revocationDate;
	STACK_OF(X509_EXTENSION) /* optional */ *extensions;
	/* Set up if indirect CRL */
	STACK_OF(GENERAL_NAME) *issuer;
	int sequence; /* load sequence */
	};

@@ -469,6 +471,7 @@ struct X509_crl_st
#ifndef OPENSSL_NO_SHA
	unsigned char sha1_hash[SHA_DIGEST_LENGTH];
#endif
	STACK_OF(GENERAL_NAMES) *issuers;
	const X509_CRL_METHOD *meth;
	void *meth_data;
	} /* X509_CRL */;
@@ -617,7 +620,8 @@ void X509_CRL_set_default_method(const X509_CRL_METHOD *meth);
X509_CRL_METHOD *X509_CRL_METHOD_new(
	int (*crl_init)(X509_CRL *crl),
	int (*crl_free)(X509_CRL *crl),
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret, ASN1_INTEGER *ser),
	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
				ASN1_INTEGER *ser, X509_NAME *issuer),
	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk));
void X509_CRL_METHOD_free(X509_CRL_METHOD *m);

@@ -847,6 +851,7 @@ DECLARE_ASN1_FUNCTIONS(X509_CRL)
int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
int X509_CRL_get0_by_serial(X509_CRL *crl,
		X509_REVOKED **ret, ASN1_INTEGER *serial);
int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x);

X509_PKEY *	X509_PKEY_new(void );
void		X509_PKEY_free(X509_PKEY *a);
+59 −28
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ static int check_revocation(X509_STORE_CTX *ctx);
static int check_cert(X509_STORE_CTX *ctx);
static int check_policy(X509_STORE_CTX *ctx);
static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer);
static int idp_check_scope(X509 *x, X509_CRL *crl);
static int idp_check_scope(X509 *x, X509_CRL *crl, int *pimatch);
static int check_crl_path(X509_STORE_CTX *ctx, X509 *x);
static int check_crl_chain(X509_STORE_CTX *ctx,
			STACK_OF(X509) *cert_path,
@@ -751,7 +751,7 @@ static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify)

/* IDP flags which cause a CRL to be rejected */

#define IDP_REJECT	(IDP_INVALID|IDP_INDIRECT|IDP_REASONS)
#define IDP_REJECT	(IDP_INVALID|IDP_REASONS)

static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl,
			X509_NAME *nm, STACK_OF(X509_CRL) *crls)
@@ -761,11 +761,19 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl,
	X509 *crl_issuer, *best_crl_issuer = NULL;
	for (i = 0; i < sk_X509_CRL_num(crls); i++)
		{
		int imatch = 1;
		crl_score = 0;
		crl_issuer = NULL;
		crl = sk_X509_CRL_value(crls, i);
		if (nm && X509_NAME_cmp(nm, X509_CRL_get_issuer(crl)))
			{
			/* Issuer name does not match: could be indirect */
			if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT))
				continue;
			if (!(crl->idp_flags & IDP_INDIRECT))
				continue;
			imatch = 0;
			}
		if (check_crl_time(ctx, crl, 0))
			crl_score |= CRL_SCORE_TIME;

@@ -773,12 +781,16 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl,
			{
			if (crl->idp_flags & IDP_REJECT)
				continue;
			if (idp_check_scope(ctx->current_cert, crl))
			if (idp_check_scope(ctx->current_cert, crl, &imatch))
				crl_score |= CRL_SCORE_SCOPE;
			}
		else
			crl_score |= CRL_SCORE_SCOPE;

		/* If no issuer match at this point try next CRL */
		if (!imatch)
			continue;

		if (crl_akid_check(ctx, crl, &crl_issuer))
			crl_score |= CRL_SCORE_AKID;
		/* If CRL matches criteria and issuer is not different use it */
@@ -809,6 +821,7 @@ static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl,
static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer)
	{
	X509 *crl_issuer;
	X509_NAME *cnm = X509_CRL_get_issuer(crl);
	int cidx = ctx->error_depth;
	int i;
	if (!crl->akid)
@@ -818,25 +831,18 @@ static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer)
	crl_issuer = sk_X509_value(ctx->chain, cidx);
	if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
		return 1;
	/* If crl_issuer is self issued we may get a match further along the
	 * chain.
	 */
	if (crl_issuer->ex_flags & EXFLAG_SI)
		{
	for (cidx++; cidx < sk_X509_num(ctx->chain); cidx++)
		{
		crl_issuer = sk_X509_value(ctx->chain, cidx);
		if (X509_NAME_cmp(X509_get_subject_name(crl_issuer), cnm))
			continue;
		if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
			{
			*pissuer = crl_issuer;
			return 1;
			}
			if (!(crl_issuer->ex_flags & EXFLAG_SI))
				break;
			}
		}


	/* Anything else needs extended CRL support */

	if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT))
@@ -845,7 +851,6 @@ static int crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer)
	/* Otherwise the CRL issuer is not on the path. Look for it in the
	 * set of untrusted certificates.
	 */

	for (i = 0; i < sk_X509_num(ctx->untrusted); i++)
		{
		crl_issuer = sk_X509_value(ctx->untrusted, i);
@@ -928,6 +933,7 @@ static int check_crl_chain(X509_STORE_CTX *ctx,
 * 1. Both are relative names and compare X509_NAME types.
 * 2. One full, one relative. Compare X509_NAME to GENERAL_NAMES.
 * 3. Both are full names and compare two GENERAL_NAMES.
 * 4. One is NULL: automatic match.
 */


@@ -937,6 +943,8 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b)
	GENERAL_NAMES *gens = NULL;
	GENERAL_NAME *gena, *genb;
	int i, j;
	if (!a || !b)
		return 1;
	if (a->type == 1)
		{
		if (!a->dpname)
@@ -995,9 +1003,30 @@ static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b)

	}

static int idp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int *pimatch)
	{
	int i;
	X509_NAME *nm = X509_CRL_get_issuer(crl);
	/* If no CRLissuer return is successful iff don't need a match */
	if (!dp->CRLissuer)
		return *pimatch;
	for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++)
		{
		GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i);
		if (gen->type != GEN_DIRNAME)
			continue;
		if (!X509_NAME_cmp(gen->d.directoryName, nm))
			{
			*pimatch = 1;
			return 1;
			}
		}
	return 0;
	}

/* Check IDP name matches at least one CRLDP name */

static int idp_check_scope(X509 *x, X509_CRL *crl)
static int idp_check_scope(X509 *x, X509_CRL *crl, int *pimatch)
	{
	int i;
	if (crl->idp_flags & IDP_ONLYATTR)
@@ -1012,19 +1041,20 @@ static int idp_check_scope(X509 *x, X509_CRL *crl)
		if (crl->idp_flags & IDP_ONLYCA)
			return 0;
		}
	if (!crl->idp->distpoint)
	if (!crl->idp->distpoint && *pimatch)
		return 1;
	if (!x->crldp)
		return 0;
	for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++)
		{
		DIST_POINT *dp = sk_DIST_POINT_value(x->crldp, i);
		/* We don't handle these at present */
		if (dp->reasons || dp->CRLissuer)
		if (dp->reasons)
			continue;
		if (idp_check_dp(dp->distpoint, crl->idp->distpoint))
			{
			if (idp_check_crlissuer(dp, crl, pimatch))
				return 1;
			}
		}
	return 0;
	}

@@ -1117,19 +1147,20 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl)

		if (crl->idp_flags & IDP_PRESENT)
			{
			int dmy = 1;
			if (crl->idp_flags & IDP_INVALID)
				{
				ctx->error = X509_V_ERR_INVALID_EXTENSION;
				ok = ctx->verify_cb(0, ctx);
				if(!ok) goto err;
				}
			if (crl->idp_flags & (IDP_REASONS|IDP_INDIRECT))
			if (crl->idp_flags & IDP_REASONS)
				{
				ctx->error = X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE;
				ok = ctx->verify_cb(0, ctx);
				if(!ok) goto err;
				}
			if (!idp_check_scope(ctx->current_cert, crl))
			if (!idp_check_scope(ctx->current_cert, crl, &dmy))
				{
				ctx->error = X509_V_ERR_DIFFERENT_CRL_SCOPE;
				ok = ctx->verify_cb(0, ctx);
@@ -1177,7 +1208,7 @@ static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x)
	 * If found assume revoked: want something cleverer than
	 * this to handle entry extensions in V2 CRLs.
	 */
	if (X509_CRL_get0_by_serial(crl, NULL, X509_get_serialNumber(x)) > 0)
	if (X509_CRL_get0_by_cert(crl, NULL, x) > 0)
		{
		ctx->error = X509_V_ERR_CERT_REVOKED;
		ok = ctx->verify_cb(0, ctx);