From d9b8b89bec4480de3a10bdaf9425db371c19145b Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Tue, 29 Dec 2015 12:42:57 -0500 Subject: X509_verify_cert() cleanup Reviewed-by: Richard Levitte --- crypto/x509/x509_err.c | 1 + crypto/x509/x509_vfy.c | 739 ++++++++++++++++++++++++++----------------------- 2 files changed, 397 insertions(+), 343 deletions(-) (limited to 'crypto/x509') diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c index 25f90ea584..668af28cba 100644 --- a/crypto/x509/x509_err.c +++ b/crypto/x509/x509_err.c @@ -71,6 +71,7 @@ static ERR_STRING_DATA X509_str_functs[] = { {ERR_FUNC(X509_F_ADD_CERT_DIR), "add_cert_dir"}, + {ERR_FUNC(X509_F_BUILD_CHAIN), "build_chain"}, {ERR_FUNC(X509_F_BY_FILE_CTRL), "by_file_ctrl"}, {ERR_FUNC(X509_F_CHECK_POLICY), "check_policy"}, {ERR_FUNC(X509_F_DIR_CTRL), "dir_ctrl"}, diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 3acb374a54..9b8803176e 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "internal/cryptlib.h" #include @@ -69,7 +70,7 @@ #include #include #include -#include "internal/x509_int.h" +#include #include "x509_lcl.h" /* CRL score values */ @@ -110,13 +111,15 @@ #define CRL_SCORE_TIME_DELTA 0x002 +static int build_chain(X509_STORE_CTX *ctx); +static int verify_chain(X509_STORE_CTX *ctx); 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_trust(X509_STORE_CTX *ctx, int num_untrusted); static int check_revocation(X509_STORE_CTX *ctx); static int check_cert(X509_STORE_CTX *ctx); static int check_policy(X509_STORE_CTX *ctx); @@ -148,6 +151,11 @@ static int null_callback(int ok, X509_STORE_CTX *e) /* Return 1 is a certificate is self signed */ static int cert_self_signed(X509 *x) { + /* + * FIXME: x509v3_cache_extensions() needs to detect more failures and not + * set EXFLAG_SET when that happens. Especially, if the failures are + * parse errors, rather than memory pressure! + */ X509_check_purpose(x, -1, 0); if (x->ex_flags & EXFLAG_SS) return 1; @@ -180,19 +188,60 @@ static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x) return xtmp; } +static int verify_chain(X509_STORE_CTX *ctx) +{ + int (*cb) (int xok, X509_STORE_CTX *xctx) = ctx->verify_cb; + int err; + int ok; + + /* + * Before either returning with an error, or continuing with CRL checks, + * instantiate chain public key parameters. + */ + if ((ok = build_chain(ctx)) == 0 || + (ok = check_chain_extensions(ctx)) == 0 || + (ok = check_name_constraints(ctx)) == 0 || + (ok = check_id(ctx)) == 0 || 1) + X509_get_pubkey_parameters(NULL, ctx->chain); + if (ok == 0 || (ok = ctx->check_revocation(ctx)) == 0) + return ok; + + err = X509_chain_check_suiteb(&ctx->error_depth, NULL, ctx->chain, + ctx->param->flags); + if (err != X509_V_OK) { + ctx->error = err; + ctx->current_cert = sk_X509_value(ctx->chain, ctx->error_depth); + if ((ok = cb(0, ctx)) == 0) + return ok; + } + + /* Verify chain signatures and expiration times */ + ok = (ctx->verify != NULL) ? ctx->verify(ctx) : internal_verify(ctx); + if (!ok) + return ok; + +#ifndef OPENSSL_NO_RFC3779 + /* RFC 3779 path validation, now that CRL check has been done */ + if ((ok = v3_asid_validate_path(ctx)) == 0) + return ok; + if ((ok = v3_addr_validate_path(ctx)) == 0) + return ok; +#endif + + /* If we get this far evaluate policies */ + if (ctx->param->flags & X509_V_FLAG_POLICY_CHECK) + ok = ctx->check_policy(ctx); + return ok; +} + int X509_verify_cert(X509_STORE_CTX *ctx) { - X509 *x, *xtmp, *xtmp2, *chain_ss = NULL; - int bad_chain = 0; - X509_VERIFY_PARAM *param = ctx->param; - int depth, i, ok = 0; - 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 @@ -202,8 +251,6 @@ int X509_verify_cert(X509_STORE_CTX *ctx) return -1; } - cb = ctx->verify_cb; - /* * first we make sure the chain we are going to build is present and that * the first entry is in place @@ -211,302 +258,12 @@ int X509_verify_cert(X509_STORE_CTX *ctx) 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; + return -1; } X509_up_ref(ctx->cert); - ctx->last_untrusted = 1; - - /* We use a temporary STACK so we can chop and hack at it */ - if (ctx->untrusted != NULL - && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) { - X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); - goto end; - } - - num = sk_X509_num(ctx->chain); - x = sk_X509_value(ctx->chain, num - 1); - depth = param->depth; - - for (;;) { - /* If we have enough, we break */ - if (depth < num) - break; /* FIXME: If this happens, we should take - * note of it and, if appropriate, use the - * X509_V_ERR_CERT_CHAIN_TOO_LONG error code - * later. */ - - /* If we are self signed, we break */ - 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) - goto end; - /* - * 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) { - xtmp = find_issuer(ctx, sktmp, x); - if (xtmp != NULL) { - if (!sk_X509_push(ctx->chain, xtmp)) { - X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE); - goto end; - } - X509_up_ref(xtmp); - (void)sk_X509_delete_ptr(sktmp, xtmp); - ctx->last_untrusted++; - x = xtmp; - num++; - /* - * reparse the full chain for the next one - */ - continue; - } - } - 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. - */ - - 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 { - /* - * extract and save self signed certificate for later use - */ - chain_ss = sk_X509_pop(ctx->chain); - ctx->last_untrusted--; - num--; - j--; - 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; - /* If we are self signed, we break */ - if (cert_self_signed(x)) - break; - ok = ctx->get_issuer(&xtmp, ctx, x); - - if (ok < 0) - goto end; - 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); - ok = 0; - goto done; - } - num++; - } - - /* we now have our chain, lets check it... */ - i = check_trust(ctx); - - /* 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) { - STACK_OF(X509) *chtmp = ctx->chain; - xtmp2 = sk_X509_value(ctx->chain, j - 1); - /* - * Temporarily set chain to NULL so we don't discount - * duplicates: the same certificate could be an untrusted - * CA found in the trusted store. - */ - ctx->chain = NULL; - ok = ctx->get_issuer(&xtmp, ctx, xtmp2); - ctx->chain = chtmp; - 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); - - /* - * 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; - } - } - } - } while (retry); - - /* - * 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; - else - ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; - ctx->current_cert = x; - } else { - - sk_X509_push(ctx->chain, chain_ss); - num++; - ctx->last_untrusted = num; - ctx->current_cert = chain_ss; - ctx->error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; - chain_ss = NULL; - } - - ctx->error_depth = num - 1; - bad_chain = 1; - ok = cb(0, ctx); - if (!ok) - goto end; - } - - /* We have the chain complete: now we need to check its purpose */ - ok = check_chain_extensions(ctx); - - if (!ok) - goto end; - - /* Check name constraints */ - - ok = check_name_constraints(ctx); - - if (!ok) - goto end; - - ok = check_id(ctx); - - if (!ok) - goto end; - - /* We may as well copy down any DSA parameters that are required */ - X509_get_pubkey_parameters(NULL, ctx->chain); - - /* - * Check revocation status: we do this after copying parameters because - * they may be needed for CRL signature verification. - */ - - ok = ctx->check_revocation(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); - else - ok = internal_verify(ctx); - if (!ok) - goto end; + ctx->num_untrusted = 1; -#ifndef OPENSSL_NO_RFC3779 - /* RFC 3779 path validation, now that CRL check has been done */ - ok = v3_asid_validate_path(ctx); - if (!ok) - goto end; - ok = v3_addr_validate_path(ctx); - if (!ok) - goto end; -#endif - - /* If we get this far evaluate policies */ - if (!bad_chain && (ctx->param->flags & X509_V_FLAG_POLICY_CHECK)) - ok = ctx->check_policy(ctx); - if (ok) - goto done; - - end: - X509_get_pubkey_parameters(NULL, ctx->chain); - done: - sk_X509_free(sktmp); - X509_free(chain_ss); - return ok; + return verify_chain(ctx); } /* @@ -618,7 +375,7 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) } /* Check all untrusted certificates */ - for (i = 0; i < ctx->last_untrusted; i++) { + for (i = 0; i == 0 || i < ctx->num_untrusted; i++) { int ret; x = sk_X509_value(ctx->chain, i); if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL) @@ -801,48 +558,66 @@ static int check_id(X509_STORE_CTX *ctx) return 1; } -static int check_trust(X509_STORE_CTX *ctx) +static int check_trust(X509_STORE_CTX *ctx, int num_untrusted) { - int i, ok; + int i, ok = 0; X509 *x = NULL; - int (*cb) (int xok, X509_STORE_CTX *xctx); - cb = ctx->verify_cb; - /* Check all trusted certificates in chain */ - for (i = ctx->last_untrusted; i < sk_X509_num(ctx->chain); i++) { + X509 *mx; + int (*cb) (int xok, X509_STORE_CTX *xctx) = ctx->verify_cb; + int num = sk_X509_num(ctx->chain); + int trust; + + /* + * Check trusted certificates in chain at depth num_untrusted and up. + * Note, that depths 0..num_untrusted-1 may also contain trusted + * certificates, but the caller is expected to have already checked those, + * and wants to incrementally check just any added since. + */ + for (i = num_untrusted; i < num; i++) { x = sk_X509_value(ctx->chain, i); - ok = X509_check_trust(x, ctx->param->trust, 0); + trust = 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 (trust == X509_TRUST_TRUSTED) + goto trusted; + if (trust == X509_TRUST_REJECTED) + goto rejected; } + /* - * If we accept partial chains and have at least one trusted certificate - * return success. + * If we are looking at a trusted certificate, and accept partial chains, + * the chain is PKIX trusted. */ + if (num_untrusted < num) { + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) + goto trusted; + return X509_TRUST_UNTRUSTED; + } + if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) { - X509 *mx; - if (ctx->last_untrusted < sk_X509_num(ctx->chain)) - return X509_TRUST_TRUSTED; + /* + * Last-resort call with no new trusted certificates, check the leaf + * for a direct trust store match. + */ 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 (!mx) + return X509_TRUST_UNTRUSTED; + + /* + * Check explicit auxiliary trust/reject settings. If none are set, + * we'll accept X509_TRUST_UNTRUSTED when not self-signed. + */ + trust = X509_check_trust(mx, ctx->param->trust, 0); + if (trust == X509_TRUST_REJECTED) { + X509_free(mx); + goto rejected; } + + /* Replace leaf with trusted match */ + (void) sk_X509_set(ctx->chain, 0, mx); + X509_free(x); + ctx->num_untrusted = 0; + goto trusted; } /* @@ -850,6 +625,18 @@ static int check_trust(X509_STORE_CTX *ctx) * standard (no issuer cert) etc errors to be indicated. */ return X509_TRUST_UNTRUSTED; + + 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; + return X509_TRUST_UNTRUSTED; + + trusted: + return X509_TRUST_TRUSTED; } static int check_revocation(X509_STORE_CTX *ctx) @@ -1732,9 +1519,8 @@ static int internal_verify(X509_STORE_CTX *ctx) cb = ctx->verify_cb; - n = sk_X509_num(ctx->chain); - ctx->error_depth = n - 1; - n--; + n = sk_X509_num(ctx->chain) - 1; + ctx->error_depth = n; xi = sk_X509_value(ctx->chain, n); if (ctx->check_issued(ctx, xi, xi)) @@ -1756,7 +1542,10 @@ static int internal_verify(X509_STORE_CTX *ctx) } } -/* ctx->error=0; not needed */ + /* + * Do not clear ctx->error=0, it must be "sticky", only the user's callback + * is allowed to reset errors (at its own peril). + */ while (n >= 0) { ctx->error_depth = n; @@ -2269,7 +2058,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, ctx->cert = x509; ctx->untrusted = chain; ctx->crls = NULL; - ctx->last_untrusted = 0; + ctx->num_untrusted = 0; ctx->other_ctx = NULL; ctx->valid = 0; ctx->chain = NULL; @@ -2380,7 +2169,7 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, } return 1; -err: + err: X509_STORE_CTX_cleanup(ctx); return 0; } @@ -2447,7 +2236,7 @@ int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx) int X509_STORE_CTX_get_num_untrusted(X509_STORE_CTX *ctx) { - return ctx->last_untrusted; + return ctx->num_untrusted; } int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name) @@ -2469,3 +2258,267 @@ void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param) X509_VERIFY_PARAM_free(ctx->param); ctx->param = param; } + +static int build_chain(X509_STORE_CTX *ctx) +{ + int (*cb) (int, X509_STORE_CTX *) = ctx->verify_cb; + int num = sk_X509_num(ctx->chain); + X509 *cert = sk_X509_value(ctx->chain, num - 1); + int ss = cert_self_signed(cert); + STACK_OF(X509) *sktmp = NULL; + unsigned int search; + int may_trusted = 1; + int may_alternate = 0; + int trust = X509_TRUST_UNTRUSTED; + int alt_untrusted = 0; + int depth; + int ok = 0; + int i; + + /* Our chain starts with a single untrusted element. */ + OPENSSL_assert(num == 1 && ctx->num_untrusted == num); + +#define S_DOUNTRUSTED (1 << 0) /* Search untrusted chain */ +#define S_DOTRUSTED (1 << 1) /* Search trusted store */ +#define S_DOALTERNATE (1 << 2) /* Retry with pruned alternate chain */ + /* + * Set up search policy, untrusted if possible, trusted-first if enabled. + * If not trusted-first, and alternate chains are not disabled, try + * building an alternate chain if no luck with untrusted first. + */ + search = (ctx->untrusted != NULL) ? S_DOUNTRUSTED : 0; + if (search == 0 || ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) + search |= S_DOTRUSTED; + else if (!(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) + may_alternate = 1; + + /* + * Shallow-copy the stack of untrusted certificates (with TLS, this is + * typically the content of the peer's certificate message) so can make + * multiple passes over it, while free to remove elements as we go. + */ + if (ctx->untrusted && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) { + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* + * Still absurdly large, but arithmetically safe, a lower hard upper bound + * might be reasonable. + */ + if (ctx->param->depth > INT_MAX/2) + ctx->param->depth = INT_MAX/2; + + /* + * Try to Extend the chain until we reach an ultimately trusted issuer. + * Build chains up to one longer the limit, later fail if we hit the limit, + * with an X509_V_ERR_CERT_CHAIN_TOO_LONG error code. + */ + depth = ctx->param->depth + 1; + + while (search != 0) { + X509 *x; + X509 *xtmp = NULL; + + /* + * Look in the trust store if enabled for first lookup, or we've run + * out of untrusted issuers and search here is not disabled. When + * we exceed the depth limit, we simulate absence of a match. + */ + if ((search & S_DOTRUSTED) != 0) { + STACK_OF(X509) *hide = ctx->chain; + + i = num = sk_X509_num(ctx->chain); + if ((search & S_DOALTERNATE) != 0) { + /* + * As high up the chain as we can, look for an alternative + * trusted issuer of an untrusted certificate that currently + * has an untrusted issuer. We use the alt_untrusted variable + * to track how far up the chain we find the first match. It + * is only if and when we find a match, that we prune the chain + * and reset ctx->num_untrusted to the reduced count of + * untrusted certificates. While we're searching for such a + * match (which may never be found), it is neither safe nor + * wise to preemptively modify either the chain or + * ctx->num_untrusted. + * + * Note, like ctx->num_untrusted, alt_untrusted is a count of + * untrusted certificates, not a "depth". + */ + i = alt_untrusted; + } + x = sk_X509_value(ctx->chain, i-1); + + /* Suppress duplicate suppression */ + ctx->chain = NULL; + ok = (depth < num) ? 0 : ctx->get_issuer(&xtmp, ctx, x); + ctx->chain = hide; + + if (ok < 0) { + trust = X509_TRUST_REJECTED; + search = 0; + continue; + } + + if (ok > 0) { + /* + * Alternative trusted issuer for a mid-chain untrusted cert? + * Pop the untrusted cert's successors and retry. We might now + * be able to complete a valid chain via the trust store. Note + * that despite the current trust-store match we might still + * fail complete the chain to a suitable trust-anchor, in which + * case we may prune some more untrusted certificates and try + * again. Thus the S_DOALTERNATE bit may yet be turned on + * again with an even shorter untrusted chain! + */ + if ((search & S_DOALTERNATE) != 0) { + OPENSSL_assert(num > i && i > 0 && ss == 0); + search &= ~S_DOALTERNATE; + for (; num > i; --num) + X509_free(sk_X509_pop(ctx->chain)); + ctx->num_untrusted = num; + } + + /* + * Self-signed untrusted certificates get replaced by their + * trusted matching issuer. Otherwise, grow the chain. + */ + if (ss == 0) { + if (!sk_X509_push(ctx->chain, x = xtmp)) { + X509_free(xtmp); + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + trust = X509_TRUST_REJECTED; + search = 0; + continue; + } + ss = cert_self_signed(x); + } else if (num == ctx->num_untrusted) { + /* + * We have a self-signed certificate that has the same + * subject name (and perhaps keyid and/or serial number) as + * a trust-anchor. We must have an exact match to avoid + * possible impersonation via key substitution etc. + */ + if (X509_cmp(x, xtmp) != 0) { + /* Self-signed untrusted mimic. */ + X509_free(xtmp); + ok = 0; + } else { + X509_free(x); + ctx->num_untrusted = --num; + (void) sk_X509_set(ctx->chain, num, x = xtmp); + } + } + + /* + * We've added a new trusted certificate to the chain, recheck + * trust. If not done, and not self-signed look deeper. + * Whether or not we're doing "trusted first", we no longer + * look for untrusted certificates from the peer's chain. + */ + if (ok) { + OPENSSL_assert(ctx->num_untrusted <= num); + search &= ~S_DOUNTRUSTED; + switch (trust = check_trust(ctx, num)) { + case X509_TRUST_TRUSTED: + case X509_TRUST_REJECTED: + search = 0; + continue; + } + if (ss == 0) + continue; + } + } + + /* + * No dispositive decision, and either self-signed or no match, if + * we were doing untrusted-first, and alt-chains are not disabled, + * do that, by repeatedly losing one untrusted element at a time, + * and trying to extend the shorted chain. + */ + if ((search & S_DOUNTRUSTED) == 0) { + /* Continue search for a trusted issuer of a shorter chain? */ + if ((search & S_DOALTERNATE) != 0 && --alt_untrusted > 0) + continue; + /* Still no luck and no fallbacks left? */ + if (!may_alternate || (search & S_DOALTERNATE) != 0 || + ctx->num_untrusted < 2) + break; + /* Search for a trusted issuer of a shorter chain */ + search |= S_DOALTERNATE; + alt_untrusted = ctx->num_untrusted - 1; + ss = 0; + } + } + + /* + * Extend chain with peer-provided certificates + */ + if ((search & S_DOUNTRUSTED) != 0) { + num = sk_X509_num(ctx->chain); + OPENSSL_assert(num == ctx->num_untrusted); + x = sk_X509_value(ctx->chain, num-1); + xtmp = (depth < num) ? NULL : find_issuer(ctx, sktmp, x); + + /* + * Once we run out of untrusted issuers, we stop looking for more + * and start looking only in the trust store if enabled. + */ + if (xtmp == NULL) { + search &= ~S_DOUNTRUSTED; + if (may_trusted) + search |= S_DOTRUSTED; + continue; + } + + if (!sk_X509_push(ctx->chain, x = xtmp)) { + X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE); + trust = X509_TRUST_REJECTED; + search = 0; + continue; + } + X509_up_ref(x); + ++ctx->num_untrusted; + ss = cert_self_signed(xtmp); + + /* + * Not strictly necessary, but saves cycles looking at the same + * certificates over and over. + */ + (void) sk_X509_delete_ptr(sktmp, x); + } + } + sk_X509_free(sktmp); + + /* + * Last chance to make a trusted chain, check for direct leaf PKIX trust. + */ + if (sk_X509_num(ctx->chain) <= depth) { + if (trust == X509_TRUST_UNTRUSTED && + sk_X509_num(ctx->chain) == ctx->num_untrusted) + trust = check_trust(ctx, 1); + } + + switch (trust) { + case X509_TRUST_TRUSTED: + return 1; + case X509_TRUST_REJECTED: + return 0; + case X509_TRUST_UNTRUSTED: + default: + num = sk_X509_num(ctx->chain); + ctx->current_cert = sk_X509_value(ctx->chain, num - 1); + ctx->error_depth = num-1; + if (num > depth) + ctx->error = X509_V_ERR_CERT_CHAIN_TOO_LONG; + else if (ss && sk_X509_num(ctx->chain) == 1) + ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; + else if (ss) + ctx->error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + else if (ctx->num_untrusted == num) + ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; + else + ctx->error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + return cb(0, ctx); + } +} -- cgit v1.2.3