aboutsummaryrefslogtreecommitdiff
path: root/crypto/x509/x509_vfy.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/x509/x509_vfy.c')
-rw-r--r--crypto/x509/x509_vfy.c338
1 files changed, 295 insertions, 43 deletions
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 7009ae63076e..a2f1dbefe352 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -69,6 +69,7 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/objects.h>
+#include "vpm_int.h"
/* CRL score values */
@@ -113,6 +114,7 @@ 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);
@@ -148,6 +150,40 @@ 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)
{
@@ -205,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) {
@@ -244,7 +296,7 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
*/
i = sk_X509_num(ctx->chain);
x = sk_X509_value(ctx->chain, i - 1);
- if (ctx->check_issued(ctx, x, x)) {
+ if (cert_self_signed(x)) {
/* we have a self signed certificate */
if (sk_X509_num(ctx->chain) == 1) {
/*
@@ -290,9 +342,10 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
if (depth < num)
break;
/* If we are self signed, we break */
- if (ctx->check_issued(ctx, x, x))
+ if (cert_self_signed(x))
break;
ok = ctx->get_issuer(&xtmp, ctx, x);
+
if (ok < 0)
return ok;
if (ok == 0)
@@ -306,14 +359,22 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
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 we haven't got a least one certificate from our store then check
- * if there is an alternative chain that could be used. We only do this
- * if the user hasn't switched off alternate chain checking
+ * 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 (num == ctx->last_untrusted &&
- !(ctx->param->flags & X509_V_FLAG_NO_ALT_CHAINS)) {
+ 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);
@@ -343,8 +404,12 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
}
} 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;
@@ -381,10 +446,7 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
if (!ok)
goto end;
- /* The chain extensions are OK: check trust */
-
- if (param->trust > 0)
- ok = check_trust(ctx);
+ ok = check_id(ctx);
if (!ok)
goto end;
@@ -401,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);
@@ -467,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 */
@@ -667,30 +738,97 @@ static int check_name_constraints(X509_STORE_CTX *ctx)
return 1;
}
-static int check_trust(X509_STORE_CTX *ctx)
+static int check_id_error(X509_STORE_CTX *ctx, int errcode)
{
-#ifdef OPENSSL_NO_CHAIN_VERIFY
+ 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;
-#else
+}
+
+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)
@@ -1409,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;
@@ -1565,6 +1711,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;
@@ -1610,6 +1760,7 @@ static int internal_verify(X509_STORE_CTX *ctx)
xs->valid = 1;
+ check_cert:
ok = check_cert_time(ctx, xs);
if (!ok)
goto end;
@@ -1824,6 +1975,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,
@@ -1874,16 +2133,9 @@ 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)