In certain situations the server provided certificate chain may no longer be
valid. However the issuer of the leaf, or some intermediate cert is in fact in the trust store. When building a trust chain if the first attempt fails, then try to see if alternate chains could be constructed that are trusted. RT3637 RT3621 Reviewed-by: Rich Salz <rsalz@openssl.org>
This commit is contained in:
parent
3661bb4e79
commit
6281abc796
1 changed files with 106 additions and 71 deletions
|
@ -187,11 +187,11 @@ static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x)
|
||||||
|
|
||||||
int X509_verify_cert(X509_STORE_CTX *ctx)
|
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;
|
int bad_chain = 0;
|
||||||
X509_VERIFY_PARAM *param = ctx->param;
|
X509_VERIFY_PARAM *param = ctx->param;
|
||||||
int depth, i, ok = 0;
|
int depth, i, ok = 0;
|
||||||
int num;
|
int num, j, retry;
|
||||||
int (*cb) (int xok, X509_STORE_CTX *xctx);
|
int (*cb) (int xok, X509_STORE_CTX *xctx);
|
||||||
STACK_OF(X509) *sktmp = NULL;
|
STACK_OF(X509) *sktmp = NULL;
|
||||||
if (ctx->cert == NULL) {
|
if (ctx->cert == NULL) {
|
||||||
|
@ -276,91 +276,126 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remember how many untrusted certs we have */
|
||||||
|
j = num;
|
||||||
/*
|
/*
|
||||||
* at this point, chain should contain a list of untrusted certificates.
|
* 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
|
* We now need to add at least one trusted one, if possible, otherwise we
|
||||||
* complain.
|
* complain.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
do {
|
||||||
* Examine last certificate in chain and see if it is self signed.
|
/*
|
||||||
*/
|
* Examine last certificate in chain and see if it is self signed.
|
||||||
|
*/
|
||||||
i = sk_X509_num(ctx->chain);
|
i = sk_X509_num(ctx->chain);
|
||||||
x = sk_X509_value(ctx->chain, i - 1);
|
x = sk_X509_value(ctx->chain, i - 1);
|
||||||
if (cert_self_signed(x)) {
|
if (cert_self_signed(x)) {
|
||||||
/* we have a self signed certificate */
|
/* we have a self signed certificate */
|
||||||
if (sk_X509_num(ctx->chain) == 1) {
|
if (sk_X509_num(ctx->chain) == 1) {
|
||||||
/*
|
/*
|
||||||
* We have a single self signed certificate: see if we can find
|
* We have a single self signed certificate: see if we can
|
||||||
* it in the store. We must have an exact match to avoid possible
|
* find it in the store. We must have an exact match to avoid
|
||||||
* impersonation.
|
* possible impersonation.
|
||||||
*/
|
*/
|
||||||
ok = ctx->get_issuer(&xtmp, ctx, x);
|
ok = ctx->get_issuer(&xtmp, ctx, x);
|
||||||
if ((ok <= 0) || X509_cmp(x, xtmp)) {
|
if ((ok <= 0) || X509_cmp(x, xtmp)) {
|
||||||
ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
|
ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
|
||||||
ctx->current_cert = x;
|
ctx->current_cert = x;
|
||||||
ctx->error_depth = i - 1;
|
ctx->error_depth = i - 1;
|
||||||
if (ok == 1)
|
if (ok == 1)
|
||||||
X509_free(xtmp);
|
X509_free(xtmp);
|
||||||
bad_chain = 1;
|
bad_chain = 1;
|
||||||
ok = cb(0, ctx);
|
ok = cb(0, ctx);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
goto end;
|
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 {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We have a match: replace certificate with store version so
|
* extract and save self signed certificate for later use
|
||||||
* we get any trust settings.
|
|
||||||
*/
|
*/
|
||||||
X509_free(x);
|
chain_ss = sk_X509_pop(ctx->chain);
|
||||||
x = xtmp;
|
ctx->last_untrusted--;
|
||||||
(void)sk_X509_set(ctx->chain, i - 1, x);
|
num--;
|
||||||
ctx->last_untrusted = 0;
|
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;
|
||||||
|
/* If we are self signed, we break */
|
||||||
|
if (cert_self_signed(x))
|
||||||
|
break;
|
||||||
|
ok = ctx->get_issuer(&xtmp, ctx, x);
|
||||||
|
|
||||||
/* We now lookup certs from the certificate store */
|
if (ok < 0)
|
||||||
for (;;) {
|
return ok;
|
||||||
/* If we have enough, we break */
|
if (ok == 0)
|
||||||
if (depth < num)
|
break;
|
||||||
break;
|
x = xtmp;
|
||||||
|
if (!sk_X509_push(ctx->chain, x)) {
|
||||||
/* If we are self signed, we break */
|
X509_free(xtmp);
|
||||||
if (cert_self_signed(x))
|
X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
|
||||||
break;
|
return 0;
|
||||||
|
}
|
||||||
ok = ctx->get_issuer(&xtmp, ctx, x);
|
num++;
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we now have our chain, lets check it... */
|
/* we now have our chain, lets check it... */
|
||||||
|
i = check_trust(ctx);
|
||||||
|
|
||||||
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
|
||||||
|
*/
|
||||||
|
retry = 0;
|
||||||
|
if (i != X509_TRUST_TRUSTED
|
||||||
|
&& !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST)) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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--;
|
||||||
|
}
|
||||||
|
retry = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (retry);
|
||||||
|
|
||||||
/* If explicitly rejected error */
|
|
||||||
if (i == X509_TRUST_REJECTED)
|
|
||||||
goto end;
|
|
||||||
/*
|
/*
|
||||||
* If not explicitly trusted then indicate error unless it's a single
|
* If not explicitly trusted then indicate error unless it's a single
|
||||||
* self signed certificate in which case we've indicated an error already
|
* self signed certificate in which case we've indicated an error already
|
||||||
|
|
Loading…
Reference in a new issue