From d0fff69dc958602b56872efc597bc0b153d20336 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Wed, 20 Aug 2008 16:42:19 +0000 Subject: [PATCH] Initial indirect CRL support. --- CHANGES | 8 +++ crypto/asn1/asn1_locl.h | 3 +- crypto/asn1/x_crl.c | 144 ++++++++++++++++++++++++++++++++++++---- crypto/x509/x509.h | 7 +- crypto/x509/x509_vfy.c | 87 ++++++++++++++++-------- 5 files changed, 206 insertions(+), 43 deletions(-) diff --git a/CHANGES b/CHANGES index 966a90021c..15ad439990 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/crypto/asn1/asn1_locl.h b/crypto/asn1/asn1_locl.h index 3cead31fcd..7d10700fe1 100644 --- a/crypto/asn1/asn1_locl.h +++ b/crypto/asn1/asn1_locl.h @@ -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); }; diff --git a/crypto/asn1/x_crl.c b/crypto/asn1/x_crl.c index 6edc6f2e34..e6346374dc 100644 --- a/crypto/asn1/x_crl.c +++ b/crypto/asn1/x_crl.c @@ -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; diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index adbdc65891..a1ac6192ba 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -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); diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 7435781947..e92a9c3e3b 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -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);