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;
}
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);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return -1;
}
X509_up_ref(ctx->cert);
!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;
}
/*
} 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;
}
* 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;
/* 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)
+ 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;
+ }
}
}
}
*/
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;
}
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->lookup_crls = X509_STORE_CTX_get1_crls;
ctx->check_policy = check_policy;
/*
* 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)
/* 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;
*/
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;
}
if (DANETLS_ENABLED(dane) && dane->certs != NULL) {
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;
}
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);
+ ctx->error = X509_V_ERR_OUT_OF_MEM;
return 0;
}
}
if (ok < 0) {
trust = X509_TRUST_REJECTED;
+ ctx->error = X509_V_ERR_STORE_LOOKUP;
search = 0;
continue;
}
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;
}
if (!sk_X509_push(ctx->chain, 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;
}