diff options
Diffstat (limited to 'Cryptlib/OpenSSL/crypto/x509/x509_vfy.c')
| -rw-r--r-- | Cryptlib/OpenSSL/crypto/x509/x509_vfy.c | 1476 |
1 files changed, 1219 insertions, 257 deletions
diff --git a/Cryptlib/OpenSSL/crypto/x509/x509_vfy.c b/Cryptlib/OpenSSL/crypto/x509/x509_vfy.c index 3249ff82..c085c134 100644 --- a/Cryptlib/OpenSSL/crypto/x509/x509_vfy.c +++ b/Cryptlib/OpenSSL/crypto/x509/x509_vfy.c @@ -69,15 +69,73 @@ #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/objects.h> +#include "vpm_int.h" + +/* CRL score values */ + +/* No unhandled critical extensions */ + +#define CRL_SCORE_NOCRITICAL 0x100 + +/* certificate is within CRL scope */ + +#define CRL_SCORE_SCOPE 0x080 + +/* CRL times valid */ + +#define CRL_SCORE_TIME 0x040 + +/* Issuer name matches certificate */ + +#define CRL_SCORE_ISSUER_NAME 0x020 + +/* If this score or above CRL is probably valid */ + +#define CRL_SCORE_VALID (CRL_SCORE_NOCRITICAL|CRL_SCORE_TIME|CRL_SCORE_SCOPE) + +/* CRL issuer is certificate issuer */ + +#define CRL_SCORE_ISSUER_CERT 0x018 + +/* CRL issuer is on certificate path */ + +#define CRL_SCORE_SAME_PATH 0x008 + +/* CRL issuer matches CRL AKID */ + +#define CRL_SCORE_AKID 0x004 + +/* Have a delta CRL with valid times */ + +#define CRL_SCORE_TIME_DELTA 0x002 static int null_callback(int ok, X509_STORE_CTX *e); static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x); static int check_chain_extensions(X509_STORE_CTX *ctx); +static int check_name_constraints(X509_STORE_CTX *ctx); +static int check_id(X509_STORE_CTX *ctx); static int check_trust(X509_STORE_CTX *ctx); 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 get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, + unsigned int *preasons, X509_CRL *crl, X509 *x); +static int get_crl_delta(X509_STORE_CTX *ctx, + X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x); +static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, + int *pcrl_score, X509_CRL *base, + STACK_OF(X509_CRL) *crls); +static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, X509 **pissuer, + int *pcrl_score); +static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score, + unsigned int *preasons); +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, + STACK_OF(X509) *crl_path); + static int internal_verify(X509_STORE_CTX *ctx); const char X509_version[] = "X.509" OPENSSL_VERSION_PTEXT; @@ -92,20 +150,62 @@ static int x509_subject_cmp(X509 **a, X509 **b) return X509_subject_name_cmp(*a, *b); } #endif +/* Return 1 is a certificate is self signed */ +static int cert_self_signed(X509 *x) +{ + X509_check_purpose(x, -1, 0); + if (x->ex_flags & EXFLAG_SS) + return 1; + else + return 0; +} + +/* Given a certificate try and find an exact match in the store */ + +static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) +{ + STACK_OF(X509) *certs; + X509 *xtmp = NULL; + int i; + /* Lookup all certs with matching subject name */ + certs = ctx->lookup_certs(ctx, X509_get_subject_name(x)); + if (certs == NULL) + return NULL; + /* Look for exact match */ + for (i = 0; i < sk_X509_num(certs); i++) { + xtmp = sk_X509_value(certs, i); + if (!X509_cmp(xtmp, x)) + break; + } + if (i < sk_X509_num(certs)) + CRYPTO_add(&xtmp->references, 1, CRYPTO_LOCK_X509); + else + xtmp = NULL; + sk_X509_pop_free(certs, X509_free); + return xtmp; +} int X509_verify_cert(X509_STORE_CTX *ctx) { - X509 *x, *xtmp, *chain_ss = NULL; + X509 *x, *xtmp, *xtmp2, *chain_ss = NULL; int bad_chain = 0; X509_VERIFY_PARAM *param = ctx->param; int depth, i, ok = 0; - int num; + int num, j, retry; int (*cb) (int xok, X509_STORE_CTX *xctx); STACK_OF(X509) *sktmp = NULL; if (ctx->cert == NULL) { X509err(X509_F_X509_VERIFY_CERT, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); return -1; } + if (ctx->chain != NULL) { + /* + * This X509_STORE_CTX has already been used to verify a cert. We + * cannot do another one. + */ + X509err(X509_F_X509_VERIFY_CERT, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } cb = ctx->verify_cb; @@ -113,15 +213,13 @@ int X509_verify_cert(X509_STORE_CTX *ctx) * first we make sure the chain we are going to build is present and that * the first entry is in place */ - if (ctx->chain == NULL) { - if (((ctx->chain = sk_X509_new_null()) == NULL) || - (!sk_X509_push(ctx->chain, ctx->cert))) { - X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); - goto end; - } - CRYPTO_add(&ctx->cert->references, 1, CRYPTO_LOCK_X509); - ctx->last_untrusted = 1; + if (((ctx->chain = sk_X509_new_null()) == NULL) || + (!sk_X509_push(ctx->chain, ctx->cert))) { + X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + goto end; } + CRYPTO_add(&ctx->cert->references, 1, CRYPTO_LOCK_X509); + ctx->last_untrusted = 1; /* We use a temporary STACK so we can chop and hack at it */ if (ctx->untrusted != NULL @@ -143,8 +241,24 @@ int X509_verify_cert(X509_STORE_CTX *ctx) * later. */ /* If we are self signed, we break */ - if (ctx->check_issued(ctx, x, x)) + if (cert_self_signed(x)) break; + /* + * If asked see if we can find issuer in trusted store first + */ + if (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) { + ok = ctx->get_issuer(&xtmp, ctx, x); + if (ok < 0) + return ok; + /* + * If successful for now free up cert so it will be picked up + * again later. + */ + if (ok > 0) { + X509_free(xtmp); + break; + } + } /* If we were passed a cert chain, use it first */ if (ctx->untrusted != NULL) { @@ -168,88 +282,134 @@ int X509_verify_cert(X509_STORE_CTX *ctx) break; } + /* Remember how many untrusted certs we have */ + j = num; /* * at this point, chain should contain a list of untrusted certificates. * We now need to add at least one trusted one, if possible, otherwise we * complain. */ - /* - * Examine last certificate in chain and see if it is self signed. - */ - - i = sk_X509_num(ctx->chain); - x = sk_X509_value(ctx->chain, i - 1); - if (ctx->check_issued(ctx, x, x)) { - /* we have a self signed certificate */ - if (sk_X509_num(ctx->chain) == 1) { - /* - * We have a single self signed certificate: see if we can find - * it in the store. We must have an exact match to avoid possible - * impersonation. - */ - ok = ctx->get_issuer(&xtmp, ctx, x); - if ((ok <= 0) || X509_cmp(x, xtmp)) { - ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; - ctx->current_cert = x; - ctx->error_depth = i - 1; - if (ok == 1) - X509_free(xtmp); - bad_chain = 1; - ok = cb(0, ctx); - if (!ok) - goto end; + do { + /* + * Examine last certificate in chain and see if it is self signed. + */ + i = sk_X509_num(ctx->chain); + x = sk_X509_value(ctx->chain, i - 1); + if (cert_self_signed(x)) { + /* we have a self signed certificate */ + if (sk_X509_num(ctx->chain) == 1) { + /* + * We have a single self signed certificate: see if we can + * find it in the store. We must have an exact match to avoid + * possible impersonation. + */ + ok = ctx->get_issuer(&xtmp, ctx, x); + if ((ok <= 0) || X509_cmp(x, xtmp)) { + ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; + ctx->current_cert = x; + ctx->error_depth = i - 1; + if (ok == 1) + X509_free(xtmp); + bad_chain = 1; + ok = cb(0, ctx); + if (!ok) + goto end; + } else { + /* + * We have a match: replace certificate with store + * version so we get any trust settings. + */ + X509_free(x); + x = xtmp; + (void)sk_X509_set(ctx->chain, i - 1, x); + ctx->last_untrusted = 0; + } } else { /* - * We have a match: replace certificate with store version so - * we get any trust settings. + * extract and save self signed certificate for later use */ - X509_free(x); - x = xtmp; - (void)sk_X509_set(ctx->chain, i - 1, x); - ctx->last_untrusted = 0; + chain_ss = sk_X509_pop(ctx->chain); + ctx->last_untrusted--; + num--; + j--; + x = sk_X509_value(ctx->chain, num - 1); } - } else { - /* - * extract and save self signed certificate for later use - */ - chain_ss = sk_X509_pop(ctx->chain); - ctx->last_untrusted--; - num--; - x = sk_X509_value(ctx->chain, num - 1); } - } - - /* We now lookup certs from the certificate store */ - for (;;) { - /* If we have enough, we break */ - if (depth < num) - break; + /* We now lookup certs from the certificate store */ + for (;;) { + /* If we have enough, we break */ + if (depth < num) + break; + /* If we are self signed, we break */ + if (cert_self_signed(x)) + break; + ok = ctx->get_issuer(&xtmp, ctx, x); - /* If we are self signed, we break */ - if (ctx->check_issued(ctx, x, x)) - break; + if (ok < 0) + return ok; + if (ok == 0) + break; + x = xtmp; + if (!sk_X509_push(ctx->chain, x)) { + X509_free(xtmp); + X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); + return 0; + } + num++; + } - ok = ctx->get_issuer(&xtmp, ctx, x); + /* we now have our chain, lets check it... */ + i = check_trust(ctx); - if (ok < 0) - return ok; - if (ok == 0) - break; + /* If explicitly rejected error */ + if (i == X509_TRUST_REJECTED) + goto end; + /* + * If it's not explicitly trusted then check if there is an alternative + * chain that could be used. We only do this if we haven't already + * checked via TRUSTED_FIRST and the user hasn't switched off alternate + * chain checking + */ + retry = 0; + if (i != X509_TRUST_TRUSTED + && !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) + && !(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) { + while (j-- > 1) { + xtmp2 = sk_X509_value(ctx->chain, j - 1); + ok = ctx->get_issuer(&xtmp, ctx, xtmp2); + if (ok < 0) + goto end; + /* Check if we found an alternate chain */ + if (ok > 0) { + /* + * Free up the found cert we'll add it again later + */ + X509_free(xtmp); - x = xtmp; - if (!sk_X509_push(ctx->chain, x)) { - X509_free(xtmp); - X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); - return 0; + /* + * Dump all the certs above this point - we've found an + * alternate chain + */ + while (num > j) { + xtmp = sk_X509_pop(ctx->chain); + X509_free(xtmp); + num--; + } + ctx->last_untrusted = sk_X509_num(ctx->chain); + retry = 1; + break; + } + } } - num++; - } - - /* we now have our chain, lets check it... */ + } while (retry); - /* Is last certificate looked up self signed? */ - if (!ctx->check_issued(ctx, x, x)) { + /* + * If not explicitly trusted then indicate error unless it's a single + * self signed certificate in which case we've indicated an error already + * and set bad_chain == 1 + */ + if (i != X509_TRUST_TRUSTED && !bad_chain) { if ((chain_ss == NULL) || !ctx->check_issued(ctx, x, chain_ss)) { if (ctx->last_untrusted >= num) ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; @@ -279,10 +439,14 @@ int X509_verify_cert(X509_STORE_CTX *ctx) if (!ok) goto end; - /* The chain extensions are OK: check trust */ + /* Check name constraints */ + + ok = check_name_constraints(ctx); + + if (!ok) + goto end; - if (param->trust > 0) - ok = check_trust(ctx); + ok = check_id(ctx); if (!ok) goto end; @@ -299,6 +463,16 @@ int X509_verify_cert(X509_STORE_CTX *ctx) if (!ok) goto end; + i = X509_chain_check_suiteb(&ctx->error_depth, NULL, ctx->chain, + ctx->param->flags); + if (i != X509_V_OK) { + ctx->error = i; + ctx->current_cert = sk_X509_value(ctx->chain, ctx->error_depth); + ok = cb(0, ctx); + if (!ok) + goto end; + } + /* At this point, we have a chain and need to verify it */ if (ctx->verify != NULL) ok = ctx->verify(ctx); @@ -365,7 +539,6 @@ static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer) ctx->current_cert = x; ctx->current_issuer = issuer; return ctx->verify_cb(0, ctx); - return 0; } /* Alternative lookup method: look from a STACK stored in other_ctx */ @@ -394,8 +567,8 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) X509 *x; int (*cb) (int xok, X509_STORE_CTX *xctx); int proxy_path_length = 0; - int allow_proxy_certs = - ! !(ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS); + int purpose; + int allow_proxy_certs; cb = ctx->verify_cb; /*- @@ -409,11 +582,21 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) */ must_be_ca = -1; - /* - * A hack to keep people who don't want to modify their software happy - */ - if (getenv("OPENSSL_ALLOW_PROXY_CERTS")) - allow_proxy_certs = 1; + /* CRL path validation */ + if (ctx->parent) { + allow_proxy_certs = 0; + purpose = X509_PURPOSE_CRL_SIGN; + } else { + allow_proxy_certs = + ! !(ctx->param->flags & X509_V_FLAG_ALLOW_PROXY_CERTS); + /* + * A hack to keep people who don't want to modify their software + * happy + */ + if (getenv("OPENSSL_ALLOW_PROXY_CERTS")) + allow_proxy_certs = 1; + purpose = ctx->param->purpose; + } /* Check all untrusted certificates */ for (i = 0; i < ctx->last_untrusted; i++) { @@ -471,7 +654,7 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) goto end; } if (ctx->param->purpose > 0) { - ret = X509_check_purpose(x, ctx->param->purpose, must_be_ca > 0); + ret = X509_check_purpose(x, purpose, must_be_ca > 0); if ((ret == 0) || ((ctx->param->flags & X509_V_FLAG_X509_STRICT) && (ret != 1))) { @@ -522,30 +705,130 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) #endif } -static int check_trust(X509_STORE_CTX *ctx) +static int check_name_constraints(X509_STORE_CTX *ctx) { -#ifdef OPENSSL_NO_CHAIN_VERIFY + X509 *x; + int i, j, rv; + /* Check name constraints for all certificates */ + for (i = sk_X509_num(ctx->chain) - 1; i >= 0; i--) { + x = sk_X509_value(ctx->chain, i); + /* Ignore self issued certs unless last in chain */ + if (i && (x->ex_flags & EXFLAG_SI)) + continue; + /* + * Check against constraints for all certificates higher in chain + * including trust anchor. Trust anchor not strictly speaking needed + * but if it includes constraints it is to be assumed it expects them + * to be obeyed. + */ + for (j = sk_X509_num(ctx->chain) - 1; j > i; j--) { + NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc; + if (nc) { + rv = NAME_CONSTRAINTS_check(x, nc); + if (rv != X509_V_OK) { + ctx->error = rv; + ctx->error_depth = i; + ctx->current_cert = x; + if (!ctx->verify_cb(0, ctx)) + return 0; + } + } + } + } return 1; -#else +} + +static int check_id_error(X509_STORE_CTX *ctx, int errcode) +{ + ctx->error = errcode; + ctx->current_cert = ctx->cert; + ctx->error_depth = 0; + return ctx->verify_cb(0, ctx); +} + +static int check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id) +{ + int i; + int n = sk_OPENSSL_STRING_num(id->hosts); + char *name; + + for (i = 0; i < n; ++i) { + name = sk_OPENSSL_STRING_value(id->hosts, i); + if (X509_check_host(x, name, 0, id->hostflags, &id->peername) > 0) + return 1; + } + return n == 0; +} + +static int check_id(X509_STORE_CTX *ctx) +{ + X509_VERIFY_PARAM *vpm = ctx->param; + X509_VERIFY_PARAM_ID *id = vpm->id; + X509 *x = ctx->cert; + if (id->hosts && check_hosts(x, id) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH)) + return 0; + } + if (id->email && X509_check_email(x, id->email, id->emaillen, 0) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH)) + return 0; + } + if (id->ip && X509_check_ip(x, id->ip, id->iplen, 0) <= 0) { + if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH)) + return 0; + } + return 1; +} + +static int check_trust(X509_STORE_CTX *ctx) +{ int i, ok; - X509 *x; + X509 *x = NULL; int (*cb) (int xok, X509_STORE_CTX *xctx); cb = ctx->verify_cb; -/* For now just check the last certificate in the chain */ - i = sk_X509_num(ctx->chain) - 1; - x = sk_X509_value(ctx->chain, i); - ok = X509_check_trust(x, ctx->param->trust, 0); - if (ok == X509_TRUST_TRUSTED) - return 1; - ctx->error_depth = i; - ctx->current_cert = x; - if (ok == X509_TRUST_REJECTED) - ctx->error = X509_V_ERR_CERT_REJECTED; - else - ctx->error = X509_V_ERR_CERT_UNTRUSTED; - ok = cb(0, ctx); - return ok; -#endif + /* Check all trusted certificates in chain */ + for (i = ctx->last_untrusted; i < sk_X509_num(ctx->chain); i++) { + x = sk_X509_value(ctx->chain, i); + ok = X509_check_trust(x, ctx->param->trust, 0); + /* If explicitly trusted return trusted */ + if (ok == X509_TRUST_TRUSTED) + return X509_TRUST_TRUSTED; + /* + * If explicitly rejected notify callback and reject if not + * overridden. + */ + if (ok == X509_TRUST_REJECTED) { + ctx->error_depth = i; + ctx->current_cert = x; + ctx->error = X509_V_ERR_CERT_REJECTED; + ok = cb(0, ctx); + if (!ok) + return X509_TRUST_REJECTED; + } + } + /* + * If we accept partial chains and have at least one trusted certificate + * return success. + */ + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { + X509 *mx; + if (ctx->last_untrusted < sk_X509_num(ctx->chain)) + return X509_TRUST_TRUSTED; + x = sk_X509_value(ctx->chain, 0); + mx = lookup_cert_match(ctx, x); + if (mx) { + (void)sk_X509_set(ctx->chain, 0, mx); + X509_free(x); + ctx->last_untrusted = 0; + return X509_TRUST_TRUSTED; + } + } + + /* + * If no trusted certs in chain at all return untrusted and allow + * standard (no issuer cert) etc errors to be indicated. + */ + return X509_TRUST_UNTRUSTED; } static int check_revocation(X509_STORE_CTX *ctx) @@ -555,8 +838,12 @@ static int check_revocation(X509_STORE_CTX *ctx) return 1; if (ctx->param->flags & X509_V_FLAG_CRL_CHECK_ALL) last = sk_X509_num(ctx->chain) - 1; - else + else { + /* If checking CRL paths this isn't the EE certificate */ + if (ctx->parent) + return 1; last = 0; + } for (i = 0; i <= last; i++) { ctx->error_depth = i; ok = check_cert(ctx); @@ -568,30 +855,72 @@ static int check_revocation(X509_STORE_CTX *ctx) static int check_cert(X509_STORE_CTX *ctx) { - X509_CRL *crl = NULL; + X509_CRL *crl = NULL, *dcrl = NULL; X509 *x; int ok, cnum; + unsigned int last_reasons; cnum = ctx->error_depth; x = sk_X509_value(ctx->chain, cnum); ctx->current_cert = x; - /* Try to retrieve relevant CRL */ - ok = ctx->get_crl(ctx, &crl, x); - /* - * If error looking up CRL, nothing we can do except notify callback - */ - if (!ok) { - ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL; - ok = ctx->verify_cb(0, ctx); - goto err; + ctx->current_issuer = NULL; + ctx->current_crl_score = 0; + ctx->current_reasons = 0; + while (ctx->current_reasons != CRLDP_ALL_REASONS) { + last_reasons = ctx->current_reasons; + /* Try to retrieve relevant CRL */ + if (ctx->get_crl) + ok = ctx->get_crl(ctx, &crl, x); + else + ok = get_crl_delta(ctx, &crl, &dcrl, x); + /* + * If error looking up CRL, nothing we can do except notify callback + */ + if (!ok) { + ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL; + ok = ctx->verify_cb(0, ctx); + goto err; + } + ctx->current_crl = crl; + ok = ctx->check_crl(ctx, crl); + if (!ok) + goto err; + + if (dcrl) { + ok = ctx->check_crl(ctx, dcrl); + if (!ok) + goto err; + ok = ctx->cert_crl(ctx, dcrl, x); + if (!ok) + goto err; + } else + ok = 1; + + /* Don't look in full CRL if delta reason is removefromCRL */ + if (ok != 2) { + ok = ctx->cert_crl(ctx, crl, x); + if (!ok) + goto err; + } + + X509_CRL_free(crl); + X509_CRL_free(dcrl); + crl = NULL; + dcrl = NULL; + /* + * If reasons not updated we wont get anywhere by another iteration, + * so exit loop. + */ + if (last_reasons == ctx->current_reasons) { + ctx->error = X509_V_ERR_UNABLE_TO_GET_CRL; + ok = ctx->verify_cb(0, ctx); + goto err; + } } - ctx->current_crl = crl; - ok = ctx->check_crl(ctx, crl); - if (!ok) - goto err; - ok = ctx->cert_crl(ctx, crl, x); err: - ctx->current_crl = NULL; X509_CRL_free(crl); + X509_CRL_free(dcrl); + + ctx->current_crl = NULL; return ok; } @@ -602,7 +931,8 @@ static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) { time_t *ptime; int i; - ctx->current_crl = crl; + if (notify) + ctx->current_crl = crl; if (ctx->param->flags & X509_V_FLAG_USE_CHECK_TIME) ptime = &ctx->param->check_time; else @@ -610,14 +940,18 @@ static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) i = X509_cmp_time(X509_CRL_get_lastUpdate(crl), ptime); if (i == 0) { + if (!notify) + return 0; ctx->error = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD; - if (!notify || !ctx->verify_cb(0, ctx)) + if (!ctx->verify_cb(0, ctx)) return 0; } if (i > 0) { + if (!notify) + return 0; ctx->error = X509_V_ERR_CRL_NOT_YET_VALID; - if (!notify || !ctx->verify_cb(0, ctx)) + if (!ctx->verify_cb(0, ctx)) return 0; } @@ -625,86 +959,509 @@ static int check_crl_time(X509_STORE_CTX *ctx, X509_CRL *crl, int notify) i = X509_cmp_time(X509_CRL_get_nextUpdate(crl), ptime); if (i == 0) { + if (!notify) + return 0; ctx->error = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD; - if (!notify || !ctx->verify_cb(0, ctx)) + if (!ctx->verify_cb(0, ctx)) return 0; } - - if (i < 0) { + /* Ignore expiry of base CRL is delta is valid */ + if ((i < 0) && !(ctx->current_crl_score & CRL_SCORE_TIME_DELTA)) { + if (!notify) + return 0; ctx->error = X509_V_ERR_CRL_HAS_EXPIRED; - if (!notify || !ctx->verify_cb(0, ctx)) + if (!ctx->verify_cb(0, ctx)) return 0; } } - ctx->current_crl = NULL; + if (notify) + ctx->current_crl = NULL; return 1; } -/* - * Lookup CRLs from the supplied list. Look for matching isser name and - * validity. If we can't find a valid CRL return the last one with matching - * name. This gives more meaningful error codes. Otherwise we'd get a CRL not - * found error if a CRL existed with matching name but was invalid. - */ - -static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, - X509_NAME *nm, STACK_OF(X509_CRL) *crls) +static int get_crl_sk(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509_CRL **pdcrl, + X509 **pissuer, int *pscore, unsigned int *preasons, + STACK_OF(X509_CRL) *crls) { - int i; + int i, crl_score, best_score = *pscore; + unsigned int reasons, best_reasons = 0; + X509 *x = ctx->current_cert; X509_CRL *crl, *best_crl = NULL; + X509 *crl_issuer = NULL, *best_crl_issuer = NULL; + for (i = 0; i < sk_X509_CRL_num(crls); i++) { crl = sk_X509_CRL_value(crls, i); - if (X509_NAME_cmp(nm, X509_CRL_get_issuer(crl))) - continue; - if (check_crl_time(ctx, crl, 0)) { - *pcrl = crl; - CRYPTO_add(&crl->references, 1, CRYPTO_LOCK_X509); - return 1; + reasons = *preasons; + crl_score = get_crl_score(ctx, &crl_issuer, &reasons, crl, x); + + if (crl_score > best_score) { + best_crl = crl; + best_crl_issuer = crl_issuer; + best_score = crl_score; + best_reasons = reasons; } - best_crl = crl; } + if (best_crl) { + if (*pcrl) + X509_CRL_free(*pcrl); *pcrl = best_crl; - CRYPTO_add(&best_crl->references, 1, CRYPTO_LOCK_X509); + *pissuer = best_crl_issuer; + *pscore = best_score; + *preasons = best_reasons; + CRYPTO_add(&best_crl->references, 1, CRYPTO_LOCK_X509_CRL); + if (*pdcrl) { + X509_CRL_free(*pdcrl); + *pdcrl = NULL; + } + get_delta_sk(ctx, pdcrl, pscore, best_crl, crls); } + if (best_score >= CRL_SCORE_VALID) + return 1; + return 0; } /* - * Retrieve CRL corresponding to certificate: currently just a subject - * lookup: maybe use AKID later... + * Compare two CRL extensions for delta checking purposes. They should be + * both present or both absent. If both present all fields must be identical. */ -static int get_crl(X509_STORE_CTX *ctx, X509_CRL **pcrl, X509 *x) + +static int crl_extension_match(X509_CRL *a, X509_CRL *b, int nid) { - int ok; - X509_CRL *crl = NULL; - X509_OBJECT xobj; - X509_NAME *nm; - nm = X509_get_issuer_name(x); - ok = get_crl_sk(ctx, &crl, nm, ctx->crls); - if (ok) { - *pcrl = crl; + ASN1_OCTET_STRING *exta, *extb; + int i; + i = X509_CRL_get_ext_by_NID(a, nid, -1); + if (i >= 0) { + /* Can't have multiple occurrences */ + if (X509_CRL_get_ext_by_NID(a, nid, i) != -1) + return 0; + exta = X509_EXTENSION_get_data(X509_CRL_get_ext(a, i)); + } else + exta = NULL; + + i = X509_CRL_get_ext_by_NID(b, nid, -1); + + if (i >= 0) { + + if (X509_CRL_get_ext_by_NID(b, nid, i) != -1) + return 0; + extb = X509_EXTENSION_get_data(X509_CRL_get_ext(b, i)); + } else + extb = NULL; + + if (!exta && !extb) return 1; + + if (!exta || !extb) + return 0; + + if (ASN1_OCTET_STRING_cmp(exta, extb)) + return 0; + + return 1; +} + +/* See if a base and delta are compatible */ + +static int check_delta_base(X509_CRL *delta, X509_CRL *base) +{ + /* Delta CRL must be a delta */ + if (!delta->base_crl_number) + return 0; + /* Base must have a CRL number */ + if (!base->crl_number) + return 0; + /* Issuer names must match */ + if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(delta))) + return 0; + /* AKID and IDP must match */ + if (!crl_extension_match(delta, base, NID_authority_key_identifier)) + return 0; + if (!crl_extension_match(delta, base, NID_issuing_distribution_point)) + return 0; + /* Delta CRL base number must not exceed Full CRL number. */ + if (ASN1_INTEGER_cmp(delta->base_crl_number, base->crl_number) > 0) + return 0; + /* Delta CRL number must exceed full CRL number */ + if (ASN1_INTEGER_cmp(delta->crl_number, base->crl_number) > 0) + return 1; + return 0; +} + +/* + * For a given base CRL find a delta... maybe extend to delta scoring or + * retrieve a chain of deltas... + */ + +static void get_delta_sk(X509_STORE_CTX *ctx, X509_CRL **dcrl, int *pscore, + X509_CRL *base, STACK_OF(X509_CRL) *crls) +{ + X509_CRL *delta; + int i; + if (!(ctx->param->flags & X509_V_FLAG_USE_DELTAS)) + return; + if (!((ctx->current_cert->ex_flags | base->flags) & EXFLAG_FRESHEST)) + return; + for (i = 0; i < sk_X509_CRL_num(crls); i++) { + delta = sk_X509_CRL_value(crls, i); + if (check_delta_base(delta, base)) { + if (check_crl_time(ctx, delta, 0)) + *pscore |= CRL_SCORE_TIME_DELTA; + CRYPTO_add(&delta->references, 1, CRYPTO_LOCK_X509_CRL); + *dcrl = delta; + return; + } } + *dcrl = NULL; +} - ok = X509_STORE_get_by_subject(ctx, X509_LU_CRL, nm, &xobj); +/* + * For a given CRL return how suitable it is for the supplied certificate + * 'x'. The return value is a mask of several criteria. If the issuer is not + * the certificate issuer this is returned in *pissuer. The reasons mask is + * also used to determine if the CRL is suitable: if no new reasons the CRL + * is rejected, otherwise reasons is updated. + */ - if (!ok) { - /* If we got a near match from get_crl_sk use that */ - if (crl) { - *pcrl = crl; - return 1; +static int get_crl_score(X509_STORE_CTX *ctx, X509 **pissuer, + unsigned int *preasons, X509_CRL *crl, X509 *x) +{ + + int crl_score = 0; + unsigned int tmp_reasons = *preasons, crl_reasons; + + /* First see if we can reject CRL straight away */ + + /* Invalid IDP cannot be processed */ + if (crl->idp_flags & IDP_INVALID) + return 0; + /* Reason codes or indirect CRLs need extended CRL support */ + if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) { + if (crl->idp_flags & (IDP_INDIRECT | IDP_REASONS)) + return 0; + } else if (crl->idp_flags & IDP_REASONS) { + /* If no new reasons reject */ + if (!(crl->idp_reasons & ~tmp_reasons)) + return 0; + } + /* Don't process deltas at this stage */ + else if (crl->base_crl_number) + return 0; + /* If issuer name doesn't match certificate need indirect CRL */ + if (X509_NAME_cmp(X509_get_issuer_name(x), X509_CRL_get_issuer(crl))) { + if (!(crl->idp_flags & IDP_INDIRECT)) + return 0; + } else + crl_score |= CRL_SCORE_ISSUER_NAME; + + if (!(crl->flags & EXFLAG_CRITICAL)) + crl_score |= CRL_SCORE_NOCRITICAL; + + /* Check expiry */ + if (check_crl_time(ctx, crl, 0)) + crl_score |= CRL_SCORE_TIME; + + /* Check authority key ID and locate certificate issuer */ + crl_akid_check(ctx, crl, pissuer, &crl_score); + + /* If we can't locate certificate issuer at this point forget it */ + + if (!(crl_score & CRL_SCORE_AKID)) + return 0; + + /* Check cert for matching CRL distribution points */ + + if (crl_crldp_check(x, crl, crl_score, &crl_reasons)) { + /* If no new reasons reject */ + if (!(crl_reasons & ~tmp_reasons)) + return 0; + tmp_reasons |= crl_reasons; + crl_score |= CRL_SCORE_SCOPE; + } + + *preasons = tmp_reasons; + + return crl_score; + +} + +static void crl_akid_check(X509_STORE_CTX *ctx, X509_CRL *crl, + X509 **pissuer, int *pcrl_score) +{ + X509 *crl_issuer = NULL; + X509_NAME *cnm = X509_CRL_get_issuer(crl); + int cidx = ctx->error_depth; + int i; + + if (cidx != sk_X509_num(ctx->chain) - 1) + cidx++; + + crl_issuer = sk_X509_value(ctx->chain, cidx); + + if (X509_check_akid(crl_issuer, crl->akid) == X509_V_OK) { + if (*pcrl_score & CRL_SCORE_ISSUER_NAME) { + *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_ISSUER_CERT; + *pissuer = crl_issuer; + return; + } + } + + 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) { + *pcrl_score |= CRL_SCORE_AKID | CRL_SCORE_SAME_PATH; + *pissuer = crl_issuer; + return; } + } + + /* Anything else needs extended CRL support */ + + if (!(ctx->param->flags & X509_V_FLAG_EXTENDED_CRL_SUPPORT)) + return; + + /* + * 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); + 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; + *pcrl_score |= CRL_SCORE_AKID; + return; + } + } +} + +/* + * Check the path of a CRL issuer certificate. This creates a new + * X509_STORE_CTX and populates it with most of the parameters from the + * parent. This could be optimised somewhat since a lot of path checking will + * be duplicated by the parent, but this will rarely be used in practice. + */ + +static int check_crl_path(X509_STORE_CTX *ctx, X509 *x) +{ + X509_STORE_CTX crl_ctx; + int ret; + /* Don't allow recursive CRL path validation */ + if (ctx->parent) return 0; + if (!X509_STORE_CTX_init(&crl_ctx, ctx->ctx, x, ctx->untrusted)) + return -1; + + crl_ctx.crls = ctx->crls; + /* Copy verify params across */ + X509_STORE_CTX_set0_param(&crl_ctx, ctx->param); + + crl_ctx.parent = ctx; + crl_ctx.verify_cb = ctx->verify_cb; + + /* Verify CRL issuer */ + ret = X509_verify_cert(&crl_ctx); + + if (ret <= 0) + goto err; + + /* Check chain is acceptable */ + + ret = check_crl_chain(ctx, ctx->chain, crl_ctx.chain); + err: + X509_STORE_CTX_cleanup(&crl_ctx); + return ret; +} + +/* + * RFC3280 says nothing about the relationship between CRL path and + * certificate path, which could lead to situations where a certificate could + * be revoked or validated by a CA not authorised to do so. RFC5280 is more + * strict and states that the two paths must end in the same trust anchor, + * though some discussions remain... until this is resolved we use the + * RFC5280 version + */ + +static int check_crl_chain(X509_STORE_CTX *ctx, + STACK_OF(X509) *cert_path, + STACK_OF(X509) *crl_path) +{ + X509 *cert_ta, *crl_ta; + cert_ta = sk_X509_value(cert_path, sk_X509_num(cert_path) - 1); + crl_ta = sk_X509_value(crl_path, sk_X509_num(crl_path) - 1); + if (!X509_cmp(cert_ta, crl_ta)) + return 1; + return 0; +} + +/*- + * Check for match between two dist point names: three separate cases. + * 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. + */ + +static int idp_check_dp(DIST_POINT_NAME *a, DIST_POINT_NAME *b) +{ + X509_NAME *nm = NULL; + GENERAL_NAMES *gens = NULL; + GENERAL_NAME *gena, *genb; + int i, j; + if (!a || !b) + return 1; + if (a->type == 1) { + if (!a->dpname) + return 0; + /* Case 1: two X509_NAME */ + if (b->type == 1) { + if (!b->dpname) + return 0; + if (!X509_NAME_cmp(a->dpname, b->dpname)) + return 1; + else + return 0; + } + /* Case 2: set name and GENERAL_NAMES appropriately */ + nm = a->dpname; + gens = b->name.fullname; + } else if (b->type == 1) { + if (!b->dpname) + return 0; + /* Case 2: set name and GENERAL_NAMES appropriately */ + gens = a->name.fullname; + nm = b->dpname; } - *pcrl = xobj.data.crl; - if (crl) - X509_CRL_free(crl); - return 1; + /* Handle case 2 with one GENERAL_NAMES and one X509_NAME */ + if (nm) { + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { + gena = sk_GENERAL_NAME_value(gens, i); + if (gena->type != GEN_DIRNAME) + continue; + if (!X509_NAME_cmp(nm, gena->d.directoryName)) + return 1; + } + return 0; + } + + /* Else case 3: two GENERAL_NAMES */ + + for (i = 0; i < sk_GENERAL_NAME_num(a->name.fullname); i++) { + gena = sk_GENERAL_NAME_value(a->name.fullname, i); + for (j = 0; j < sk_GENERAL_NAME_num(b->name.fullname); j++) { + genb = sk_GENERAL_NAME_value(b->name.fullname, j); + if (!GENERAL_NAME_cmp(gena, genb)) + return 1; + } + } + + return 0; + +} + +static int crldp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int crl_score) +{ + 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 ! !(crl_score & CRL_SCORE_ISSUER_NAME); + 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)) + return 1; + } + return 0; +} + +/* Check CRLDP and IDP */ + +static int crl_crldp_check(X509 *x, X509_CRL *crl, int crl_score, + unsigned int *preasons) +{ + int i; + if (crl->idp_flags & IDP_ONLYATTR) + return 0; + if (x->ex_flags & EXFLAG_CA) { + if (crl->idp_flags & IDP_ONLYUSER) + return 0; + } else { + if (crl->idp_flags & IDP_ONLYCA) + return 0; + } + *preasons = crl->idp_reasons; + for (i = 0; i < sk_DIST_POINT_num(x->crldp); i++) { + DIST_POINT *dp = sk_DIST_POINT_value(x->crldp, i); + if (crldp_check_crlissuer(dp, crl, crl_score)) { + if (!crl->idp || idp_check_dp(dp->distpoint, crl->idp->distpoint)) { + *preasons &= dp->dp_reasons; + return 1; + } + } + } + if ((!crl->idp || !crl->idp->distpoint) + && (crl_score & CRL_SCORE_ISSUER_NAME)) + return 1; + return 0; +} + +/* + * Retrieve CRL corresponding to current certificate. If deltas enabled try + * to find a delta CRL too + */ + +static int get_crl_delta(X509_STORE_CTX *ctx, + X509_CRL **pcrl, X509_CRL **pdcrl, X509 *x) +{ + int ok; + X509 *issuer = NULL; + int crl_score = 0; + unsigned int reasons; + X509_CRL *crl = NULL, *dcrl = NULL; + STACK_OF(X509_CRL) *skcrl; + X509_NAME *nm = X509_get_issuer_name(x); + reasons = ctx->current_reasons; + ok = get_crl_sk(ctx, &crl, &dcrl, + &issuer, &crl_score, &reasons, ctx->crls); + + if (ok) + goto done; + + /* Lookup CRLs from store */ + + skcrl = ctx->lookup_crls(ctx, nm); + + /* If no CRLs found and a near match from get_crl_sk use that */ + if (!skcrl && crl) + goto done; + + get_crl_sk(ctx, &crl, &dcrl, &issuer, &crl_score, &reasons, skcrl); + + sk_X509_CRL_pop_free(skcrl, X509_CRL_free); + + done: + + /* If we got any kind of CRL use it and return success */ + if (crl) { + ctx->current_issuer = issuer; + ctx->current_crl_score = crl_score; + ctx->current_reasons = reasons; + *pcrl = crl; + *pdcrl = dcrl; + return 1; + } + + return 0; } /* Check CRL validity */ @@ -715,11 +1472,15 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) int ok = 0, chnum, cnum; cnum = ctx->error_depth; chnum = sk_X509_num(ctx->chain) - 1; + /* if we have an alternative CRL issuer cert use that */ + if (ctx->current_issuer) + issuer = ctx->current_issuer; + /* - * Find CRL issuer: if not last certificate then issuer is next + * Else find CRL issuer: if not last certificate then issuer is next * certificate in chain. */ - if (cnum < chnum) + else if (cnum < chnum) issuer = sk_X509_value(ctx->chain, cnum + 1); else { issuer = sk_X509_value(ctx->chain, chnum); @@ -733,11 +1494,46 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) } if (issuer) { - /* Check for cRLSign bit if keyUsage present */ - if ((issuer->ex_flags & EXFLAG_KUSAGE) && - !(issuer->ex_kusage & KU_CRL_SIGN)) { - ctx->error = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN; - ok = ctx->verify_cb(0, ctx); + /* + * Skip most tests for deltas because they have already been done + */ + if (!crl->base_crl_number) { + /* Check for cRLSign bit if keyUsage present */ + if ((issuer->ex_flags & EXFLAG_KUSAGE) && + !(issuer->ex_kusage & KU_CRL_SIGN)) { + ctx->error = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN; + ok = ctx->verify_cb(0, ctx); + if (!ok) + goto err; + } + + if (!(ctx->current_crl_score & CRL_SCORE_SCOPE)) { + ctx->error = X509_V_ERR_DIFFERENT_CRL_SCOPE; + ok = ctx->verify_cb(0, ctx); + if (!ok) + goto err; + } + + if (!(ctx->current_crl_score & CRL_SCORE_SAME_PATH)) { + if (check_crl_path(ctx, ctx->current_issuer) <= 0) { + ctx->error = X509_V_ERR_CRL_PATH_VALIDATION_ERROR; + ok = ctx->verify_cb(0, ctx); + if (!ok) + goto err; + } + } + + if (crl->idp_flags & IDP_INVALID) { + ctx->error = X509_V_ERR_INVALID_EXTENSION; + ok = ctx->verify_cb(0, ctx); + if (!ok) + goto err; + } + + } + + if (!(ctx->current_crl_score & CRL_SCORE_TIME)) { + ok = check_crl_time(ctx, crl, 1); if (!ok) goto err; } @@ -751,6 +1547,14 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) if (!ok) goto err; } else { + int rv; + rv = X509_CRL_check_suiteb(crl, ikey, ctx->param->flags); + if (rv != X509_V_OK) { + ctx->error = rv; + ok = ctx->verify_cb(0, ctx); + if (!ok) + goto err; + } /* Verify CRL signature */ if (X509_CRL_verify(crl, ikey) <= 0) { ctx->error = X509_V_ERR_CRL_SIGNATURE_FAILURE; @@ -761,10 +1565,6 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) } } - ok = check_crl_time(ctx, crl, 1); - if (!ok) - goto err; - ok = 1; err: @@ -775,61 +1575,42 @@ static int check_crl(X509_STORE_CTX *ctx, X509_CRL *crl) /* Check certificate against CRL */ static int cert_crl(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x) { - int idx, ok; - X509_REVOKED rtmp; - STACK_OF(X509_EXTENSION) *exts; - X509_EXTENSION *ext; - /* Look for serial number of certificate in CRL */ - rtmp.serialNumber = X509_get_serialNumber(x); + int ok; + X509_REVOKED *rev; /* - * Sort revoked into serial number order if not already sorted. Do this - * under a lock to avoid race condition. + * The rules changed for this... previously if a CRL contained unhandled + * critical extensions it could still be used to indicate a certificate + * was revoked. This has since been changed since critical extension can + * change the meaning of CRL entries. */ - if (!sk_X509_REVOKED_is_sorted(crl->crl->revoked)) { - CRYPTO_w_lock(CRYPTO_LOCK_X509_CRL); - sk_X509_REVOKED_sort(crl->crl->revoked); - CRYPTO_w_unlock(CRYPTO_LOCK_X509_CRL); + if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) + && (crl->flags & EXFLAG_CRITICAL)) { + ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION; + ok = ctx->verify_cb(0, ctx); + if (!ok) + return 0; } - 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. + * Look for serial number of certificate in CRL If found make sure reason + * is not removeFromCRL. */ - if (idx >= 0) { + if (X509_CRL_get0_by_cert(crl, &rev, x)) { + if (rev->reason == CRL_REASON_REMOVE_FROM_CRL) + return 2; ctx->error = X509_V_ERR_CERT_REVOKED; ok = ctx->verify_cb(0, ctx); if (!ok) return 0; } - if (ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) - return 1; - - /* - * See if we have any critical CRL extensions: since we currently don't - * handle any CRL extensions the CRL must be rejected. This code - * accesses the X509_CRL structure directly: applications shouldn't do - * this. - */ - - exts = crl->crl->extensions; - - for (idx = 0; idx < sk_X509_EXTENSION_num(exts); idx++) { - ext = sk_X509_EXTENSION_value(exts, idx); - if (ext->critical > 0) { - ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION; - ok = ctx->verify_cb(0, ctx); - if (!ok) - return 0; - break; - } - } return 1; } static int check_policy(X509_STORE_CTX *ctx) { int ret; + if (ctx->parent) + return 1; ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain, ctx->param->policies, ctx->param->flags); if (ret == 0) { @@ -849,7 +1630,8 @@ static int check_policy(X509_STORE_CTX *ctx) continue; ctx->current_cert = x; ctx->error = X509_V_ERR_INVALID_POLICY_EXTENSION; - ret = ctx->verify_cb(0, ctx); + if (!ctx->verify_cb(0, ctx)) + return 0; } return 1; } @@ -871,9 +1653,9 @@ static int check_policy(X509_STORE_CTX *ctx) static int check_cert_time(X509_STORE_CTX *ctx, X509 *x) { -#if defined(OPENSSL_SYS_UEFI) - /* Bypass Certificate Time Checking for UEFI version. */ - return 1; +#ifdef OPENSSL_SYS_UEFI + /* Bypass Certificate Time Checking for UEFI version. */ + return 1; #else time_t *ptime; int i; @@ -934,6 +1716,10 @@ static int internal_verify(X509_STORE_CTX *ctx) if (ctx->check_issued(ctx, xi, xi)) xs = xi; else { + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { + xs = xi; + goto check_cert; + } if (n <= 0) { ctx->error = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; ctx->current_cert = xi; @@ -979,6 +1765,7 @@ static int internal_verify(X509_STORE_CTX *ctx) xs->valid = 1; + check_cert: ok = check_cert_time(ctx, xs); if (!ok) goto end; @@ -1001,64 +1788,102 @@ static int internal_verify(X509_STORE_CTX *ctx) return ok; } -int X509_cmp_current_time(ASN1_TIME *ctm) +int X509_cmp_current_time(const ASN1_TIME *ctm) { return X509_cmp_time(ctm, NULL); } -int X509_cmp_time(ASN1_TIME *ctm, time_t *cmp_time) +int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) { char *str; ASN1_TIME atm; long offset; char buff1[24], buff2[24], *p; - int i, j; + int i, j, remaining; p = buff1; - i = ctm->length; + remaining = ctm->length; str = (char *)ctm->data; + /* + * Note that the following (historical) code allows much more slack in the + * time format than RFC5280. In RFC5280, the representation is fixed: + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ if (ctm->type == V_ASN1_UTCTIME) { - if ((i < 11) || (i > 17)) + /* YYMMDDHHMM[SS]Z or YYMMDDHHMM[SS](+-)hhmm */ + int min_length = sizeof("YYMMDDHHMMZ") - 1; + int max_length = sizeof("YYMMDDHHMMSS+hhmm") - 1; + if (remaining < min_length || remaining > max_length) return 0; memcpy(p, str, 10); p += 10; str += 10; + remaining -= 10; } else { - if (i < 13) + /* YYYYMMDDHHMM[SS[.fff]]Z or YYYYMMDDHHMM[SS[.f[f[f]]]](+-)hhmm */ + int min_length = sizeof("YYYYMMDDHHMMZ") - 1; + int max_length = sizeof("YYYYMMDDHHMMSS.fff+hhmm") - 1; + if (remaining < min_length || remaining > max_length) return 0; memcpy(p, str, 12); p += 12; str += 12; + remaining -= 12; } if ((*str == 'Z') || (*str == '-') || (*str == '+')) { *(p++) = '0'; *(p++) = '0'; } else { + /* SS (seconds) */ + if (remaining < 2) + return 0; *(p++) = *(str++); *(p++) = *(str++); - /* Skip any fractional seconds... */ - if (*str == '.') { + remaining -= 2; + /* + * Skip any (up to three) fractional seconds... + * TODO(emilia): in RFC5280, fractional seconds are forbidden. + * Can we just kill them altogether? + */ + if (remaining && *str == '.') { str++; - while ((*str >= '0') && (*str <= '9')) - str++; + remaining--; + for (i = 0; i < 3 && remaining; i++, str++, remaining--) { + if (*str < '0' || *str > '9') + break; + } } } *(p++) = 'Z'; *(p++) = '\0'; - if (*str == 'Z') + /* We now need either a terminating 'Z' or an offset. */ + if (!remaining) + return 0; + if (*str == 'Z') { + if (remaining != 1) + return 0; offset = 0; - else { + } else { + /* (+-)HHMM */ if ((*str != '+') && (*str != '-')) return 0; + /* Historical behaviour: the (+-)hhmm offset is forbidden in RFC5280. */ + if (remaining != 5) + return 0; + if (str[1] < '0' || str[1] > '9' || str[2] < '0' || str[2] > '9' || + str[3] < '0' || str[3] > '9' || str[4] < '0' || str[4] > '9') + return 0; offset = ((str[1] - '0') * 10 + (str[2] - '0')) * 60; offset += (str[3] - '0') * 10 + (str[4] - '0'); if (*str == '-') offset = -offset; } atm.type = ctm->type; + atm.flags = 0; atm.length = sizeof(buff2); atm.data = (unsigned char *)buff2; @@ -1090,24 +1915,28 @@ ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj) return X509_time_adj(s, adj, NULL); } -ASN1_TIME *X509_time_adj(ASN1_TIME *s, long adj, time_t *in_tm) +ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec, time_t *in_tm) +{ + return X509_time_adj_ex(s, 0, offset_sec, in_tm); +} + +ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, + int offset_day, long offset_sec, time_t *in_tm) { time_t t; - int type = -1; if (in_tm) t = *in_tm; else time(&t); - t += adj; - if (s) - type = s->type; - if (type == V_ASN1_UTCTIME) - return ASN1_UTCTIME_set(s, t); - if (type == V_ASN1_GENERALIZEDTIME) - return ASN1_GENERALIZEDTIME_set(s, t); - return ASN1_TIME_set(s, t); + if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING)) { + if (s->type == V_ASN1_UTCTIME) + return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec); + if (s->type == V_ASN1_GENERALIZEDTIME) + return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec); + } + return ASN1_TIME_adj(s, t, offset_day, offset_sec); } int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain) @@ -1151,6 +1980,114 @@ int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain) return 1; } +/* Make a delta CRL as the diff between two full CRLs */ + +X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, + EVP_PKEY *skey, const EVP_MD *md, unsigned int flags) +{ + X509_CRL *crl = NULL; + int i; + STACK_OF(X509_REVOKED) *revs = NULL; + /* CRLs can't be delta already */ + if (base->base_crl_number || newer->base_crl_number) { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_ALREADY_DELTA); + return NULL; + } + /* Base and new CRL must have a CRL number */ + if (!base->crl_number || !newer->crl_number) { + X509err(X509_F_X509_CRL_DIFF, X509_R_NO_CRL_NUMBER); + return NULL; + } + /* Issuer names must match */ + if (X509_NAME_cmp(X509_CRL_get_issuer(base), X509_CRL_get_issuer(newer))) { + X509err(X509_F_X509_CRL_DIFF, X509_R_ISSUER_MISMATCH); + return NULL; + } + /* AKID and IDP must match */ + if (!crl_extension_match(base, newer, NID_authority_key_identifier)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_AKID_MISMATCH); + return NULL; + } + if (!crl_extension_match(base, newer, NID_issuing_distribution_point)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_IDP_MISMATCH); + return NULL; + } + /* Newer CRL number must exceed full CRL number */ + if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0) { + X509err(X509_F_X509_CRL_DIFF, X509_R_NEWER_CRL_NOT_NEWER); + return NULL; + } + /* CRLs must verify */ + if (skey && (X509_CRL_verify(base, skey) <= 0 || + X509_CRL_verify(newer, skey) <= 0)) { + X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_VERIFY_FAILURE); + return NULL; + } + /* Create new CRL */ + crl = X509_CRL_new(); + if (!crl || !X509_CRL_set_version(crl, 1)) + goto memerr; + /* Set issuer name */ + if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer))) + goto memerr; + + if (!X509_CRL_set_lastUpdate(crl, X509_CRL_get_lastUpdate(newer))) + goto memerr; + if (!X509_CRL_set_nextUpdate(crl, X509_CRL_get_nextUpdate(newer))) + goto memerr; + + /* Set base CRL number: must be critical */ + + if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0)) + goto memerr; + + /* + * Copy extensions across from newest CRL to delta: this will set CRL + * number to correct value too. + */ + + for (i = 0; i < X509_CRL_get_ext_count(newer); i++) { + X509_EXTENSION *ext; + ext = X509_CRL_get_ext(newer, i); + if (!X509_CRL_add_ext(crl, ext, -1)) + goto memerr; + } + + /* Go through revoked entries, copying as needed */ + + revs = X509_CRL_get_REVOKED(newer); + + for (i = 0; i < sk_X509_REVOKED_num(revs); i++) { + X509_REVOKED *rvn, *rvtmp; + rvn = sk_X509_REVOKED_value(revs, i); + /* + * Add only if not also in base. TODO: need something cleverer here + * for some more complex CRLs covering multiple CAs. + */ + if (!X509_CRL_get0_by_serial(base, &rvtmp, rvn->serialNumber)) { + rvtmp = X509_REVOKED_dup(rvn); + if (!rvtmp) + goto memerr; + if (!X509_CRL_add0_revoked(crl, rvtmp)) { + X509_REVOKED_free(rvtmp); + goto memerr; + } + } + } + /* TODO: optionally prune deleted entries */ + + if (skey && md && !X509_CRL_sign(crl, skey, md)) + goto memerr; + + return crl; + + memerr: + X509err(X509_F_X509_CRL_DIFF, ERR_R_MALLOC_FAILURE); + if (crl) + X509_CRL_free(crl); + return NULL; +} + int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, @@ -1201,16 +2138,24 @@ STACK_OF(X509) *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx) STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx) { - int i; - X509 *x; - STACK_OF(X509) *chain; - if (!ctx->chain || !(chain = sk_X509_dup(ctx->chain))) + if (!ctx->chain) return NULL; - for (i = 0; i < sk_X509_num(chain); i++) { - x = sk_X509_value(chain, i); - CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); - } - return chain; + return X509_chain_up_ref(ctx->chain); +} + +X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx) +{ + return ctx->current_issuer; +} + +X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx) +{ + return ctx->current_crl; +} + +X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx) +{ + return ctx->parent; } void X509_STORE_CTX_set_cert(X509_STORE_CTX *ctx, X509 *x) @@ -1309,6 +2254,8 @@ X509_STORE_CTX *X509_STORE_CTX_new(void) void X509_STORE_CTX_free(X509_STORE_CTX *ctx) { + if (!ctx) + return; X509_STORE_CTX_cleanup(ctx); OPENSSL_free(ctx); } @@ -1331,7 +2278,11 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, ctx->error_depth = 0; ctx->current_cert = NULL; ctx->current_issuer = NULL; + ctx->current_crl = NULL; + ctx->current_crl_score = 0; + ctx->current_reasons = 0; ctx->tree = NULL; + ctx->parent = NULL; ctx->param = X509_VERIFY_PARAM_new(); @@ -1392,7 +2343,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, if (store && store->get_crl) ctx->get_crl = store->get_crl; else - ctx->get_crl = get_crl; + ctx->get_crl = NULL; if (store && store->check_crl) ctx->check_crl = store->check_crl; @@ -1404,6 +2355,16 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, else ctx->cert_crl = cert_crl; + if (store && store->lookup_certs) + ctx->lookup_certs = store->lookup_certs; + else + ctx->lookup_certs = X509_STORE_get1_certs; + + if (store && store->lookup_crls) + ctx->lookup_crls = store->lookup_crls; + else + ctx->lookup_crls = X509_STORE_get1_crls; + ctx->check_policy = check_policy; /* @@ -1437,7 +2398,8 @@ void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx) if (ctx->cleanup) ctx->cleanup(ctx); if (ctx->param != NULL) { - X509_VERIFY_PARAM_free(ctx->param); + if (ctx->parent == NULL) + X509_VERIFY_PARAM_free(ctx->param); ctx->param = NULL; } if (ctx->tree != NULL) { |
