in crypto/x509/x509_vfy.c [174:508]
int X509_verify_cert(X509_STORE_CTX *ctx) {
X509 *xtmp, *xtmp2, *chain_ss = NULL;
int bad_chain = 0;
X509_VERIFY_PARAM *param = ctx->param;
int i, ok = 0;
int j, retry, trust;
STACK_OF(X509) *sktmp = NULL;
if (ctx->cert == NULL) {
OPENSSL_PUT_ERROR(X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY);
ctx->error = X509_V_ERR_INVALID_CALL;
return -1;
}
if (ctx->chain != NULL) {
// This X509_STORE_CTX has already been used to verify a cert. We
// cannot do another one.
OPENSSL_PUT_ERROR(X509, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
ctx->error = X509_V_ERR_INVALID_CALL;
return -1;
}
if (ctx->param->flags &
(X509_V_FLAG_EXTENDED_CRL_SUPPORT | X509_V_FLAG_USE_DELTAS)) {
// We do not support indirect or delta CRLs. The flags still exist for
// compatibility with bindings libraries, but to ensure we do not
// inadvertently skip a CRL check that the caller expects, fail closed.
OPENSSL_PUT_ERROR(X509, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
ctx->error = X509_V_ERR_INVALID_CALL;
return -1;
}
// first we make sure the chain we are going to build is present and that
// the first entry is in place
ctx->chain = sk_X509_new_null();
if (ctx->chain == NULL || !sk_X509_push(ctx->chain, ctx->cert)) {
ctx->error = X509_V_ERR_OUT_OF_MEM;
goto end;
}
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) {
ctx->error = X509_V_ERR_OUT_OF_MEM;
goto end;
}
int num = (int)sk_X509_num(ctx->chain);
X509 *x = sk_X509_value(ctx->chain, num - 1);
// |param->depth| does not include the leaf certificate or the trust anchor,
// so the maximum size is 2 more.
int max_chain = param->depth >= INT_MAX - 2 ? INT_MAX : param->depth + 2;
for (;;) {
if (num >= max_chain) {
// 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.
break;
}
int is_self_signed;
if (!cert_self_signed(x, &is_self_signed)) {
ctx->error = X509_V_ERR_INVALID_EXTENSION;
goto end;
}
// If we are self signed, we break
if (is_self_signed) {
break;
}
// If asked see if we can find issuer in trusted store first
if (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) {
ok = get_issuer(&xtmp, ctx, x);
if (ok < 0) {
ctx->error = X509_V_ERR_STORE_LOOKUP;
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 (sktmp != NULL) {
xtmp = find_issuer(ctx, sktmp, x);
if (xtmp != NULL) {
if (!sk_X509_push(ctx->chain, xtmp)) {
ctx->error = X509_V_ERR_OUT_OF_MEM;
ok = 0;
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 = (int)sk_X509_num(ctx->chain);
x = sk_X509_value(ctx->chain, i - 1);
int is_self_signed;
if (!cert_self_signed(x, &is_self_signed)) {
ctx->error = X509_V_ERR_INVALID_EXTENSION;
goto end;
}
if (is_self_signed) {
// 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 = 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 = call_verify_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 (num >= max_chain) {
// 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.
break;
}
if (!cert_self_signed(x, &is_self_signed)) {
ctx->error = X509_V_ERR_INVALID_EXTENSION;
goto end;
}
// If we are self signed, we break
if (is_self_signed) {
break;
}
ok = get_issuer(&xtmp, ctx, x);
if (ok < 0) {
ctx->error = X509_V_ERR_STORE_LOOKUP;
goto end;
}
if (ok == 0) {
break;
}
x = xtmp;
if (!sk_X509_push(ctx->chain, x)) {
X509_free(xtmp);
ctx->error = X509_V_ERR_OUT_OF_MEM;
ok = 0;
goto end;
}
// OpenSSL 1.1.1 continuously re-checks for trust and breaks the loop
// as soon as trust has been established. 1.0.2 builds the chain with all
// possible certs first and only checks for trust if the final cert in
// the chain is self-signed.
// This caused additional unanticipated certs to be in the established
// certificate chain, particularly when |X509_V_FLAG_PARTIAL_CHAIN| was
// set. We try checking continuously for trust here for better 1.1.1
// compatibility.
trust = check_trust(ctx);
if (trust == X509_TRUST_TRUSTED || trust == X509_TRUST_REJECTED) {
break;
}
num++;
}
// we now have our chain, lets check it...
trust = check_trust(ctx);
// If explicitly rejected error
if (trust == X509_TRUST_REJECTED) {
ok = 0;
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 (trust != 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 = 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);
// 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 = (int)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 (trust != X509_TRUST_TRUSTED && !bad_chain) {
if (chain_ss == NULL ||
!x509_check_issued_with_callback(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 {
if (!sk_X509_push(ctx->chain, chain_ss)) {
ctx->error = X509_V_ERR_OUT_OF_MEM;
ok = 0;
goto end;
}
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 = call_verify_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;
}
ok = check_id(ctx);
if (!ok) {
goto end;
}
// Check revocation status: we do this after copying parameters because
// they may be needed for CRL signature verification.
ok = check_revocation(ctx);
if (!ok) {
goto end;
}
// At this point, we have a chain and need to verify it
ok = internal_verify(ctx);
if (!ok) {
goto end;
}
// Check name constraints
ok = check_name_constraints(ctx);
if (!ok) {
goto end;
}
// If we get this far evaluate policies
if (!bad_chain && (ctx->param->flags & X509_V_FLAG_POLICY_CHECK)) {
ok = check_policy(ctx);
}
end:
if (sktmp != NULL) {
sk_X509_free(sktmp);
}
if (chain_ss != NULL) {
X509_free(chain_ss);
}
// Safety net, error returns must set ctx->error
if (ok <= 0 && ctx->error == X509_V_OK) {
ctx->error = X509_V_ERR_UNSPECIFIED;
}
return ok;
}