-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to. The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code. The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * "This product includes cryptographic software written by
- * Eric Young (eay@cryptsoft.com)"
- * The word 'cryptographic' can be left out if the rouines from the library
- * being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- * the apps directory (application code) you must include an acknowledgement:
- * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
*
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed. i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include <errno.h>
#include <limits.h>
+#include "crypto/ctype.h"
#include "internal/cryptlib.h"
#include <openssl/crypto.h>
-#include <openssl/lhash.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/asn1.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/objects.h>
-#include <internal/dane.h>
-#include <internal/x509_int.h>
-#include "x509_lcl.h"
+#include "internal/dane.h"
+#include "crypto/x509.h"
+#include "x509_local.h"
+
+DEFINE_STACK_OF(GENERAL_NAME)
+DEFINE_STACK_OF(DIST_POINT)
+DEFINE_STACK_OF_STRING()
/* CRL score values */
return ok;
}
-/* Return 1 is a certificate is self signed */
-static int cert_self_signed(X509 *x)
+/*-
+ * Return 1 if given cert is considered self-signed, 0 if not, or -1 on error.
+ * This actually verifies self-signedness only if requested.
+ * It calls X509v3_cache_extensions()
+ * to match issuer and subject names (i.e., the cert being self-issued) and any
+ * present authority key identifier to match the subject key identifier, etc.
+ */
+int X509_self_signed(X509 *cert, int verify_signature)
{
- /*
- * 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;
- else
+ EVP_PKEY *pkey;
+
+ if ((pkey = X509_get0_pubkey(cert)) == NULL) { /* handles cert == NULL */
+ X509err(0, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY);
+ return -1;
+ }
+ if (!x509v3_cache_extensions(cert))
+ return -1;
+ if ((cert->ex_flags & EXFLAG_SS) == 0)
return 0;
+ if (!verify_signature)
+ return 1;
+ return X509_verify(cert, pkey);
}
/* 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;
xtmp = sk_X509_value(certs, i);
if (!X509_cmp(xtmp, x))
break;
+ xtmp = NULL;
}
- if (i < sk_X509_num(certs))
- X509_up_ref(xtmp);
- else
+ if (xtmp != NULL && !X509_up_ref(xtmp))
xtmp = NULL;
sk_X509_pop_free(certs, X509_free);
return xtmp;
if ((ok = build_chain(ctx)) == 0 ||
(ok = check_chain_extensions(ctx)) == 0 ||
(ok = check_auth_level(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)
if (!ok)
return ok;
+ if ((ok = check_name_constraints(ctx)) == 0)
+ 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)
+ if ((ok = X509v3_asid_validate_path(ctx)) == 0)
return ok;
- if ((ok = v3_addr_validate_path(ctx)) == 0)
+ if ((ok = X509v3_addr_validate_path(ctx)) == 0)
return ok;
#endif
int X509_verify_cert(X509_STORE_CTX *ctx)
{
SSL_DANE *dane = ctx->dane;
+ int ret;
if (ctx->cert == NULL) {
X509err(X509_F_X509_VERIFY_CERT, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY);
+ ctx->error = X509_V_ERR_INVALID_CALL;
return -1;
}
* cannot do another one.
*/
X509err(X509_F_X509_VERIFY_CERT, 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
- */
- 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);
+ if (!X509_add_cert_new(&ctx->chain, ctx->cert, X509_ADD_FLAG_UP_REF)) {
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return -1;
}
- X509_up_ref(ctx->cert);
ctx->num_untrusted = 1;
/* If the peer's public key is too weak, we can stop early. */
!verify_cb_cert(ctx, ctx->cert, 0, X509_V_ERR_EE_KEY_TOO_SMALL))
return 0;
+ if (DANETLS_ENABLED(dane))
+ ret = dane_verify(ctx);
+ else
+ ret = verify_chain(ctx);
+
/*
- * If dane->trecs is an empty stack, we'll fail, since the user enabled
- * DANE. If none of the TLSA records were usable, and it makes sense to
- * keep going with an unauthenticated handshake, they can handle that in
- * the verify callback, or not set SSL_VERIFY_PEER.
+ * Safety-net. If we are returning an error, we must also set ctx->error,
+ * so that the chain is not considered verified should the error be ignored
+ * (e.g. TLS with SSL_VERIFY_NONE).
*/
- if (DANETLS_ENABLED(dane))
- return dane_verify(ctx);
- return verify_chain(ctx);
+ if (ret <= 0 && ctx->error == X509_V_OK)
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ return ret;
}
/*
static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
{
int i;
+ X509 *issuer, *rv = NULL;
for (i = 0; i < sk_X509_num(sk); i++) {
- X509 *issuer = sk_X509_value(sk, i);
-
- if (!ctx->check_issued(ctx, x, issuer))
- continue;
- if (x509_check_cert_time(ctx, issuer, -1))
- return issuer;
+ issuer = sk_X509_value(sk, i);
+ /*
+ * Below check 'issuer != x' is an optimization and safety precaution:
+ * Candidate issuer cert cannot be the same as the subject cert 'x'.
+ */
+ if (issuer != x && ctx->check_issued(ctx, x, issuer)) {
+ rv = issuer;
+ if (x509_check_cert_time(ctx, rv, -1))
+ break;
+ }
}
- return NULL;
+ return rv;
}
-/* Given a possible certificate and issuer check them */
-
+/*
+ * Check that the given certificate 'x' is issued by the certificate 'issuer'
+ * and the issuer is not yet in ctx->chain, where the exceptional case
+ * that 'x' is self-issued and ctx->chain has just one element is allowed.
+ */
static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
{
- int ret;
- if (x == issuer)
- return cert_self_signed(x);
- ret = X509_check_issued(issuer, x);
- if (ret == X509_V_OK) {
+ if (x509_likely_issued(issuer, x) != X509_V_OK)
+ return 0;
+ if ((x->ex_flags & EXFLAG_SI) == 0 || sk_X509_num(ctx->chain) != 1) {
int i;
X509 *ch;
- /* Special case: single self signed certificate */
- if (cert_self_signed(x) && sk_X509_num(ctx->chain) == 1)
- return 1;
+
for (i = 0; i < sk_X509_num(ctx->chain); i++) {
ch = sk_X509_value(ctx->chain, i);
- if (ch == issuer || !X509_cmp(ch, issuer)) {
- ret = X509_V_ERR_PATH_LOOP;
- break;
- }
+ if (ch == issuer || X509_cmp(ch, issuer) == 0)
+ return 0;
}
}
-
- return (ret == X509_V_OK);
+ return 1;
}
/* Alternative lookup method: look from a STACK stored in other_ctx */
-
static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
{
*issuer = find_issuer(ctx, ctx->other_ctx, x);
- if (*issuer) {
- X509_up_ref(*issuer);
- return 1;
- } else
- return 0;
+
+ if (*issuer == NULL || !X509_up_ref(*issuer))
+ goto err;
+
+ return 1;
+
+ err:
+ *issuer = NULL;
+ return 0;
}
-static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm)
+static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx,
+ const X509_NAME *nm)
{
STACK_OF(X509) *sk = NULL;
X509 *x;
int i;
+
for (i = 0; i < sk_X509_num(ctx->other_ctx); i++) {
x = sk_X509_value(ctx->other_ctx, i);
if (X509_NAME_cmp(nm, X509_get_subject_name(x)) == 0) {
- if (sk == NULL)
- sk = sk_X509_new_null();
- if (sk == NULL || sk_X509_push(sk, x) == 0) {
+ if (!X509_add_cert_new(&sk, x, X509_ADD_FLAG_UP_REF)) {
sk_X509_pop_free(sk, X509_free);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return NULL;
}
- X509_up_ref(x);
}
}
return sk;
} 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;
}
for (i = 0; i < num; i++) {
int ret;
+
x = sk_X509_value(ctx->chain, i);
if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL)
&& (x->ex_flags & EXFLAG_CRITICAL)) {
ret = 1;
break;
}
+ /*
+ * Do the following set of checks only if strict checking is requrested
+ * and not for self-issued (including self-signed) EE (non-CA) certs
+ * because RFC 5280 does not apply to them according RFC 6818 section 2.
+ */
+ if ((ctx->param->flags & X509_V_FLAG_X509_STRICT) != 0
+ && num > 1) { /*
+ * this should imply
+ * !(i == 0 && (x->ex_flags & EXFLAG_CA) == 0
+ * && (x->ex_flags & EXFLAG_SI) != 0)
+ */
+ /* Check Basic Constraints according to RFC 5280 section 4.2.1.9 */
+ if (x->ex_pathlen != -1) {
+ if ((x->ex_flags & EXFLAG_CA) == 0)
+ ctx->error = X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA;
+ if ((x->ex_kusage & KU_KEY_CERT_SIGN) == 0)
+ ctx->error = X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN;
+ }
+ if ((x->ex_flags & EXFLAG_CA) != 0
+ && (x->ex_flags & EXFLAG_BCONS) != 0
+ && (x->ex_flags & EXFLAG_BCONS_CRITICAL) == 0)
+ ctx->error = X509_V_ERR_CA_BCONS_NOT_CRITICAL;
+ /* Check Key Usage according to RFC 5280 section 4.2.1.3 */
+ if ((x->ex_flags & EXFLAG_CA) != 0) {
+ if ((x->ex_flags & EXFLAG_KUSAGE) == 0)
+ ctx->error = X509_V_ERR_CA_CERT_MISSING_KEY_USAGE;
+ } else {
+ if ((x->ex_kusage & KU_KEY_CERT_SIGN) != 0)
+ ctx->error = X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA;
+ }
+ /* Check issuer is non-empty acc. to RFC 5280 section 4.1.2.4 */
+ if (X509_NAME_entry_count(X509_get_issuer_name(x)) == 0)
+ ctx->error = X509_V_ERR_ISSUER_NAME_EMPTY;
+ /* Check subject is non-empty acc. to RFC 5280 section 4.1.2.6 */
+ if (((x->ex_flags & EXFLAG_CA) != 0
+ || (x->ex_kusage & KU_CRL_SIGN) != 0
+ || x->altname == NULL
+ ) && X509_NAME_entry_count(X509_get_subject_name(x)) == 0)
+ ctx->error = X509_V_ERR_SUBJECT_NAME_EMPTY;
+ if (X509_NAME_entry_count(X509_get_subject_name(x)) == 0
+ && x->altname != NULL
+ && (x->ex_flags & EXFLAG_SAN_CRITICAL) == 0)
+ ctx->error = X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL;
+ /* Check SAN is non-empty according to RFC 5280 section 4.2.1.6 */
+ if (x->altname != NULL && sk_GENERAL_NAME_num(x->altname) <= 0)
+ ctx->error = X509_V_ERR_EMPTY_SUBJECT_ALT_NAME;
+ /* TODO add more checks on SAN entries */
+ /* Check sig alg consistency acc. to RFC 5280 section 4.1.1.2 */
+ if (X509_ALGOR_cmp(&x->sig_alg, &x->cert_info.signature) != 0)
+ ctx->error = X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY;
+ if (x->akid != NULL && (x->ex_flags & EXFLAG_AKID_CRITICAL) != 0)
+ ctx->error = X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL;
+ if (x->skid != NULL && (x->ex_flags & EXFLAG_SKID_CRITICAL) != 0)
+ ctx->error = X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL;
+ if (X509_get_version(x) >= 2) { /* at least X.509v3 */
+ /* Check AKID presence acc. to RFC 5280 section 4.2.1.1 */
+ if (i + 1 < num /*
+ * this means not last cert in chain,
+ * taken as "generated by conforming CAs"
+ */
+ && (x->akid == NULL || x->akid->keyid == NULL))
+ ctx->error = X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER;
+ /* Check SKID presence acc. to RFC 5280 section 4.2.1.2 */
+ if ((x->ex_flags & EXFLAG_CA) != 0 && x->skid == NULL)
+ ctx->error = X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER;
+ } else {
+ if (sk_X509_EXTENSION_num(X509_get0_extensions(x)) > 0)
+ ctx->error = X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3;
+ }
+ }
+ if (ctx->error != X509_V_OK)
+ ret = 0;
if (ret == 0 && !verify_cb_cert(ctx, x, i, X509_V_OK))
return 0;
/* check_purpose() makes the callback as needed */
if (purpose > 0 && !check_purpose(ctx, x, purpose, i, must_be_ca))
return 0;
- /* Check pathlen if not self issued */
- if ((i > 1) && !(x->ex_flags & EXFLAG_SI)
- && (x->ex_pathlen != -1)
- && (plen > (x->ex_pathlen + proxy_path_length + 1))) {
+ /* Check pathlen */
+ if ((i > 1) && (x->ex_pathlen != -1)
+ && (plen > (x->ex_pathlen + proxy_path_length))) {
if (!verify_cb_cert(ctx, x, i, X509_V_ERR_PATH_LENGTH_EXCEEDED))
return 0;
}
- /* Increment path length if not self issued */
- if (!(x->ex_flags & EXFLAG_SI))
+ /* Increment path length if not a self-issued intermediate CA */
+ if (i > 0 && (x->ex_flags & EXFLAG_SI) == 0)
plen++;
/*
* If this certificate is a proxy certificate, the next certificate
* the next certificate must be a CA certificate.
*/
if (x->ex_flags & EXFLAG_PROXY) {
- if (x->ex_pcpathlen != -1 && i > x->ex_pcpathlen) {
- if (!verify_cb_cert(ctx, x, i,
- X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED))
- return 0;
+ /*
+ * RFC3820, 4.1.3 (b)(1) stipulates that if pCPathLengthConstraint
+ * is less than max_path_length, the former should be copied to
+ * the latter, and 4.1.4 (a) stipulates that max_path_length
+ * should be verified to be larger than zero and decrement it.
+ *
+ * Because we're checking the certs in the reverse order, we start
+ * with verifying that proxy_path_length isn't larger than pcPLC,
+ * and copy the latter to the former if it is, and finally,
+ * increment proxy_path_length.
+ */
+ if (x->ex_pcpathlen != -1) {
+ if (proxy_path_length > x->ex_pcpathlen) {
+ if (!verify_cb_cert(ctx, x, i,
+ X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED))
+ return 0;
+ }
+ proxy_path_length = x->ex_pcpathlen;
}
proxy_path_length++;
must_be_ca = 0;
return 1;
}
+static int has_san_id(X509 *x, int gtype)
+{
+ int i;
+ int ret = 0;
+ GENERAL_NAMES *gs = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
+
+ if (gs == NULL)
+ return 0;
+
+ for (i = 0; i < sk_GENERAL_NAME_num(gs); i++) {
+ GENERAL_NAME *g = sk_GENERAL_NAME_value(gs, i);
+
+ if (g->type == gtype) {
+ ret = 1;
+ break;
+ }
+ }
+ GENERAL_NAMES_free(gs);
+ return ret;
+}
+
static int check_name_constraints(X509_STORE_CTX *ctx)
{
int i;
X509 *x = sk_X509_value(ctx->chain, i);
int j;
- /* Ignore self issued certs unless last in chain */
+ /* Ignore self-issued certs unless last in chain */
if (i && (x->ex_flags & EXFLAG_SI))
continue;
+
+ /*
+ * Proxy certificates policy has an extra constraint, where the
+ * certificate subject MUST be the issuer with a single CN entry
+ * added.
+ * (RFC 3820: 3.4, 4.1.3 (a)(4))
+ */
+ if (x->ex_flags & EXFLAG_PROXY) {
+ X509_NAME *tmpsubject = X509_get_subject_name(x);
+ X509_NAME *tmpissuer = X509_get_issuer_name(x);
+ X509_NAME_ENTRY *tmpentry = NULL;
+ int last_object_nid = 0;
+ int err = X509_V_OK;
+ int last_object_loc = X509_NAME_entry_count(tmpsubject) - 1;
+
+ /* Check that there are at least two RDNs */
+ if (last_object_loc < 1) {
+ err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION;
+ goto proxy_name_done;
+ }
+
+ /*
+ * Check that there is exactly one more RDN in subject as
+ * there is in issuer.
+ */
+ if (X509_NAME_entry_count(tmpsubject)
+ != X509_NAME_entry_count(tmpissuer) + 1) {
+ err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION;
+ goto proxy_name_done;
+ }
+
+ /*
+ * Check that the last subject component isn't part of a
+ * multivalued RDN
+ */
+ if (X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject,
+ last_object_loc))
+ == X509_NAME_ENTRY_set(X509_NAME_get_entry(tmpsubject,
+ last_object_loc - 1))) {
+ err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION;
+ goto proxy_name_done;
+ }
+
+ /*
+ * Check that the last subject RDN is a commonName, and that
+ * all the previous RDNs match the issuer exactly
+ */
+ tmpsubject = X509_NAME_dup(tmpsubject);
+ if (tmpsubject == NULL) {
+ X509err(X509_F_CHECK_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
+ return 0;
+ }
+
+ tmpentry =
+ X509_NAME_delete_entry(tmpsubject, last_object_loc);
+ last_object_nid =
+ OBJ_obj2nid(X509_NAME_ENTRY_get_object(tmpentry));
+
+ if (last_object_nid != NID_commonName
+ || X509_NAME_cmp(tmpsubject, tmpissuer) != 0) {
+ err = X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION;
+ }
+
+ X509_NAME_ENTRY_free(tmpentry);
+ X509_NAME_free(tmpsubject);
+
+ proxy_name_done:
+ if (err != X509_V_OK
+ && !verify_cb_cert(ctx, x, i, err))
+ return 0;
+ }
+
/*
* Check against constraints for all certificates higher in chain
* including trust anchor. Trust anchor not strictly speaking needed
if (nc) {
int rv = NAME_CONSTRAINTS_check(x, nc);
- if (rv != X509_V_OK && !verify_cb_cert(ctx, x, i, rv))
+ /* If EE certificate check commonName too */
+ if (rv == X509_V_OK && i == 0
+ && (ctx->param->hostflags
+ & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT) == 0
+ && ((ctx->param->hostflags
+ & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) != 0
+ || !has_san_id(x, GEN_DNS)))
+ rv = NAME_CONSTRAINTS_check_CN(x, nc);
+
+ switch (rv) {
+ case X509_V_OK:
+ break;
+ case X509_V_ERR_OUT_OF_MEM:
return 0;
+ default:
+ if (!verify_cb_cert(ctx, x, i, rv))
+ return 0;
+ break;
+ }
}
}
}
ctx->current_crl_score = 0;
ctx->current_reasons = 0;
+ if (x->ex_flags & EXFLAG_PROXY)
+ return 1;
+
while (ctx->current_reasons != CRLDP_ALL_REASONS) {
unsigned int last_reasons = ctx->current_reasons;
crl = NULL;
dcrl = NULL;
/*
- * If reasons not updated we wont get anywhere by another iteration,
+ * If reasons not updated we won't get anywhere by another iteration,
* so exit loop.
*/
if (last_reasons == ctx->current_reasons) {
else
ptime = NULL;
- i = X509_cmp_time(X509_CRL_get_lastUpdate(crl), ptime);
+ i = X509_cmp_time(X509_CRL_get0_lastUpdate(crl), ptime);
if (i == 0) {
if (!notify)
return 0;
return 0;
}
- if (X509_CRL_get_nextUpdate(crl)) {
- i = X509_cmp_time(X509_CRL_get_nextUpdate(crl), ptime);
+ if (X509_CRL_get0_nextUpdate(crl)) {
+ i = X509_cmp_time(X509_CRL_get0_nextUpdate(crl), ptime);
if (i == 0) {
if (!notify)
crl = sk_X509_CRL_value(crls, i);
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;
+ if (crl_score < best_score || crl_score == 0)
+ continue;
+ /* If current CRL is equivalent use it if it is newer */
+ if (crl_score == best_score && best_crl != NULL) {
+ int day, sec;
+ if (ASN1_TIME_diff(&day, &sec, X509_CRL_get0_lastUpdate(best_crl),
+ X509_CRL_get0_lastUpdate(crl)) == 0)
+ continue;
+ /*
+ * ASN1_TIME_diff never returns inconsistent signs for |day|
+ * and |sec|.
+ */
+ if (day <= 0 && sec <= 0)
+ continue;
}
+ best_crl = crl;
+ best_crl_issuer = crl_issuer;
+ best_score = crl_score;
+ best_reasons = reasons;
}
if (best_crl) {
X509 **pissuer, int *pcrl_score)
{
X509 *crl_issuer = NULL;
- X509_NAME *cnm = X509_CRL_get_issuer(crl);
+ const X509_NAME *cnm = X509_CRL_get_issuer(crl);
int cidx = ctx->error_depth;
int i;
/* Don't allow recursive CRL path validation */
if (ctx->parent)
return 0;
- if (!X509_STORE_CTX_init(&crl_ctx, ctx->ctx, x, ctx->untrusted))
+ if (!X509_STORE_CTX_init(&crl_ctx, ctx->store, x, ctx->untrusted))
return -1;
crl_ctx.crls = ctx->crls;
static int crldp_check_crlissuer(DIST_POINT *dp, X509_CRL *crl, int crl_score)
{
int i;
- X509_NAME *nm = X509_CRL_get_issuer(crl);
+ const 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);
unsigned int reasons;
X509_CRL *crl = NULL, *dcrl = NULL;
STACK_OF(X509_CRL) *skcrl;
- X509_NAME *nm = X509_get_issuer_name(x);
+ const X509_NAME *nm = X509_get_issuer_name(x);
reasons = ctx->current_reasons;
ok = get_crl_sk(ctx, &crl, &dcrl,
int cnum = ctx->error_depth;
int chnum = sk_X509_num(ctx->chain) - 1;
- /* if we have an alternative CRL issuer cert use that */
+ /* If we have an alternative CRL issuer cert use that */
if (ctx->current_issuer)
issuer = ctx->current_issuer;
/*
issuer = sk_X509_value(ctx->chain, cnum + 1);
else {
issuer = sk_X509_value(ctx->chain, chnum);
- /* If not self signed, can't check signature */
+ /* If not self-issued, can't check signature */
if (!ctx->check_issued(ctx, issuer, issuer) &&
!verify_cb_crl(ctx, X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER))
return 0;
*/
if (ctx->bare_ta_signed && !sk_X509_push(ctx->chain, NULL)) {
X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain,
if (ret == X509_PCY_TREE_INTERNAL) {
X509err(X509_F_CHECK_POLICY, ERR_R_MALLOC_FAILURE);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
/* Invalid or inconsistent extensions */
if (ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY) {
ctx->current_cert = NULL;
- ctx->error = X509_V_OK;
+ /*
+ * Verification errors need to be "sticky", a callback may have allowed
+ * an SSL handshake to continue despite an error, and we must then
+ * remain in an error state. Therefore, we MUST NOT clear earlier
+ * verification errors by setting the error to X509_V_OK.
+ */
if (!ctx->verify_cb(2, ctx))
return 0;
}
else
ptime = NULL;
- i = X509_cmp_time(X509_get_notBefore(x), ptime);
+ i = X509_cmp_time(X509_get0_notBefore(x), ptime);
if (i >= 0 && depth < 0)
return 0;
if (i == 0 && !verify_cb_cert(ctx, x, depth,
if (i > 0 && !verify_cb_cert(ctx, x, depth, X509_V_ERR_CERT_NOT_YET_VALID))
return 0;
- i = X509_cmp_time(X509_get_notAfter(x), ptime);
+ i = X509_cmp_time(X509_get0_notAfter(x), ptime);
if (i <= 0 && depth < 0)
return 0;
if (i == 0 && !verify_cb_cert(ctx, x, depth,
return 1;
}
+/* verify the issuer signatures and cert times of ctx->chain */
static int internal_verify(X509_STORE_CTX *ctx)
{
int n = sk_X509_num(ctx->chain) - 1;
if (ctx->bare_ta_signed) {
xs = xi;
xi = NULL;
- goto check_cert;
+ goto check_cert_time;
}
if (ctx->check_issued(ctx, xi, xi))
- xs = xi;
+ xs = xi; /* the typical case: last cert in the chain is self-issued */
else {
if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) {
xs = xi;
- goto check_cert;
+ goto check_cert_time;
}
if (n <= 0)
return verify_cb_cert(ctx, xi, 0,
* is allowed to reset errors (at its own peril).
*/
while (n >= 0) {
- EVP_PKEY *pkey;
-
/*
- * Skip signature check for self signed certificates unless explicitly
- * asked for. It doesn't add any security and just wastes time. If
- * the issuer's public key is unusable, report the issuer certificate
- * and its depth (rather than the depth of the subject).
+ * For each iteration of this loop:
+ * n is the subject depth
+ * xs is the subject cert, for which the signature is to be checked
+ * xi is the supposed issuer cert containing the public key to use
+ * Initially xs == xi if the last cert in the chain is self-issued.
+ *
+ * Skip signature check for self-signed certificates unless explicitly
+ * asked for because it does not add any security and just wastes time.
*/
- if (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)) {
+ if (xs != xi || ((ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)
+ && (xi->ex_flags & EXFLAG_SS) != 0)) {
+ EVP_PKEY *pkey;
+ /*
+ * If the issuer's public key is not available or its key usage
+ * does not support issuing the subject cert, report the issuer
+ * cert and its depth (rather than n, the depth of the subject).
+ */
+ int issuer_depth = n + (xs == xi ? 0 : 1);
+ /*
+ * According to https://tools.ietf.org/html/rfc5280#section-6.1.4
+ * step (n) we must check any given key usage extension in a CA cert
+ * when preparing the verification of a certificate issued by it.
+ * According to https://tools.ietf.org/html/rfc5280#section-4.2.1.3
+ * we must not verify a certifiate signature if the key usage of the
+ * CA certificate that issued the certificate prohibits signing.
+ * In case the 'issuing' certificate is the last in the chain and is
+ * not a CA certificate but a 'self-issued' end-entity cert (i.e.,
+ * xs == xi && !(xi->ex_flags & EXFLAG_CA)) RFC 5280 does not apply
+ * (see https://tools.ietf.org/html/rfc6818#section-2) and thus
+ * we are free to ignore any key usage restrictions on such certs.
+ */
+ int ret = xs == xi && (xi->ex_flags & EXFLAG_CA) == 0
+ ? X509_V_OK : x509_signing_allowed(xi, xs);
+
+ if (ret != X509_V_OK && !verify_cb_cert(ctx, xi, issuer_depth, ret))
+ return 0;
if ((pkey = X509_get0_pubkey(xi)) == NULL) {
- if (!verify_cb_cert(ctx, xi, xi != xs ? n+1 : n,
- X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY))
+ ret = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+ if (!verify_cb_cert(ctx, xi, issuer_depth, ret))
return 0;
} else if (X509_verify(xs, pkey) <= 0) {
- if (!verify_cb_cert(ctx, xs, n,
- X509_V_ERR_CERT_SIGNATURE_FAILURE))
+ ret = X509_V_ERR_CERT_SIGNATURE_FAILURE;
+ if (!verify_cb_cert(ctx, xs, n, ret))
return 0;
}
}
- check_cert:
+ check_cert_time:
/* Calls verify callback as needed */
if (!x509_check_cert_time(ctx, xs, n))
return 0;
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, remaining;
-
- p = buff1;
- remaining = ctm->length;
- str = (char *)ctm->data;
+ static const size_t utctime_length = sizeof("YYMMDDHHMMSSZ") - 1;
+ static const size_t generalizedtime_length = sizeof("YYYYMMDDHHMMSSZ") - 1;
+ ASN1_TIME *asn1_cmp_time = NULL;
+ int i, day, sec, ret = 0;
+#ifdef CHARSET_EBCDIC
+ const char upper_z = 0x5A;
+#else
+ const char upper_z = 'Z';
+#endif
/*
- * Note that the following (historical) code allows much more slack in the
- * time format than RFC5280. In RFC5280, the representation is fixed:
+ * Note that ASN.1 allows much more slack in the time format than RFC5280.
+ * In RFC5280, the representation is fixed:
* UTCTime: YYMMDDHHMMSSZ
* GeneralizedTime: YYYYMMDDHHMMSSZ
+ *
+ * We do NOT currently enforce the following RFC 5280 requirement:
+ * "CAs conforming to this profile MUST always encode certificate
+ * validity dates through the year 2049 as UTCTime; certificate validity
+ * dates in 2050 or later MUST be encoded as GeneralizedTime."
*/
- if (ctm->type == V_ASN1_UTCTIME) {
- /* 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)
+ switch (ctm->type) {
+ case V_ASN1_UTCTIME:
+ if (ctm->length != (int)(utctime_length))
return 0;
- memcpy(p, str, 10);
- p += 10;
- str += 10;
- remaining -= 10;
- } else {
- /* 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)
+ break;
+ case V_ASN1_GENERALIZEDTIME:
+ if (ctm->length != (int)(generalizedtime_length))
return 0;
- memcpy(p, str, 12);
- p += 12;
- str += 12;
- remaining -= 12;
+ break;
+ default:
+ return 0;
}
- if ((*str == 'Z') || (*str == '-') || (*str == '+')) {
- *(p++) = '0';
- *(p++) = '0';
- } else {
- /* SS (seconds) */
- if (remaining < 2)
+ /**
+ * Verify the format: the ASN.1 functions we use below allow a more
+ * flexible format than what's mandated by RFC 5280.
+ * Digit and date ranges will be verified in the conversion methods.
+ */
+ for (i = 0; i < ctm->length - 1; i++) {
+ if (!ascii_isdigit(ctm->data[i]))
return 0;
- *(p++) = *(str++);
- *(p++) = *(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++;
- remaining--;
- for (i = 0; i < 3 && remaining; i++, str++, remaining--) {
- if (*str < '0' || *str > '9')
- break;
- }
- }
-
}
- *(p++) = 'Z';
- *(p++) = '\0';
-
- /* We now need either a terminating 'Z' or an offset. */
- if (!remaining)
+ if (ctm->data[ctm->length - 1] != upper_z)
return 0;
- if (*str == 'Z') {
- if (remaining != 1)
- return 0;
- offset = 0;
- } 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;
- if (X509_time_adj(&atm, offset * 60, cmp_time) == NULL)
- return 0;
+ /*
+ * There is ASN1_UTCTIME_cmp_time_t but no
+ * ASN1_GENERALIZEDTIME_cmp_time_t or ASN1_TIME_cmp_time_t,
+ * so we go through ASN.1
+ */
+ asn1_cmp_time = X509_time_adj(NULL, 0, cmp_time);
+ if (asn1_cmp_time == NULL)
+ goto err;
+ if (!ASN1_TIME_diff(&day, &sec, ctm, asn1_cmp_time))
+ goto err;
- if (ctm->type == V_ASN1_UTCTIME) {
- i = (buff1[0] - '0') * 10 + (buff1[1] - '0');
- if (i < 50)
- i += 100; /* cf. RFC 2459 */
- j = (buff2[0] - '0') * 10 + (buff2[1] - '0');
- if (j < 50)
- j += 100;
-
- if (i < j)
- return -1;
- if (i > j)
- return 1;
- }
- i = strcmp(buff1, buff2);
- if (i == 0) /* wait a second then return younger :-) */
+ /*
+ * X509_cmp_time comparison is <=.
+ * The return value 0 is reserved for errors.
+ */
+ ret = (day >= 0 && sec >= 0) ? -1 : 1;
+
+ err:
+ ASN1_TIME_free(asn1_cmp_time);
+ return ret;
+}
+
+/*
+ * Return 0 if time should not be checked or reference time is in range,
+ * or else 1 if it is past the end, or -1 if it is before the start
+ */
+int X509_cmp_timeframe(const X509_VERIFY_PARAM *vpm,
+ const ASN1_TIME *start, const ASN1_TIME *end)
+{
+ time_t ref_time;
+ time_t *time = NULL;
+ unsigned long flags = vpm == NULL ? 0 : X509_VERIFY_PARAM_get_flags(vpm);
+
+ if ((flags & X509_V_FLAG_USE_CHECK_TIME) != 0) {
+ ref_time = X509_VERIFY_PARAM_get_time(vpm);
+ time = &ref_time;
+ } else if ((flags & X509_V_FLAG_NO_CHECK_TIME) != 0) {
+ return 0; /* this means ok */
+ } /* else reference time is the current time */
+
+ if (end != NULL && X509_cmp_time(end, time) < 0)
+ return 1;
+ if (start != NULL && X509_cmp_time(start, time) > 0)
return -1;
- else
- return i;
+ return 0;
}
ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj)
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)))
+ if (!X509_CRL_set1_lastUpdate(crl, X509_CRL_get0_lastUpdate(newer)))
goto memerr;
- if (!X509_CRL_set_nextUpdate(crl, X509_CRL_get_nextUpdate(newer)))
+ if (!X509_CRL_set1_nextUpdate(crl, X509_CRL_get0_nextUpdate(newer)))
goto memerr;
/* Set base CRL number: must be critical */
return CRYPTO_set_ex_data(&ctx->ex_data, idx, data);
}
-void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx)
+void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx)
{
return CRYPTO_get_ex_data(&ctx->ex_data, idx);
}
-int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx)
+int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx)
{
return ctx->error;
}
ctx->error = err;
}
-int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx)
+int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx)
{
return ctx->error_depth;
}
-X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx)
+void X509_STORE_CTX_set_error_depth(X509_STORE_CTX *ctx, int depth)
+{
+ ctx->error_depth = depth;
+}
+
+X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx)
{
return ctx->current_cert;
}
-STACK_OF(X509) *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx)
+void X509_STORE_CTX_set_current_cert(X509_STORE_CTX *ctx, X509 *x)
+{
+ ctx->current_cert = x;
+}
+
+STACK_OF(X509) *X509_STORE_CTX_get0_chain(const X509_STORE_CTX *ctx)
{
return ctx->chain;
}
-STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx)
+STACK_OF(X509) *X509_STORE_CTX_get1_chain(const X509_STORE_CTX *ctx)
{
if (!ctx->chain)
return NULL;
return X509_chain_up_ref(ctx->chain);
}
-X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx)
+X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx)
{
return ctx->current_issuer;
}
-X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx)
+X509_CRL *X509_STORE_CTX_get0_current_crl(const X509_STORE_CTX *ctx)
{
return ctx->current_crl;
}
-X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx)
+X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(const X509_STORE_CTX *ctx)
{
return ctx->parent;
}
{
int idx;
/* If purpose not set use default */
- if (!purpose)
+ if (purpose == 0)
purpose = def_purpose;
/* If we have a purpose then check it is valid */
- if (purpose) {
+ if (purpose != 0) {
X509_PURPOSE *ptmp;
idx = X509_PURPOSE_get_by_id(purpose);
if (idx == -1) {
return 1;
}
-X509_STORE_CTX *X509_STORE_CTX_new(void)
+X509_STORE_CTX *X509_STORE_CTX_new_with_libctx(OPENSSL_CTX *libctx,
+ const char *propq)
{
X509_STORE_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL) {
- X509err(X509_F_X509_STORE_CTX_NEW, ERR_R_MALLOC_FAILURE);
+ X509err(0, ERR_R_MALLOC_FAILURE);
return NULL;
}
+
+ ctx->libctx = libctx;
+ if (propq != NULL) {
+ ctx->propq = OPENSSL_strdup(propq);
+ if (ctx->propq == NULL) {
+ OPENSSL_free(ctx);
+ X509err(0, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ }
+
return ctx;
}
+X509_STORE_CTX *X509_STORE_CTX_new(void)
+{
+ return X509_STORE_CTX_new_with_libctx(NULL, NULL);
+}
+
+
void X509_STORE_CTX_free(X509_STORE_CTX *ctx)
{
if (ctx == NULL)
return;
X509_STORE_CTX_cleanup(ctx);
+
+ /* libctx and propq survive X509_STORE_CTX_cleanup() */
+ OPENSSL_free(ctx->propq);
+
OPENSSL_free(ctx);
}
{
int ret = 1;
- ctx->ctx = store;
- ctx->current_method = 0;
+ ctx->store = store;
ctx->cert = x509;
ctx->untrusted = chain;
ctx->crls = NULL;
/* Zero ex_data to make sure we're cleanup-safe */
memset(&ctx->ex_data, 0, sizeof(ctx->ex_data));
- if (store) {
- ctx->verify_cb = store->verify_cb;
- /* Seems to always be 0 in OpenSSL, else must be idempotent */
+ /* store->cleanup is always 0 in OpenSSL, if set must be idempotent */
+ if (store)
ctx->cleanup = store->cleanup;
- } else
+ else
ctx->cleanup = 0;
if (store && store->check_issued)
else
ctx->cert_crl = cert_crl;
+ if (store && store->check_policy)
+ ctx->check_policy = store->check_policy;
+ else
+ ctx->check_policy = check_policy;
+
if (store && store->lookup_certs)
ctx->lookup_certs = store->lookup_certs;
else
- ctx->lookup_certs = X509_STORE_get1_certs;
+ ctx->lookup_certs = X509_STORE_CTX_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;
+ ctx->lookup_crls = X509_STORE_CTX_get1_crls;
ctx->param = X509_VERIFY_PARAM_new();
if (ctx->param == NULL) {
X509_VERIFY_PARAM_set_time(ctx->param, t);
}
+X509 *X509_STORE_CTX_get0_cert(const X509_STORE_CTX *ctx)
+{
+ return ctx->cert;
+}
+
+STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx)
+{
+ return ctx->untrusted;
+}
+
+void X509_STORE_CTX_set0_untrusted(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+{
+ ctx->untrusted = sk;
+}
+
+void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+{
+ sk_X509_pop_free(ctx->chain, X509_free);
+ ctx->chain = sk;
+}
+
void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx,
X509_STORE_CTX_verify_cb verify_cb)
{
ctx->verify_cb = verify_cb;
}
-X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(X509_STORE_CTX *ctx)
+X509_STORE_CTX_verify_cb X509_STORE_CTX_get_verify_cb(const X509_STORE_CTX *ctx)
{
return ctx->verify_cb;
}
-X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx)
+void X509_STORE_CTX_set_verify(X509_STORE_CTX *ctx,
+ X509_STORE_CTX_verify_fn verify)
{
- return ctx->cert;
+ ctx->verify = verify;
}
-STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx)
+X509_STORE_CTX_verify_fn X509_STORE_CTX_get_verify(const X509_STORE_CTX *ctx)
{
- return ctx->untrusted;
+ return ctx->verify;
}
-void X509_STORE_CTX_set0_verified_chain(X509_STORE_CTX *ctx, STACK_OF(X509) *sk)
+X509_STORE_CTX_get_issuer_fn X509_STORE_CTX_get_get_issuer(const X509_STORE_CTX *ctx)
{
- sk_X509_pop_free(ctx->chain, X509_free);
- ctx->chain = sk;
+ return ctx->get_issuer;
}
-void X509_STORE_CTX_set_verify(X509_STORE_CTX *ctx,
- X509_STORE_CTX_verify verify)
+X509_STORE_CTX_check_issued_fn
+ X509_STORE_CTX_get_check_issued(const X509_STORE_CTX *ctx)
{
- ctx->verify = verify;
+ return ctx->check_issued;
}
-X509_STORE_CTX_verify X509_STORE_CTX_get_verify(X509_STORE_CTX *ctx)
+X509_STORE_CTX_check_revocation_fn
+ X509_STORE_CTX_get_check_revocation(const X509_STORE_CTX *ctx)
{
- return ctx->verify;
+ return ctx->check_revocation;
+}
+
+X509_STORE_CTX_get_crl_fn X509_STORE_CTX_get_get_crl(const X509_STORE_CTX *ctx)
+{
+ return ctx->get_crl;
+}
+
+X509_STORE_CTX_check_crl_fn X509_STORE_CTX_get_check_crl(const X509_STORE_CTX *ctx)
+{
+ return ctx->check_crl;
+}
+
+X509_STORE_CTX_cert_crl_fn X509_STORE_CTX_get_cert_crl(const X509_STORE_CTX *ctx)
+{
+ return ctx->cert_crl;
}
-X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx)
+X509_STORE_CTX_check_policy_fn
+ X509_STORE_CTX_get_check_policy(const X509_STORE_CTX *ctx)
+{
+ return ctx->check_policy;
+}
+
+X509_STORE_CTX_lookup_certs_fn
+ X509_STORE_CTX_get_lookup_certs(const X509_STORE_CTX *ctx)
+{
+ return ctx->lookup_certs;
+}
+
+X509_STORE_CTX_lookup_crls_fn
+ X509_STORE_CTX_get_lookup_crls(const X509_STORE_CTX *ctx)
+{
+ return ctx->lookup_crls;
+}
+
+X509_STORE_CTX_cleanup_fn X509_STORE_CTX_get_cleanup(const X509_STORE_CTX *ctx)
+{
+ return ctx->cleanup;
+}
+
+X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(const X509_STORE_CTX *ctx)
{
return ctx->tree;
}
-int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx)
+int X509_STORE_CTX_get_explicit_policy(const X509_STORE_CTX *ctx)
{
return ctx->explicit_policy;
}
-int X509_STORE_CTX_get_num_untrusted(X509_STORE_CTX *ctx)
+int X509_STORE_CTX_get_num_untrusted(const X509_STORE_CTX *ctx)
{
return ctx->num_untrusted;
}
int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name)
{
const X509_VERIFY_PARAM *param;
+
param = X509_VERIFY_PARAM_lookup(name);
- if (!param)
+ if (param == NULL)
return 0;
return X509_VERIFY_PARAM_inherit(ctx->param, param);
}
-X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx)
+X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(const X509_STORE_CTX *ctx)
{
return ctx->param;
}
/*
* If we've previously matched a PKIX-?? record, no need to test any
- * further PKIX-?? records, it remains to just build the PKIX chain.
+ * further PKIX-?? records, it remains to just build the PKIX chain.
* Had the match been a DANE-?? record, we'd be done already.
*/
if (dane->mdpth >= 0)
cmplen = i2dlen;
if (md != NULL) {
- cmpbuf = mdbuf;
- if (!EVP_Digest(i2dbuf, i2dlen, cmpbuf, &cmplen, md, 0)) {
- matched = -1;
+ cmpbuf = mdbuf;
+ if (!EVP_Digest(i2dbuf, i2dlen, cmpbuf, &cmplen, md, 0)) {
+ matched = -1;
break;
}
}
return X509_TRUST_UNTRUSTED;
/*
- * Record any DANE trust-anchor matches, for the first depth to test, if
+ * Record any DANE trust anchor matches, for the first depth to test, if
* there's one at that depth. (This'll be false for length 1 chains looking
* for an exact match for the leaf certificate).
*/
* When testing the leaf certificate, if we match a DANE-EE(3) record,
* dane_match() returns 1 and we're done. If however we match a PKIX-EE(1)
* record, the match depth and matching TLSA record are recorded, but the
- * return value is 0, because we still need to find a PKIX trust-anchor.
+ * return value is 0, because we still need to find a PKIX trust anchor.
* Therefore, when DANE authentication is enabled (required), we're done
* if:
* + matched < 0, internal error.
/* Callback invoked as needed */
if (!check_leaf_suiteb(ctx, cert))
return 0;
+ /* Callback invoked as needed */
+ if ((dane->flags & DANE_FLAG_NO_DANE_EE_NAMECHECKS) == 0 &&
+ !check_id(ctx))
+ return 0;
/* Bypass internal_verify(), issue depth 0 success callback */
ctx->error_depth = 0;
ctx->current_cert = cert;
SSL_DANE *dane = ctx->dane;
int num = sk_X509_num(ctx->chain);
X509 *cert = sk_X509_value(ctx->chain, num - 1);
- int ss = cert_self_signed(cert);
+ int self_signed;
STACK_OF(X509) *sktmp = NULL;
unsigned int search;
int may_trusted = 0;
int i;
/* Our chain starts with a single untrusted element. */
- OPENSSL_assert(num == 1 && ctx->num_untrusted == num);
+ if (!ossl_assert(num == 1 && ctx->num_untrusted == num)) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR);
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ return 0;
+ }
+
+ self_signed = X509_self_signed(cert, 0);
+ if (self_signed < 0) {
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ return 0;
+ }
#define S_DOUNTRUSTED (1 << 0) /* Search untrusted chain */
#define S_DOTRUSTED (1 << 1) /* Search trusted store */
*/
if (ctx->untrusted && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) {
X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
- /* Include any untrusted full certificates from DNS */
+ /*
+ * If we got any "DANE-TA(2) Cert(0) Full(0)" trust anchors from DNS, add
+ * them to our working copy of the untrusted certificate stack. Since the
+ * caller of X509_STORE_CTX_init() may have provided only a leaf cert with
+ * no corresponding stack of untrusted certificates, we may need to create
+ * an empty stack first. [ At present only the ssl library provides DANE
+ * support, and ssl_verify_cert_chain() always provides a non-null stack
+ * containing at least the leaf certificate, but we must be prepared for
+ * this to change. ]
+ */
if (DANETLS_ENABLED(dane) && dane->certs != NULL) {
- for (i = 0; i < sk_X509_num(dane->certs); ++i) {
- if (!sk_X509_push(sktmp, sk_X509_value(dane->certs, i))) {
- sk_X509_free(sktmp);
- X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE);
- return 0;
- }
+ if (sktmp == NULL && (sktmp = sk_X509_new_null()) == NULL) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
+ return 0;
+ }
+ if (!X509_add_certs(sktmp, dane->certs, X509_ADD_FLAG_DEFAULT)) {
+ sk_X509_free(sktmp);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
+ return 0;
}
}
ctx->param->depth = INT_MAX/2;
/*
- * Try to Extend the chain until we reach an ultimately trusted issuer.
+ * 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.
*/
* 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
* reach the depth limit, we stop extending the chain, if by that point
- * we've not found a trust-anchor, any trusted chain would be too long.
+ * we've not found a trust anchor, any trusted chain would be too long.
*
* The error reported to the application verify callback is at the
* maximal valid depth with the current certificate equal to the last
if (ok < 0) {
trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_STORE_LOOKUP;
search = 0;
continue;
}
* 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
+ * 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!
* certificate among the ones from the trust store.
*/
if ((search & S_DOALTERNATE) != 0) {
- OPENSSL_assert(num > i && i > 0 && ss == 0);
+ if (!ossl_assert(num > i && i > 0 && !self_signed)) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR);
+ X509_free(xtmp);
+ trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ search = 0;
+ continue;
+ }
search &= ~S_DOALTERNATE;
for (; num > i; --num)
X509_free(sk_X509_pop(ctx->chain));
* Self-signed untrusted certificates get replaced by their
* trusted matching issuer. Otherwise, grow the chain.
*/
- if (ss == 0) {
+ if (!self_signed) {
if (!sk_X509_push(ctx->chain, x = xtmp)) {
X509_free(xtmp);
X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE);
trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
search = 0;
continue;
}
- ss = cert_self_signed(x);
+ self_signed = X509_self_signed(x, 0);
+ if (self_signed < 0) {
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ return 0;
+ }
} 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
+ * a trust anchor. We must have an exact match to avoid
* possible impersonation via key substitution etc.
*/
if (X509_cmp(x, xtmp) != 0) {
* certificate with ctx->num_untrusted <= num.
*/
if (ok) {
- OPENSSL_assert(ctx->num_untrusted <= num);
+ if (!ossl_assert(ctx->num_untrusted <= num)) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR);
+ trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ search = 0;
+ continue;
+ }
search &= ~S_DOUNTRUSTED;
switch (trust = check_trust(ctx, num)) {
case X509_TRUST_TRUSTED:
search = 0;
continue;
}
- if (ss == 0)
+ if (!self_signed)
continue;
}
}
/* Search for a trusted issuer of a shorter chain */
search |= S_DOALTERNATE;
alt_untrusted = ctx->num_untrusted - 1;
- ss = 0;
+ self_signed = 0;
}
}
*/
if ((search & S_DOUNTRUSTED) != 0) {
num = sk_X509_num(ctx->chain);
- OPENSSL_assert(num == ctx->num_untrusted);
+ if (!ossl_assert(num == ctx->num_untrusted)) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR);
+ trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ search = 0;
+ continue;
+ }
x = sk_X509_value(ctx->chain, num-1);
/*
* Once we run out of untrusted issuers, we stop looking for more
* and start looking only in the trust store if enabled.
*/
- xtmp = (ss || depth < num) ? NULL : find_issuer(ctx, sktmp, x);
+ xtmp = (self_signed || depth < num) ? NULL
+ : find_issuer(ctx, sktmp, x);
if (xtmp == NULL) {
search &= ~S_DOUNTRUSTED;
if (may_trusted)
/* Drop this issuer from future consideration */
(void) sk_X509_delete_ptr(sktmp, xtmp);
+ if (!X509_up_ref(xtmp)) {
+ X509err(X509_F_BUILD_CHAIN, ERR_R_INTERNAL_ERROR);
+ trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ search = 0;
+ continue;
+ }
+
if (!sk_X509_push(ctx->chain, xtmp)) {
+ X509_free(xtmp);
X509err(X509_F_BUILD_CHAIN, ERR_R_MALLOC_FAILURE);
trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
search = 0;
continue;
}
- X509_up_ref(x = xtmp);
+ x = xtmp;
++ctx->num_untrusted;
- ss = cert_self_signed(xtmp);
+ self_signed = X509_self_signed(xtmp, 0);
+ if (self_signed < 0) {
+ sk_X509_free(sktmp);
+ ctx->error = X509_V_ERR_UNSPECIFIED;
+ return 0;
+ }
/*
* Check for DANE-TA trust of the topmost untrusted certificate.
if (DANETLS_ENABLED(dane) &&
(!DANETLS_HAS_PKIX(dane) || dane->pdpth >= 0))
return verify_cb_cert(ctx, NULL, num-1, X509_V_ERR_DANE_NO_MATCH);
- if (ss && sk_X509_num(ctx->chain) == 1)
+ if (self_signed && sk_X509_num(ctx->chain) == 1)
return verify_cb_cert(ctx, NULL, num-1,
X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
- if (ss)
+ if (self_signed)
return verify_cb_cert(ctx, NULL, num-1,
X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
if (ctx->num_untrusted < num)
EVP_PKEY *pkey = X509_get0_pubkey(cert);
int level = ctx->param->auth_level;
+ /*
+ * At security level zero, return without checking for a supported public
+ * key type. Some engines support key types not understood outside the
+ * engine, and we only need to understand the key when enforcing a security
+ * floor.
+ */
+ if (level <= 0)
+ return 1;
+
/* Unsupported or malformed keys are not secure */
if (pkey == NULL)
return 0;
- if (level <= 0)
- return 1;
if (level > NUM_AUTH_LEVELS)
level = NUM_AUTH_LEVELS;
*/
static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert)
{
- int nid = X509_get_signature_nid(cert);
- int mdnid = NID_undef;
int secbits = -1;
int level = ctx->param->auth_level;
if (level > NUM_AUTH_LEVELS)
level = NUM_AUTH_LEVELS;
- /* Lookup signature algorithm digest */
- if (nid && OBJ_find_sigid_algs(nid, &mdnid, NULL)) {
- const EVP_MD *md;
-
- /* Assume 4 bits of collision resistance for each hash octet */
- if (mdnid != NID_undef && (md = EVP_get_digestbynid(mdnid)) != NULL)
- secbits = EVP_MD_size(md) * 4;
- }
+ if (!X509_get_signature_info(cert, NULL, NULL, &secbits, NULL))
+ return 0;
return secbits >= minbits_table[level - 1];
}