blob: 0640fd08b771de8da79078542ba223c614984694 [file] [log] [blame]
/*
* 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 <string.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#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;
if (name == NULL || namelen == 0) {
// Unlike OpenSSL, we reject trying to set or add an empty name.
return 0;
}
// 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;
}
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(char **pdest, size_t *pdestlen, const 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;
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) {
if (X509_PURPOSE_get0(purpose) == NULL) {
OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_PURPOSE);
return 0;
}
param->purpose = purpose;
return 1;
}
int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust) {
if (!X509_is_valid_trust_id(trust)) {
OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_TRUST_ID);
return 0;
}
param->trust = trust;
return 1;
}
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;
}
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;
}
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(&param->email, &param->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 != 4 && iplen != 16) ||
!int_x509_param_set1((char **)&param->ip, &param->iplen, (char *)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;
}
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;
}