X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=crypto%2Fx509%2Fx509_vfy.c;h=ee1c9af977509f104fccc44e7385a87a263c00f2;hb=5ae4ceb92c2ae6c677b1de2c477dce71a4d94716;hp=fe0ea988265e3d083e519ec58633cbf0b8f1274e;hpb=b1322259d93cf6b6286f9febcd468b6a9f577d91;p=oweals%2Fopenssl.git diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index fe0ea98826..ee1c9af977 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -251,9 +251,11 @@ static int verify_chain(X509_STORE_CTX *ctx) 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; } @@ -263,6 +265,7 @@ int X509_verify_cert(X509_STORE_CTX *ctx) * 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; } @@ -273,6 +276,7 @@ int X509_verify_cert(X509_STORE_CTX *ctx) 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); @@ -283,15 +287,19 @@ int X509_verify_cert(X509_STORE_CTX *ctx) !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; } /* @@ -453,12 +461,6 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) } 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; } @@ -525,10 +527,24 @@ static int check_chain_extensions(X509_STORE_CTX *ctx) * 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; @@ -550,6 +566,79 @@ static int check_name_constraints(X509_STORE_CTX *ctx) /* 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 @@ -562,8 +651,20 @@ static int check_name_constraints(X509_STORE_CTX *ctx) 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; + } } } } @@ -1457,6 +1558,7 @@ static int check_policy(X509_STORE_CTX *ctx) */ 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, @@ -1466,6 +1568,7 @@ static int check_policy(X509_STORE_CTX *ctx) 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 */ @@ -1496,7 +1599,12 @@ static int check_policy(X509_STORE_CTX *ctx) 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; } @@ -2172,12 +2280,12 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509, 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; @@ -2428,7 +2536,7 @@ static int dane_match(X509_STORE_CTX *ctx, X509 *cert, int depth) /* * 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) @@ -2657,6 +2765,10 @@ static int dane_verify(X509_STORE_CTX *ctx) /* 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; @@ -2742,6 +2854,7 @@ static int build_chain(X509_STORE_CTX *ctx) */ 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; } @@ -2758,12 +2871,14 @@ static int build_chain(X509_STORE_CTX *ctx) 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; } } @@ -2827,6 +2942,7 @@ static int build_chain(X509_STORE_CTX *ctx) if (ok < 0) { trust = X509_TRUST_REJECTED; + ctx->error = X509_V_ERR_STORE_LOOKUP; search = 0; continue; } @@ -2873,6 +2989,7 @@ static int build_chain(X509_STORE_CTX *ctx) 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; } @@ -2969,6 +3086,7 @@ static int build_chain(X509_STORE_CTX *ctx) 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; }