/* * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project * 2004. */ /* ==================================================================== * Copyright (c) 2004 The OpenSSL Project. All rights reserved. * * 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 above 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 acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * licensing@OpenSSL.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED 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 OpenSSL PROJECT OR * ITS 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ #include #include #include #include #include #include "../internal.h" #include "internal.h" // X509_VERIFY_PARAM functions #define SET_HOST 0 #define ADD_HOST 1 static void str_free(char *s) { OPENSSL_free(s); } static int int_x509_param_set_hosts(X509_VERIFY_PARAM *param, int mode, const char *name, size_t namelen) { char *copy; // Setting 0 to automatically detect the length of |name| is an OpenSSL quirk // that AWS-LC isn't keen on supporting. However, consumers often assume // OpenSSL semantics from AWS-LC, so it's supported in this case. if (name != NULL && namelen == 0) { namelen = strlen(name); } // Refuse names with embedded NUL bytes. // XXX: Do we need to push an error onto the error stack? if (name && OPENSSL_memchr(name, '\0', namelen)) { return 0; } if (mode == SET_HOST && param->hosts) { sk_OPENSSL_STRING_pop_free(param->hosts, str_free); param->hosts = NULL; } // OpenSSL returns 1 when trying to set or add an empty name. This is also a // quirk that AWS-LC isn't keen on supporting, but we maintain for backwards // compatibility. if (name == NULL || namelen == 0) { return 1; } copy = OPENSSL_strndup(name, namelen); if (copy == NULL) { return 0; } if (param->hosts == NULL && (param->hosts = sk_OPENSSL_STRING_new_null()) == NULL) { OPENSSL_free(copy); return 0; } if (!sk_OPENSSL_STRING_push(param->hosts, copy)) { OPENSSL_free(copy); if (sk_OPENSSL_STRING_num(param->hosts) == 0) { sk_OPENSSL_STRING_free(param->hosts); param->hosts = NULL; } return 0; } return 1; } X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void) { X509_VERIFY_PARAM *param = OPENSSL_zalloc(sizeof(X509_VERIFY_PARAM)); if (!param) { return NULL; } param->depth = -1; return param; } void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) { if (param == NULL) { return; } sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); sk_OPENSSL_STRING_pop_free(param->hosts, str_free); OPENSSL_free(param->email); OPENSSL_free(param->ip); OPENSSL_free(param); } static int should_copy(int dest_is_set, int src_is_set, int prefer_src) { if (prefer_src) { // We prefer the source, so as long as there is a value to copy, copy it. return src_is_set; } // We prefer the destination, so only copy if the destination is unset. return src_is_set && !dest_is_set; } static void copy_int_param(int *dest, const int *src, int default_val, int prefer_src) { if (should_copy(*dest != default_val, *src != default_val, prefer_src)) { *dest = *src; } } // x509_verify_param_copy copies fields from |src| to |dest|. If both |src| and // |dest| have some field set, |prefer_src| determines whether |src| or |dest|'s // version is used. static int x509_verify_param_copy(X509_VERIFY_PARAM *dest, const X509_VERIFY_PARAM *src, int prefer_src) { if (src == NULL) { return 1; } copy_int_param(&dest->purpose, &src->purpose, /*default_val=*/0, prefer_src); copy_int_param(&dest->trust, &src->trust, /*default_val=*/0, prefer_src); copy_int_param(&dest->depth, &src->depth, /*default_val=*/-1, prefer_src); // |check_time|, unlike all other parameters, does not honor |prefer_src|. // This means |X509_VERIFY_PARAM_set1| will not overwrite it. This behavior // comes from OpenSSL but may have been a bug. if (!(dest->flags & X509_V_FLAG_USE_CHECK_TIME)) { dest->check_time = src->check_time; // The source |X509_V_FLAG_USE_CHECK_TIME| flag, if set, is copied below. } dest->flags |= src->flags; if (should_copy(dest->policies != NULL, src->policies != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_policies(dest, src->policies)) { return 0; } } if (should_copy(dest->hosts != NULL, src->hosts != NULL, prefer_src)) { sk_OPENSSL_STRING_pop_free(dest->hosts, str_free); dest->hosts = NULL; if (src->hosts) { dest->hosts = sk_OPENSSL_STRING_deep_copy(src->hosts, OPENSSL_strdup, str_free); if (dest->hosts == NULL) { return 0; } // Copy the host flags if and only if we're copying the host list. Note // this means mechanisms like |X509_STORE_CTX_set_default| cannot be used // to set host flags. E.g. we cannot change the defaults using // |kDefaultParam| below. dest->hostflags = src->hostflags; } } if (should_copy(dest->email != NULL, src->email != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen)) { return 0; } } if (should_copy(dest->ip != NULL, src->ip != NULL, prefer_src)) { if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen)) { return 0; } } dest->poison = src->poison; return 1; } int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, const X509_VERIFY_PARAM *src) { // Prefer the destination. That is, this function only changes unset // parameters in |dest|. return x509_verify_param_copy(dest, src, /*prefer_src=*/0); } int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, const X509_VERIFY_PARAM *from) { // Prefer the source. That is, values in |to| are only preserved if they were // unset in |from|. return x509_verify_param_copy(to, from, /*prefer_src=*/1); } static int int_x509_param_set1_email(char **pdest, size_t *pdestlen, const char *src, size_t srclen) { void *tmp; if (src != NULL) { // Setting |srclen| to 0 to automatically detect the length of |src| is an // OpenSSL quirk that AWS-LC isn't keen on supporting. However, consumers // often assume OpenSSL semantics from AWS-LC, so it's supported in this // case. if (srclen == 0) { srclen = strlen(src); } tmp = OPENSSL_strndup(src, srclen); if (tmp == NULL) { return 0; } } else { // This allows an empty string to disable previously configured checks. // This is an OpenSSL quirk that AWS-LC isn't keen on supporting. However, // consumers often assume OpenSSL semantics from AWS-LC, so it's supported // in this case. tmp = NULL; srclen = 0; } if (*pdest != NULL) { OPENSSL_free(*pdest); } *pdest = tmp; if (pdestlen != NULL) { *pdestlen = srclen; } return 1; } // IP addresses work slightly differently, so we use another function to // differentiate from emails. |X509_VERIFY_PARAM_set1_ip| takes a const // unsigned char*, instead of a const char*, so the same strlen logic that was // being used is not quite suitable here. // We keep the original behavior that BoringSSL left, but only for IP addresses. // We can align the behavior with |int_x509_param_set1_email| like OpenSSL has // been doing if needed. static int int_x509_param_set1_ip(unsigned char **pdest, size_t *pdestlen, const unsigned char *src, size_t srclen) { void *tmp; if (src == NULL || srclen == 0) { // Unlike OpenSSL, we do not allow an empty string to disable previously // configured checks. return 0; } tmp = OPENSSL_memdup(src, srclen); if (!tmp) { return 0; } if (*pdest) { OPENSSL_free(*pdest); } *pdest = tmp; if (pdestlen) { *pdestlen = srclen; } return 1; } int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags) { param->flags |= flags; if (flags & X509_V_FLAG_POLICY_MASK) { param->flags |= X509_V_FLAG_POLICY_CHECK; } return 1; } int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, unsigned long flags) { param->flags &= ~flags; return 1; } unsigned long X509_VERIFY_PARAM_get_flags(const X509_VERIFY_PARAM *param) { return param->flags; } int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose) { return X509_PURPOSE_set(¶m->purpose, purpose); } int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust) { return X509_TRUST_set(¶m->trust, trust); } void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth) { param->depth = depth; } void X509_VERIFY_PARAM_set_time_posix(X509_VERIFY_PARAM *param, int64_t t) { param->check_time = t; param->flags |= X509_V_FLAG_USE_CHECK_TIME; } void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t) { X509_VERIFY_PARAM_set_time_posix(param, t); } int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, ASN1_OBJECT *policy) { if (!param->policies) { param->policies = sk_ASN1_OBJECT_new_null(); if (!param->policies) { return 0; } } if (!sk_ASN1_OBJECT_push(param->policies, policy)) { return 0; } // TODO(davidben): This does not set |X509_V_FLAG_POLICY_CHECK|, while // |X509_VERIFY_PARAM_set1_policies| does. Is this a bug? return 1; } int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, const STACK_OF(ASN1_OBJECT) *policies) { if (!param) { return 0; } sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free); if (!policies) { param->policies = NULL; return 1; } param->policies = sk_ASN1_OBJECT_deep_copy(policies, OBJ_dup, ASN1_OBJECT_free); if (!param->policies) { return 0; } param->flags |= X509_V_FLAG_POLICY_CHECK; return 1; } int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen) { if (!int_x509_param_set_hosts(param, SET_HOST, name, namelen)) { param->poison = 1; return 0; } return 1; } int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param, const char *name, size_t namelen) { if (!int_x509_param_set_hosts(param, ADD_HOST, name, namelen)) { param->poison = 1; return 0; } return 1; } void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param, unsigned int flags) { param->hostflags = flags; } int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param, const char *email, size_t emaillen) { if (OPENSSL_memchr(email, '\0', emaillen) != NULL || !int_x509_param_set1_email(¶m->email, ¶m->emaillen, email, emaillen)) { param->poison = 1; return 0; } return 1; } int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, const unsigned char *ip, size_t iplen) { if ((iplen != 0 && iplen != 4 && iplen != 16) || !int_x509_param_set1_ip(¶m->ip, ¶m->iplen, ip, iplen)) { param->poison = 1; return 0; } return 1; } int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc) { unsigned char ipout[16]; size_t iplen; iplen = (size_t)x509v3_a2i_ipadd(ipout, ipasc); if (iplen == 0) { return 0; } return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen); } int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param) { return param->depth; } #define vpm_empty_id NULL, 0U, NULL, 0, NULL, 0, 0 static const X509_VERIFY_PARAM kDefaultParam = { .flags = X509_V_FLAG_TRUSTED_FIRST, .depth = 100, }; static const X509_VERIFY_PARAM kSMIMESignParam = { .purpose = X509_PURPOSE_SMIME_SIGN, .trust = X509_TRUST_EMAIL, .depth = -1, }; static const X509_VERIFY_PARAM kSSLClientParam = { .purpose = X509_PURPOSE_SSL_CLIENT, .trust = X509_TRUST_SSL_CLIENT, .depth = -1, }; static const X509_VERIFY_PARAM kSSLServerParam = { .purpose = X509_PURPOSE_SSL_SERVER, .trust = X509_TRUST_SSL_SERVER, .depth = -1, }; const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name) { if (strcmp(name, "default") == 0) { return &kDefaultParam; } if (strcmp(name, "pkcs7") == 0) { // PKCS#7 and S/MIME signing use the same defaults. return &kSMIMESignParam; } if (strcmp(name, "smime_sign") == 0) { return &kSMIMESignParam; } if (strcmp(name, "ssl_client") == 0) { return &kSSLClientParam; } if (strcmp(name, "ssl_server") == 0) { return &kSSLServerParam; } return NULL; }