Initial indirect CRL support.

This commit is contained in:
Dr. Stephen Henson 2008-08-20 16:42:19 +00:00
parent 8c9bd89338
commit d0fff69dc9
5 changed files with 206 additions and 43 deletions

View file

@ -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

View file

@ -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);
};

View file

@ -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 def_crl_lookup(X509_CRL *crl,
X509_REVOKED **ret, ASN1_INTEGER *serial)
static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm,
X509_REVOKED *rev)
{
X509_REVOKED rtmp;
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_NAME *issuer)
{
X509_REVOKED rtmp, *rev;
int idx;
rtmp.serialNumber = serial;
/* Sort revoked into serial number order if not already sorted.
@ -310,14 +421,20 @@ 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++)
{
if (ret)
*ret = sk_X509_REVOKED_value(crl->crl->revoked, idx);
return 1;
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 = 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;

View file

@ -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);

View file

@ -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)))
continue;
{
/* 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++)
{
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)
{
crl_issuer = sk_X509_value(ctx->chain, cidx);
if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK)
{
*pissuer = crl_issuer;
return 1;
}
if (!(crl_issuer->ex_flags & EXFLAG_SI))
break;
*pissuer = crl_issuer;
return 1;
}
}
/* 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,18 +1041,19 @@ 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))
return 1;
{
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);