}
static int do_x509_check(X509 *x, const char *chk, size_t chklen,
- unsigned int flags, int check_type, char **peername)
+ unsigned int flags, int check_type, int check_nid, char **peername)
{
GENERAL_NAMES *gens = NULL;
const X509_NAME *name = NULL;
default:
continue;
case GEN_OTHERNAME:
+ if (check_type != GEN_OTHERNAME)
+ continue;
switch (OBJ_obj2nid(gen->d.otherName->type_id)) {
default:
continue;
* choose to turn it off, doing so is at this time a best
* practice.
*/
- if (check_type != GEN_EMAIL
+ if (check_nid != NID_id_on_SmtpUTF8Mailbox
|| gen->d.otherName->value->type != V_ASN1_UTF8STRING)
continue;
alt_type = 0;
return -2;
if (chklen > 1 && chk[chklen - 1] == '\0')
--chklen;
- return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername);
+ return do_x509_check(x, chk, chklen, flags, GEN_DNS, 0, peername);
+}
+
+int ossl_x509_check_rfc822(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags)
+{
+ return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, 0, NULL) == 1;
+}
+
+int ossl_x509_check_smtputf8(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags)
+{
+ return do_x509_check(x, chk, chklen, flags, GEN_OTHERNAME,
+ NID_id_on_SmtpUTF8Mailbox, NULL)
+ == 1;
}
int X509_check_email(X509 *x, const char *chk, size_t chklen,
return -2;
if (chklen > 1 && chk[chklen - 1] == '\0')
--chklen;
- return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL);
+ /*
+ * As this is public API, historically it has supported checking
+ * whatever is supplied against both RFC822 and SMTPUTF8.
+ */
+ if (do_x509_check(x, chk, chklen, flags, GEN_EMAIL, 0, NULL) == 1)
+ return 1;
+ return do_x509_check(x, chk, chklen, flags, GEN_OTHERNAME,
+ NID_id_on_SmtpUTF8Mailbox, NULL);
}
int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen,
{
if (chk == NULL)
return -2;
- return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL);
+ return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, 0, NULL);
}
int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags)
iplen = (size_t)ossl_a2i_ipadd(ipout, ipasc);
if (iplen == 0)
return -2;
- return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL);
+ return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, 0, NULL);
}
char *ossl_ipaddr_to_asc(unsigned char *p, int len)
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
+#include <openssl/safestack.h>
#include "internal/refcount.h"
#include "internal/hashtable.h"
#define X509V3_conf_add_error_name_value(val) \
ERR_add_error_data(4, "name=", (val)->name, ", value=", (val)->value)
+/*
+ * Really all I want is CRYPTO_BUFFER from BoringSSL, but let's just do this
+ * for now.
+ */
+typedef struct ossl_x509_buffer_st {
+ const uint8_t *data;
+ size_t len;
+} X509_BUFFER;
+
+DEFINE_STACK_OF(X509_BUFFER)
+
/*
* This structure holds all parameters associated with a verify operation by
* including an X509_VERIFY_PARAM structure in related structures the
* parameters used can be customized
*/
-
struct X509_VERIFY_PARAM_st {
char *name;
int64_t check_time; /* Time to use */
int auth_level; /* Security level for chain verification */
STACK_OF(ASN1_OBJECT) *policies; /* Permissible policies */
/* Peer identity details */
- STACK_OF(OPENSSL_STRING) *hosts; /* Set of acceptable names */
+ STACK_OF(X509_BUFFER) *hosts; /* Set of acceptable names */
+ int (*validate_host)(const char *name, size_t len);
+ STACK_OF(X509_BUFFER) *ips; /* Set of acceptable ip addresses */
+ int (*validate_ip)(const uint8_t *name, size_t len);
+ STACK_OF(X509_BUFFER) *rfc822s; /* Set of acceptable RFC 822 names */
+ int (*validate_rfc822)(const char *name, size_t len);
+ STACK_OF(X509_BUFFER) *smtputf8s; /* Set of acceptable SMTP Utf8 names */
+ int (*validate_smtputf8)(const char *name, size_t len);
unsigned int hostflags; /* Flags to control matching features */
char *peername; /* Matching hostname in peer certificate */
- char *email; /* If not NULL email address to match */
- size_t emaillen;
- unsigned char *ip; /* If not NULL IP address to match */
- size_t iplen; /* Length of IP address */
};
/* No error callback if depth < 0 */
__owur int ossl_x509_store_read_lock(X509_STORE *xs);
STACK_OF(X509_OBJECT) *ossl_x509_store_ht_get_by_name(const X509_STORE *store,
const X509_NAME *xn);
+int ossl_x509_check_rfc822(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags);
+int ossl_x509_check_smtputf8(X509 *x, const char *chk, size_t chklen,
+ unsigned int flags);
static int check_hosts(X509 *x, X509_VERIFY_PARAM *vpm)
{
int i;
- int n = sk_OPENSSL_STRING_num(vpm->hosts);
- char *name;
+ int n = sk_X509_BUFFER_num(vpm->hosts);
+ const uint8_t *name;
if (vpm->peername != NULL) {
OPENSSL_free(vpm->peername);
vpm->peername = NULL;
}
for (i = 0; i < n; ++i) {
- name = sk_OPENSSL_STRING_value(vpm->hosts, i);
- if (X509_check_host(x, name, 0, vpm->hostflags, &vpm->peername) > 0)
+ size_t len = sk_X509_BUFFER_value(vpm->hosts, i)->len;
+ name = sk_X509_BUFFER_value(vpm->hosts, i)->data;
+ if (X509_check_host(x, (const char *)name, len, vpm->hostflags, &vpm->peername) > 0)
+ return 1;
+ }
+ return n == 0;
+}
+
+static int check_email(X509 *x, X509_VERIFY_PARAM *vpm)
+{
+ int i, n, j;
+ const uint8_t *name;
+
+ if (vpm->rfc822s == NULL)
+ return 1;
+
+ n = sk_X509_BUFFER_num(vpm->rfc822s);
+
+ for (i = 0; i < n; ++i) {
+ size_t len = sk_X509_BUFFER_value(vpm->rfc822s, i)->len;
+ name = sk_X509_BUFFER_value(vpm->rfc822s, i)->data;
+ if (ossl_x509_check_rfc822(x, (const char *)name, len, vpm->hostflags))
+ return 1;
+ }
+
+ j = sk_X509_BUFFER_num(vpm->smtputf8s);
+ for (i = 0; i < j; ++i) {
+ size_t len = sk_X509_BUFFER_value(vpm->smtputf8s, i)->len;
+ name = sk_X509_BUFFER_value(vpm->smtputf8s, i)->data;
+ if (ossl_x509_check_smtputf8(x, (const char *)name, len, vpm->hostflags))
+ return 1;
+ }
+
+ return n == 0 && i == 0;
+}
+
+static int check_ips(X509 *x, X509_VERIFY_PARAM *vpm)
+{
+ int i;
+ int n = sk_X509_BUFFER_num(vpm->ips);
+ const uint8_t *name;
+
+ for (i = 0; i < n; ++i) {
+ size_t len = sk_X509_BUFFER_value(vpm->ips, i)->len;
+ name = sk_X509_BUFFER_value(vpm->ips, i)->data;
+ if (X509_check_ip(x, name, len, vpm->hostflags) > 0)
return 1;
}
return n == 0;
if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
return 0;
}
- if (vpm->email != NULL
- && X509_check_email(x, vpm->email, vpm->emaillen, 0) <= 0) {
+
+ if (!check_email(x, vpm)) {
if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH))
return 0;
}
- if (vpm->ip != NULL && X509_check_ip(x, vpm->ip, vpm->iplen, 0) <= 0) {
+
+ if (vpm->ips != NULL && check_ips(x, vpm) <= 0) {
if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH))
return 0;
}
#include <openssl/buffer.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+
+#include "crypto/ctype.h"
#include "crypto/x509.h"
#include "x509_local.h"
+typedef enum {
+ OSSL_CHARSET_NONASCII,
+ OSSL_CHARSET_ASCII,
+ OSSL_CHARSET_ASCII_ALNUM,
+} ossl_charset_t;
+
/* X509_VERIFY_PARAM functions */
#define SET_HOST 0
#define ADD_HOST 1
-static char *str_copy(const char *s)
+static X509_BUFFER *buffer_from_bytes(const uint8_t *bytes, size_t length)
{
- return OPENSSL_strdup(s);
+ X509_BUFFER *buf;
+
+ if ((buf = OPENSSL_zalloc(sizeof *buf)) != NULL
+ && (buf->data = OPENSSL_memdup(bytes, length)) != NULL)
+ buf->len = length;
+ else
+ OPENSSL_free(buf);
+ return buf;
}
-static void str_free(char *s)
+/*
+ * Copies |length| bytes from |bytes| to a new buffer, making the data
+ * A C string. It is an error for the |bytes| to contain any \0 values
+ * within |length|. |bytes| need not itself be \0 terminated, the data
+ * in the buffer will be on success.
+ */
+static X509_BUFFER *buffer_from_string(const uint8_t *bytes, size_t length)
{
- OPENSSL_free(s);
+ X509_BUFFER *buf, *ret = NULL;
+ uint8_t *data = NULL;
+
+ if ((buf = OPENSSL_zalloc(sizeof *buf)) == NULL)
+ goto err;
+
+ if ((data = (uint8_t *)OPENSSL_strndup((char *)bytes, length)) == NULL)
+ goto err;
+
+ if (strlen((char *)data) != length)
+ goto err;
+
+ ret = buf;
+ buf = NULL;
+ ret->data = data;
+ ret->len = length;
+ data = NULL;
+
+err:
+ OPENSSL_free(buf);
+ OPENSSL_free(data);
+
+ return ret;
}
-static int int_x509_param_set_hosts(X509_VERIFY_PARAM *vpm, int mode,
- const char *name, size_t namelen)
+static X509_BUFFER *buffer_copy(const X509_BUFFER *b)
{
- char *copy;
+ return buffer_from_bytes(b->data, b->len);
+}
+
+static void buffer_free(X509_BUFFER *b)
+{
+ if (b == NULL)
+ return;
+ OPENSSL_free((void *)b->data);
+ OPENSSL_free(b);
+}
+
+static int replace_buffer_stack(STACK_OF(X509_BUFFER) **dest,
+ STACK_OF(X509_BUFFER) *const *src)
+{
+ sk_X509_BUFFER_pop_free(*dest, buffer_free);
+ *dest = NULL;
+ if (*src != NULL) {
+ *dest = sk_X509_BUFFER_deep_copy(*src, buffer_copy, buffer_free);
+ if (*dest == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static int buffer_cmp(const X509_BUFFER *const *a, const X509_BUFFER *const *b)
+{
+ if ((*a)->len < (*b)->len)
+ return -1;
+ if ((*a)->len > (*b)->len)
+ return 1;
+ return memcmp((*a)->data, (*b)->data, (*b)->len);
+}
+
+static void clear_buffer_stack(STACK_OF(X509_BUFFER) **buffer_stack)
+{
+ sk_X509_BUFFER_pop_free(*buffer_stack, buffer_free);
+ *buffer_stack = NULL;
+}
+
+static int add_bytes_to_buffer_stack(STACK_OF(X509_BUFFER) **buffer_stack,
+ const uint8_t *name, size_t name_len)
+{
+ STACK_OF(X509_BUFFER) *tmp_stack = NULL;
+ X509_BUFFER *copy = NULL;
+ int ret = 0;
+
+ if ((copy = buffer_from_bytes(name, name_len)) == NULL)
+ goto err;
+
+ tmp_stack = *buffer_stack;
+ if (tmp_stack == NULL && (tmp_stack = sk_X509_BUFFER_new(buffer_cmp)) == NULL)
+ goto err;
+
+ if (!sk_X509_BUFFER_push(tmp_stack, copy))
+ goto err;
+
+ ret = 1;
+ copy = NULL;
+ *buffer_stack = tmp_stack;
+ tmp_stack = NULL;
+
+err:
+ sk_X509_BUFFER_pop_free(tmp_stack, buffer_free);
+ buffer_free(copy);
+
+ return ret;
+}
+
+static int add_string_to_buffer_stack(STACK_OF(X509_BUFFER) **buffer_stack,
+ const uint8_t *name, size_t name_len)
+{
+ STACK_OF(X509_BUFFER) *tmp_stack = NULL;
+ X509_BUFFER *copy = NULL;
+ int ret = 0;
+
+ if ((copy = buffer_from_string(name, name_len)) == NULL)
+ goto err;
+
+ tmp_stack = *buffer_stack;
+ if (tmp_stack == NULL && (tmp_stack = sk_X509_BUFFER_new(buffer_cmp)) == NULL)
+ goto err;
+
+ if (!sk_X509_BUFFER_push(tmp_stack, copy))
+ goto err;
+
+ ret = 1;
+ copy = NULL;
+ *buffer_stack = tmp_stack;
+ tmp_stack = NULL;
+
+err:
+ sk_X509_BUFFER_pop_free(tmp_stack, buffer_free);
+ buffer_free(copy);
+
+ return ret;
+}
+
+static int validate_string_name(const char *name, size_t *name_len)
+{
+ size_t len = *name_len;
+
+ if (name == NULL || len == 0)
+ return 0;
/*
- * Refuse names with embedded NUL bytes, except perhaps as final byte.
- * XXX: Do we need to push an error onto the error stack?
+ * Accept the trailing \0 byte if this is a C string. This is to
+ * preserver behaviour that is traditional for the
+ * set1_[host|email] functions.
*/
- if (namelen == 0 || name == NULL)
- namelen = name ? strlen(name) : 0;
- else if (name != NULL
- && memchr(name, '\0', namelen > 1 ? namelen - 1 : namelen) != NULL)
+ if (name[len - 1] == '\0')
+ len--;
+
+ /* Refuse the empty string */
+ if (len == 0)
return 0;
- if (namelen > 0 && name[namelen - 1] == '\0')
- --namelen;
- if (mode == SET_HOST) {
- sk_OPENSSL_STRING_pop_free(vpm->hosts, str_free);
- vpm->hosts = NULL;
- }
- if (name == NULL || namelen == 0)
+ /* Refuse values with embedded \0 bytes other than at the end */
+ if (memchr(name, '\0', len) != NULL)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Default input validation for verification parameter names. As these
+ * could potentially come from untrusted input, doing basic input
+ * validation makes sense, and ensures that subsequent parsing or
+ * comparisons do not need to handle extreme out of range input.
+ */
+
+/* Default ip name input validation */
+static int validate_ip_name(const uint8_t *name, size_t len)
+{
+ if (name != NULL && (len == 4 || len == 16))
return 1;
+ return 0;
+}
- copy = OPENSSL_strndup(name, namelen);
- if (copy == NULL)
+static ossl_charset_t ossl_name_charset(int c, ossl_charset_t charset)
+{
+ if (ossl_isalnum(c))
+ return 1;
+ if (ossl_isascii(c))
+ return charset == OSSL_CHARSET_ASCII
+ || charset == OSSL_CHARSET_NONASCII;
+ return charset == OSSL_CHARSET_NONASCII;
+}
+
+/*
+ * Check for allowed characters in a dns name label.
+ * |charset| controls the strictness of the checking.
+ *
+ * if |charset|is OSSL_CHARSET_NONASCII, anything is allowed
+ * except the forbidden characters of '.' and '-'. This
+ * will make minimally valid structure be checked but nothing
+ * else.
+ *
+ * if |charset| is OSSL_CHARSET_ASCII all ascii characters
+ * are allowed except the forbidden characters of '.' and '-'.
+ *
+ * if |charset| is OSSL_CHARSET_ASCII_ALNUM all alphanumeric
+ * characters plus the character '_' are allowed except the forbidden
+ * characters of '.' and '-'.
+ */
+static int is_label_ok(int c, ossl_charset_t charset)
+{
+ if (!ossl_name_charset(c, charset) && c != '_')
return 0;
+ else
+ return c != '.' && c != '-';
+}
+
+/* Default host name input validation */
+static int validate_hostname_part(const char *name, size_t len,
+ ossl_charset_t charset)
+{
+ size_t i, part_len;
+ char c, prev;
- if (vpm->hosts == NULL && (vpm->hosts = sk_OPENSSL_STRING_new_null()) == NULL) {
- OPENSSL_free(copy);
+ if (len < 2 || len > 256)
return 0;
- }
- if (!sk_OPENSSL_STRING_push(vpm->hosts, copy)) {
- OPENSSL_free(copy);
- if (sk_OPENSSL_STRING_num(vpm->hosts) == 0) {
- sk_OPENSSL_STRING_free(vpm->hosts);
- vpm->hosts = NULL;
+ part_len = 0;
+ prev = '\0';
+ for (i = 0; i < len; i++) {
+ c = name[i];
+ if (c == '.') {
+ /* Can not start a label with a . */
+ if (part_len == 0)
+ return 0;
+ /* Can not end a label with a - */
+ if (prev == '-')
+ return 0;
+ part_len = 0;
+ } else {
+ if (!is_label_ok(c, charset) && c != '-')
+ return 0;
+ if (c == '-') {
+ /* Can not start a label with a - */
+ if (part_len == 0)
+ return 0;
+ }
}
+ part_len++;
+ if (part_len > 63)
+ return 0;
+
+ prev = c;
+ }
+ /* Can not end with a . or a _ */
+ if (prev == '.' || prev == '-')
return 0;
+
+ return 1;
+}
+
+static int validate_local_part(const char *name, size_t len,
+ ossl_charset_t *out_charset)
+{
+ ossl_charset_t charset = OSSL_CHARSET_ASCII;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (name[i] == '\0')
+ return 0;
+ if (!ossl_isascii(name[i]))
+ charset = OSSL_CHARSET_NONASCII;
}
+ *out_charset = charset;
return 1;
}
+/* Default email name input validation */
+static int validate_email_name(const char *name, size_t len, int rfc822)
+{
+ size_t dns_len, local_len;
+ char *at, *dnsname;
+ ossl_charset_t local_charset;
+
+ /*
+ * 64 for local part, 1 for @, 255 for domain name
+ */
+ if (len > 320)
+ return 0;
+
+ /* Reject it if there is no @ */
+ if ((at = memchr(name, '@', len)) == NULL)
+ return 0;
+
+ /* Ensure the local part is not oversize */
+ local_len = len - (at - name);
+ if (local_len > 64)
+ return 0;
+
+ if (!validate_local_part(name, len, &local_charset))
+ return 0;
+
+ if (rfc822 && local_charset == OSSL_CHARSET_NONASCII)
+ return 0;
+
+ if (!rfc822 && local_charset == OSSL_CHARSET_ASCII)
+ return 0;
+
+ /* What is after the @ must be valid as a dns name */
+ dnsname = at + 1;
+ dns_len = len - local_len - 1;
+
+ /* It may not have another @ */
+ if ((at = memchr(dnsname, '@', dns_len)) != NULL)
+ return 0;
+
+ if (rfc822)
+ return validate_hostname_part(dnsname, dns_len, OSSL_CHARSET_ASCII_ALNUM);
+
+ return validate_hostname_part(dnsname, dns_len, OSSL_CHARSET_NONASCII);
+}
+
X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void)
{
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);
+ clear_buffer_stack(¶m->hosts);
+ clear_buffer_stack(¶m->ips);
+ clear_buffer_stack(¶m->rfc822s);
+ clear_buffer_stack(¶m->smtputf8s);
OPENSSL_free(param->peername);
- OPENSSL_free(param->email);
- OPENSSL_free(param->ip);
OPENSSL_free(param);
}
x509_verify_param_copy(hostflags, 0);
if (test_x509_verify_param_copy(hosts, NULL)) {
- sk_OPENSSL_STRING_pop_free(dest->hosts, str_free);
- dest->hosts = NULL;
- if (src->hosts != NULL) {
- dest->hosts = sk_OPENSSL_STRING_deep_copy(src->hosts, str_copy, str_free);
- if (dest->hosts == NULL)
- return 0;
- }
+ if (!replace_buffer_stack(&dest->hosts, &src->hosts))
+ return 0;
+ }
+ x509_verify_param_copy(validate_host, NULL);
+
+ if (test_x509_verify_param_copy(ips, NULL)) {
+ if (!replace_buffer_stack(&dest->ips, &src->ips))
+ return 0;
}
+ x509_verify_param_copy(validate_ip, NULL);
- if (test_x509_verify_param_copy(email, NULL)) {
- if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen))
+ if (test_x509_verify_param_copy(rfc822s, NULL)) {
+ if (!replace_buffer_stack(&dest->rfc822s, &src->rfc822s))
return 0;
}
+ x509_verify_param_copy(validate_rfc822, NULL);
- if (test_x509_verify_param_copy(ip, NULL)) {
- if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen))
+ if (test_x509_verify_param_copy(smtputf8s, NULL)) {
+ if (!replace_buffer_stack(&dest->smtputf8s, &src->smtputf8s))
return 0;
}
+ x509_verify_param_copy(validate_smtputf8, NULL);
return 1;
}
return ret;
}
-static int int_x509_param_set1(char **pdest, size_t *pdestlen,
- const char *src, size_t srclen)
-{
- char *tmp;
-
- if (src != NULL) {
- if (srclen == 0)
- srclen = strlen(src);
-
- tmp = OPENSSL_malloc(srclen + 1);
- if (tmp == NULL)
- return 0;
- memcpy(tmp, src, srclen);
- tmp[srclen] = '\0'; /* enforce NUL termination */
- } else {
- tmp = NULL;
- srclen = 0;
- }
- OPENSSL_free(*pdest);
- *pdest = tmp;
- if (pdestlen != NULL)
- *pdestlen = srclen;
- return 1;
-}
-
int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name)
{
OPENSSL_free(param->name);
char *X509_VERIFY_PARAM_get0_host(X509_VERIFY_PARAM *param, int idx)
{
- return sk_OPENSSL_STRING_value(param->hosts, idx);
+ X509_BUFFER *buf = sk_X509_BUFFER_value(param->hosts, idx);
+
+ return (buf != NULL) ? (char *)buf->data : NULL;
}
int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
- const char *name, size_t namelen)
+ const char *dnsname, size_t len)
{
- return int_x509_param_set_hosts(param, SET_HOST, name, namelen);
+ clear_buffer_stack(¶m->hosts);
+ if (dnsname == NULL)
+ return 1;
+ if (len == 0)
+ len = strlen(dnsname);
+ if (len == 0)
+ return 1;
+ return X509_VERIFY_PARAM_add1_host(param, dnsname, len);
}
int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
- const char *name, size_t namelen)
+ const char *dnsname, size_t len)
+{
+ if (dnsname == NULL)
+ return 1;
+ if (len == 0)
+ len = strlen(dnsname);
+ if (len == 0)
+ return 1;
+ if (!validate_string_name(dnsname, &len))
+ return 0;
+ if (param->validate_host != NULL) {
+ if (!param->validate_host(dnsname, len))
+ return 0;
+ } else {
+ if (!validate_hostname_part(dnsname, len, OSSL_CHARSET_ASCII_ALNUM))
+ return 0;
+ }
+ return add_string_to_buffer_stack(¶m->hosts, (const uint8_t *)dnsname, len);
+}
+
+void X509_VERIFY_PARAM_set1_host_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_host)(const char *name, size_t len))
+{
+ param->validate_host = validate_host;
+}
+
+int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
+ const uint8_t *ip, size_t len)
+{
+ clear_buffer_stack(¶m->ips);
+ if (ip == NULL)
+ return 1;
+ return X509_VERIFY_PARAM_add1_ip(param, ip, len);
+}
+
+int X509_VERIFY_PARAM_add1_ip(X509_VERIFY_PARAM *param,
+ const uint8_t *ip, size_t len)
+{
+ if (param->validate_ip != NULL) {
+ if (!param->validate_ip(ip, len))
+ return 0;
+ } else {
+ if (!validate_ip_name(ip, len))
+ return 0;
+ }
+ return add_bytes_to_buffer_stack(¶m->ips, ip, len);
+}
+
+void X509_VERIFY_PARAM_set1_ip_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_ip)(const uint8_t *name, size_t len))
+{
+ param->validate_ip = validate_ip;
+}
+
+char *X509_VERIFY_PARAM_get0_email(X509_VERIFY_PARAM *param)
+{
+ X509_BUFFER *buf = sk_X509_BUFFER_value(param->rfc822s, 0);
+
+ if ((buf = sk_X509_BUFFER_value(param->rfc822s, 0)) != NULL
+ || (buf = sk_X509_BUFFER_value(param->smtputf8s, 0)) != NULL)
+ return (char *)buf->data;
+
+ return NULL;
+}
+
+int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
+ const char *email, size_t len)
+{
+ int ret = 0;
+
+ if (X509_VERIFY_PARAM_set1_smtputf8(param, email, len))
+ ret = 1;
+ if (X509_VERIFY_PARAM_set1_rfc822(param, email, len))
+ ret = 1;
+
+ return ret;
+}
+
+int X509_VERIFY_PARAM_set1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t len)
+{
+ clear_buffer_stack(¶m->smtputf8s);
+ if (email == NULL)
+ return 1;
+ return X509_VERIFY_PARAM_add1_smtputf8(param, email, len);
+}
+
+int X509_VERIFY_PARAM_add1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t len)
+{
+ if (len == 0)
+ len = strlen(email);
+ if (!validate_string_name(email, &len))
+ return 0;
+ if (param->validate_smtputf8 != NULL) {
+ if (!param->validate_smtputf8(email, len))
+ return 0;
+ } else {
+ if (!validate_email_name(email, len, /*rfc822 =*/0))
+ return 0;
+ }
+
+ return add_string_to_buffer_stack(¶m->smtputf8s,
+ (const uint8_t *)email, len);
+}
+
+void X509_VERIFY_PARAM_set1_smtputf8_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_smtputf8)(const char *name, size_t len))
+{
+ param->validate_smtputf8 = validate_smtputf8;
+}
+
+int X509_VERIFY_PARAM_set1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t len)
+{
+ clear_buffer_stack(¶m->rfc822s);
+ if (email == NULL)
+ return 1;
+ return X509_VERIFY_PARAM_add1_rfc822(param, email, len);
+}
+
+int X509_VERIFY_PARAM_add1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t len)
+{
+ if (len == 0)
+ len = strlen(email);
+ if (!validate_string_name(email, &len))
+ return 0;
+ if (param->validate_rfc822 != NULL) {
+ if (!param->validate_rfc822(email, len))
+ return 0;
+ } else {
+ if (!validate_email_name(email, len, /*rfc822 =*/1))
+ return 0;
+ }
+
+ return add_string_to_buffer_stack(¶m->rfc822s,
+ (const uint8_t *)email, len);
+}
+
+void X509_VERIFY_PARAM_set1_rfc822_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_rfc822)(const char *name, size_t len))
{
- return int_x509_param_set_hosts(param, ADD_HOST, name, namelen);
+ param->validate_rfc822 = validate_rfc822;
}
void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
from->peername = NULL;
}
-char *X509_VERIFY_PARAM_get0_email(X509_VERIFY_PARAM *param)
+static const unsigned char *int_X509_VERIFY_PARAM_get0_ip(X509_VERIFY_PARAM *param, size_t *plen, size_t idx)
{
- return param->email;
-}
+ X509_BUFFER *buf;
-int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
- const char *email, size_t emaillen)
-{
- return int_x509_param_set1(¶m->email, ¶m->emaillen,
- email, emaillen);
-}
+ if (idx > INT_MAX)
+ return NULL;
-static unsigned char *int_X509_VERIFY_PARAM_get0_ip(X509_VERIFY_PARAM *param, size_t *plen)
-{
- if (param == NULL || param->ip == NULL) {
+ buf = sk_X509_BUFFER_value(param->ips, (int)idx);
+
+ if (param == NULL || param->ips == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
- if (plen != NULL)
- *plen = param->iplen;
- return param->ip;
+
+ if (buf != NULL) {
+ if (plen != NULL)
+ *plen = buf->len;
+ return (unsigned char *)buf->data;
+ }
+ return NULL;
}
char *X509_VERIFY_PARAM_get1_ip_asc(X509_VERIFY_PARAM *param)
{
size_t iplen;
- unsigned char *ip = int_X509_VERIFY_PARAM_get0_ip(param, &iplen);
+ /* XXX casts away const */
+ unsigned char *ip = (unsigned char *)int_X509_VERIFY_PARAM_get0_ip(param, &iplen, 0);
return ip == NULL ? NULL : ossl_ipaddr_to_asc(ip, (int)iplen);
}
-int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
- const unsigned char *ip, size_t iplen)
+int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc)
{
- if (iplen != 0 && iplen != 4 && iplen != 16) {
- ERR_raise(ERR_LIB_X509, ERR_R_PASSED_INVALID_ARGUMENT);
+ unsigned char ipout[16];
+ size_t iplen;
+
+ if (ipasc == NULL)
+ return X509_VERIFY_PARAM_set1_ip(param, NULL, 0);
+ if ((iplen = (size_t)ossl_a2i_ipadd(ipout, ipasc)) == 0)
return 0;
- }
- return int_x509_param_set1((char **)¶m->ip, ¶m->iplen,
- (char *)ip, iplen);
+ return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen);
}
-int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc)
+int X509_VERIFY_PARAM_add1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc)
{
unsigned char ipout[16];
- size_t iplen = (size_t)ossl_a2i_ipadd(ipout, ipasc);
+ size_t iplen;
- if (iplen == 0)
+ if ((iplen = (size_t)ossl_a2i_ipadd(ipout, ipasc)) == 0)
return 0;
- return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen);
+ return X509_VERIFY_PARAM_add1_ip(param, ipout, iplen);
}
int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param)
return param->name;
}
-#define vpm_empty_id NULL, 0U, NULL, NULL, 0, NULL, 0
-
/*
* Default verify parameters: these are used for various applications and can
* be overridden by the user specified table. NB: the 'name' field *must* be
*/
static const X509_VERIFY_PARAM default_table[] = {
- { "code_sign", /* Code sign parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- 0, /* flags */
- X509_PURPOSE_CODE_SIGN, /* purpose */
- X509_TRUST_OBJECT_SIGN, /* trust */
- -1, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id },
- { "default", /* X509 default parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- X509_V_FLAG_TRUSTED_FIRST, /* flags */
- 0, /* purpose */
- 0, /* trust */
- 100, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id },
- { "pkcs7", /* S/MIME sign parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- 0, /* flags */
- X509_PURPOSE_SMIME_SIGN, /* purpose */
- X509_TRUST_EMAIL, /* trust */
- -1, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id },
- { "smime_sign", /* S/MIME sign parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- 0, /* flags */
- X509_PURPOSE_SMIME_SIGN, /* purpose */
- X509_TRUST_EMAIL, /* trust */
- -1, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id },
- { "ssl_client", /* SSL/TLS client parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- 0, /* flags */
- X509_PURPOSE_SSL_CLIENT, /* purpose */
- X509_TRUST_SSL_CLIENT, /* trust */
- -1, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id },
- { "ssl_server", /* SSL/TLS server parameters */
- 0, /* check time to use */
- 0, /* inheritance flags */
- 0, /* flags */
- X509_PURPOSE_SSL_SERVER, /* purpose */
- X509_TRUST_SSL_SERVER, /* trust */
- -1, /* depth */
- -1, /* auth_level */
- NULL, /* policies */
- vpm_empty_id }
+ {
+ .name = "code_sign", /* Code sign parameters */
+ .purpose = X509_PURPOSE_CODE_SIGN,
+ .trust = X509_TRUST_OBJECT_SIGN,
+ .depth = -1,
+ .auth_level = -1,
+ },
+ {
+ .name = "default", /* X509 default parameters */
+ .flags = X509_V_FLAG_TRUSTED_FIRST,
+ .depth = 100,
+ .auth_level = -1,
+ },
+ {
+ .name = "pkcs7", /* S/MIME sign parameters */
+ .purpose = X509_PURPOSE_SMIME_SIGN,
+ .trust = X509_TRUST_EMAIL,
+ .depth = -1,
+ .auth_level = -1,
+ },
+ {
+ .name = "smime_sign", /* S/MIME sign parameters */
+ .purpose = X509_PURPOSE_SMIME_SIGN,
+ .trust = X509_TRUST_EMAIL,
+ .depth = -1,
+ .auth_level = -1,
+ },
+ {
+ .name = "ssl_client", /* SSL/TLS client parameters */
+ .purpose = X509_PURPOSE_SSL_CLIENT,
+ .trust = X509_TRUST_SSL_CLIENT,
+ .depth = -1,
+ .auth_level = -1,
+ },
+ {
+ .name = "ssl_server", /* SSL/TLS server parameters */
+ .purpose = X509_PURPOSE_SSL_SERVER,
+ .trust = X509_TRUST_SSL_SERVER,
+ .depth = -1,
+ .auth_level = -1,
+ }
};
static STACK_OF(X509_VERIFY_PARAM) *param_table = NULL;
/* The BIO has parsed the host:port and even IPv6 literals in [] */
hostname = BIO_get_conn_hostname(out);
- if (!hostname || SSL_set1_host(ssl, hostname) <= 0) {
+ if (!hostname || SSL_set1_dnsname(ssl, hostname) <= 0) {
BIO_free(ssl_bio);
goto err;
}
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(ssl, hostname)) {
+ if (!SSL_set1_dnsname(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(ssl, hostname)) {
+ if (!SSL_set1_dnsname(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(ssl, hostname)) {
+ if (!SSL_set1_dnsname(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(ssl, hostname)) {
+ if (!SSL_set1_dnsname(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(ssl, hostname)) {
+ if (!SSL_set1_dnsname(ssl, hostname)) {
printf("Failed to set the certificate verification hostname");
goto end;
}
qconn_bio = NULL;
/* Set the hostname we will validate the X.509 certificate against. */
- if (SSL_set1_host(qconn, bare_hostname) <= 0)
+ if (SSL_set1_dnsname(qconn, bare_hostname) <= 0)
goto err;
/* Configure SNI */
/* Set hostname for SNI */
SSL_set_tlsext_host_name(ssl, rem_server_ip);
/* Configure server hostname check */
- if (!SSL_set1_host(ssl, rem_server_ip)) {
+ if (!SSL_set1_dnsname(ssl, rem_server_ip)) {
ERR_print_errors_fp(stderr);
goto exit;
}
The B<basedomain> argument specifies the RFC7671 TLSA base domain,
which will be the primary peer reference identifier for certificate
name checks.
-Additional server names can be specified via L<SSL_add1_host(3)>.
+Additional server names can be specified via L<SSL_add1_dnsname(3)>.
The B<basedomain> is used as the default SNI hint if none has yet been
specified via L<SSL_set_tlsext_host_name(3)>.
*/
SSL_dane_set_flags(ssl, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
- if (!SSL_add1_host(ssl, nexthop_domain))
+ if (!SSL_add1_dnsname(ssl, nexthop_domain))
/* error */
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
L<ssl(7)>,
L<SSL_new(3)>,
-L<SSL_add1_host(3)>,
+L<SSL_add1_dnsname(3)>,
L<SSL_set_hostflags(3)>,
L<SSL_set_tlsext_host_name(3)>,
L<SSL_set_verify(3)>,
=head1 NAME
-SSL_set1_host, SSL_add1_host, SSL_set_hostflags, SSL_get0_peername -
+SSL_set1_dnsname, SSL_add1_dnsname,
+SSL_set1_ipaddr, SSL_add1_ipaddr,
+SSL_set1_host, SSL_add1_host,
+SSL_set_hostflags, SSL_get0_peername -
SSL server verification parameters
=head1 SYNOPSIS
int SSL_set1_host(SSL *s, const char *host);
int SSL_add1_host(SSL *s, const char *host);
+ int SSL_set1_dnsname(SSL *s, const char *dnsname);
+ int SSL_add1_dnsname(SSL *s, const char *dnsname);
+ int SSL_set1_ipaddr(SSL *s, const uint8_t *ip, size_t len);
+ int SSL_add1_ipaddr(SSL *s, const uint8_t *ip size_t len);
void SSL_set_hostflags(SSL *s, unsigned int flags);
const char *SSL_get0_peername(SSL *s);
+ int SSL_set1_host(SSL *s, const char *host);
+ int SSL_add1_host(SSL *s, const char *host);
+
=head1 DESCRIPTION
-These functions configure server hostname checks in the SSL client.
+These functions maintain lists of expected matches for peer
+certificate subject alternate name (SAN) values is peer certificates
+presented in an SSL connection. A peer certificate will be considered
+a match for validation purposes if all of the following is true:
-SSL_set1_host() sets in the verification parameters of I<s>
-the expected DNS hostname or IP address to I<host>,
-clearing any previously specified IP address and hostnames.
-If I<host> is NULL or the empty string, IP address
-and hostname checks are not performed on the peer certificate.
-When a nonempty I<host> is specified, certificate verification automatically
-checks the peer hostname via L<X509_check_host(3)> with I<flags> as specified
-via SSL_set_hostflags(). Clients that enable DANE TLSA authentication
-via L<SSL_dane_enable(3)> should leave it to that function to set
-the primary reference identifier of the peer, and should not call
-SSL_set1_host().
+* Any name in the dnsname list, if not empty, matches any SAN dnsname
+ in the certificate. If verification flags allow it, these will also
+ attempt to match against the CN in the subject.
-SSL_add1_host() adds I<host> as an additional reference identifier
-that can match the peer's certificate. Any previous hostnames
-set via SSL_set1_host() or SSL_add1_host() are retained.
-Adding an IP address is allowed only if no IP address has been set before.
-No change is made if I<host> is NULL or empty.
-When an IP address and/or multiple hostnames are configured,
-the peer is considered verified when any of these matches.
-This function is required for DANE TLSA in the presence of service name indirection
-via CNAME, MX or SRV records as specified in RFCs 7671, 7672, and 7673.
-
-TLS clients are recommended to use SSL_set1_host() or SSL_add1_host()
-for server hostname or IP address validation,
-as well as L<SSL_set_tlsext_host_name(3)> for Server Name Indication (SNI),
-which may be crucial also for correct routing of the connection request.
+* Any address in the IP address list, if not empty, matches any IP
+ address SAN in the certificate.
+
+The set1 family of functions clears the list, and sets the first value
+to a the provided parameter if the provided parameter is not NULL.
+
+The add1 family of functions adds a single entry to the list.
+
+SSL_set1_dnsname() clears the list of dnsnames to match certificate
+SAN dnsnames.
+If I<dnsname> is not NULL, it will be checked for
+validity and added to the list of DNS name reference identifiers as its first entry.
+
+SSL_set1_ipaddr() clears the list of addresses to match certificate IP address SANs.
+If <ip_asc> is not NULL, it is parsed as an
+IPv4 or IPv6 address and added to the list as its first entry.
+
+SSL_add1_dnsname() adds I<dnsname> to the list of DNS name reference
+identifiers, if it has the correct structure for a DNS name. This
+function is required for DANE TLSA in the presence of service name
+indirection via CNAME, MX or SRV records as specified in RFCs 7671,
+7672, and 7673.
+
+SSL_add1_ipaddr() adds I<ip> to the list of IP addresses, if it parses as an IP address.
+
+It is recommended that TLS clients use SSL_set1_dnsname() to configure server
+hostname validation and L<SSL_set_tlsext_host_name(3)> to configure
+Server Name Indication (SNI), which may be crucial also for correct
+server certificate selection and/or routing of the connection request.
+SSL_set1_ip(), or SSL_set1_ip_asc()
+should be used for server IP address validation,
SSL_set_hostflags() sets the I<flags> that will be passed to
L<X509_check_host(3)> when name checks are applicable, by default
internal check will be suppressed as appropriate when DANE is
enabled.
+==head1 DEPRECATED FUNCTIONS
+
+SSL_set1_host and SSL_add1_host are deprecated as of OpenSSL 4.0.0.
+SSL_add1_dnsame and SSL_add1_ip should be used instead.
+
+SSL_set1_host() sets in the verification parameters of I<s>
+the expected DNS hostname or IP address to I<host>,
+clearing any previously specified IP address and hostnames.
+If I<host> is NULL or the empty string, IP address
+and hostname checks are not performed on the peer certificate.
+When a nonempty I<host> is specified, certificate verification automatically
+checks the peer hostname via L<X509_check_host(3)> with I<flags> as specified
+via SSL_set_hostflags(). Clients that enable DANE TLSA authentication
+via L<SSL_dane_enable(3)> should leave it to that function to set
+the primary reference identifier of the peer, and should not call
+SSL_set1_host().
+
+SSL_add1_host() adds I<host> as an additional reference identifier
+that can match the peer's certificate. Any previous hostnames set via
+SSL_set1_host() or SSL_add1_host() are retained. Adding an IP address
+is allowed only if no IP address has been set before. No change is
+made if I<host> is NULL or empty. The peer is considered verified
+when any of the added hostnames, if present, match, and the provided
+IP address, if present, matches.
+
=head1 RETURN VALUES
+SSL_set1_dnsname, SSL_set1_ip, SSL_set1_ip_asc,
+SSL_add1_dnsname, SSL_add1_ip, SSL_add1_ip_asc,
SSL_set1_host() and SSL_add1_host() return 1 for success and 0 for
failure.
the lifetime of the SSL connection.
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
- if (!SSL_set1_host(ssl, "smtp.example.com"))
+ if (!SSL_set1_dnsname(ssl, "smtp.example.com"))
/* error */
- if (!SSL_add1_host(ssl, "example.com"))
+ if (!SSL_add1_dnsname(ssl, "example.com"))
/* error */
/* XXX: Perform SSL_connect() handshake and handle errors here */
These functions were added in OpenSSL 1.1.0.
+SSL_set1_host and SSL_add1_host were deprecated in OpenSSL 4.0.0
+
=head1 COPYRIGHT
Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
X509_VERIFY_PARAM_set_hostflags,
X509_VERIFY_PARAM_get_hostflags,
X509_VERIFY_PARAM_get0_peername,
-X509_VERIFY_PARAM_get0_email, X509_VERIFY_PARAM_set1_email,
-X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_get1_ip_asc,
-X509_VERIFY_PARAM_set1_ip_asc
+X509_VERIFY_PARAM_get0_email,
+X509_VERIFY_PARAM_set1_email,
+X509_VERIFY_PARAM_set1_rfc822, X509_VERIFY_PARAM_add1_rfc822,
+X509_VERIFY_PARAM_set1_smtputf8, X509_VERIFY_PARAM_add1_smtputf8,
+X509_VERIFY_PARAM_set1_ip, X509_VERIFY_PARAM_add1_ip,
+X509_VERIFY_PARAM_set1_ip_asc, X509_VERIFY_PARAM_add1_ip_asc,
+X509_VERIFY_PARAM_get1_ip_asc,
+X509_VERIFY_PARAM_set1_host_input_validation,
+X509_VERIFY_PARAM_set1_rfc822_input_validation,
+X509_VERIFY_PARAM_set1_smtputf8_input_validation,
+X509_VERIFY_PARAM_set1_ip_input_validation
- X509 verification parameters
=head1 SYNOPSIS
char *X509_VERIFY_PARAM_get0_email(X509_VERIFY_PARAM *param);
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
const char *email, size_t emaillen);
+ int X509_VERIFY_PARAM_set1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
+ int X509_VERIFY_PARAM_add1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
+ int X509_VERIFY_PARAM_set1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
+ int X509_VERIFY_PARAM_add1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
char *X509_VERIFY_PARAM_get1_ip_asc(X509_VERIFY_PARAM *param);
int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
const unsigned char *ip, size_t iplen);
+ int X509_VERIFY_PARAM_add1_ip(X509_VERIFY_PARAM *param,
+ const unsigned char *ip, size_t iplen);
int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc);
+ int X509_VERIFY_PARAM_add1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc);
+ void X509_VERIFY_PARAM_set1_ip_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_ip)(const uint8_t *name, size_t len));
+ void X509_VERIFY_PARAM_set1_host_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_host)(const char *name, size_t len));
+ void X509_VERIFY_PARAM_set1_rfc822_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_rfc822)(const char *name, size_t len));
+ void X509_VERIFY_PARAM_set1_smtputf8_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_smtputf8)(const char *name, size_t len));
=head1 DESCRIPTION
X509_VERIFY_PARAM_get0_email() returns the expected RFC822 email address.
-X509_VERIFY_PARAM_set1_email() sets the expected RFC822 email address to
+X509_VERIFY_PARAM_set1_rfc822() clears all expected RFC822 email
+addresses, and sets the expected RFC822 email address to I<email>. If
+I<email> is NULL no expected address is set. Otherwise, if
+I<emaillen> is zero, I<email> must be NUL-terminated; if I<emaillen>
+is nonzero, I<emaillen> must be set to the length of I<email>. When
+any email address is specified, certificate verification automatically
+invokes L<X509_check_email(3)>.
+
+X509_VERIFY_PARAM_add1_rfc822() adds I<email> as an additional
+reference identifier that can match RFC822 email addresses in the
+peer's certificate. Any previous names set via
+X509_VERIFY_PARAM_set1_rfc822(), X509_VERIFY_PARAM_add1_rfc822(), or
+X509_VERIFY_PARAM_set1_email() are
+retained on success, no change is made on failure. It is a failure if
+email is NULL or the empty string.The peer is considered verified
+when any one of the specified RFC822 or SMTPUTF8 names matches a corresponding email
+address SAN in the certificate.
+
+X509_VERIFY_PARAM_set1_smtputf8() sets the expected SMTPUTF8 email address to
I<email>.
-If I<email> is NULL, email checking is disabled. Otherwise,
+If I<email> is NULL, SMTPUTF8 email checking is disabled. Otherwise,
if I<emaillen> is zero, I<email> must be NUL-terminated; if I<emaillen> is nonzero,
-I<emaillen> must be set to the length of I<email>. When an email address
+I<emaillen> must be set to the length of I<email>. When any email address
is specified, certificate verification automatically invokes
L<X509_check_email(3)>.
+X509_VERIFY_PARAM_add1_smtputf8() adds I<email> as an additional
+reference identifier that can match SMTPUTF8 email addresses in the
+peer's certificate. Any previous names set via
+X509_VERIFY_PARAM_set1_smtputf8(), X509_VERIFY_PARAM_add1_smtputf8(), or
+X509_VERIFY_PARAM_set1_email() are
+retained on success, no change is made on failure. It is a failure if
+email is NULL or the empty string. The peer is considered verified
+when any one of the specified RFC822 or SMTPUTF8 names matches a corresponding email
+address SAN in the certificate.
+
+X509_VERIFY_PARAM_set1_email() calls X509_VERIFY_PARAM_set_rfc822(), and
+X509_VERIFY_PARAM_set_smtputf8() and succeeds if any call succeeds.
+
X509_VERIFY_PARAM_get1_ip_asc() returns the expected IP address as a string.
The caller is responsible for freeing it.
address is specified, certificate verification automatically invokes
L<X509_check_ip(3)>.
+X509_VERIFY_PARAM_add1_ip() adds I<ip> as an additional reference
+identifier that can match the peer's certificate on success. Any
+previous names set via X509_VERIFY_PARAM_set1_ip(),
+X509_VERIFY_PARAM_add1_ip(), X509_VERIFY_PARAM_set1_ip_asc(), or
+X509_VERIFY_PARAM_add1_ip_asc() are retained. No change is made on
+failure. It is a failure if <ip> is NULL or the empty string. When
+multiple names are configured, the peer is considered verified when
+any name matches.
+
X509_VERIFY_PARAM_set1_ip_asc() sets the expected IP address to
I<ipasc>. The I<ipasc> argument must be a NUL-terminated ASCII string:
dotted decimal quad for IPv4 and colon-separated hexadecimal for
IPv6. The condensed "::" notation is supported for IPv6 addresses.
+X509_VERIFY_PARAM_add1_ip_asc() adds I<ip_asc> as an additional
+reference identifier that can match the peer's certificate on success.
+The I<ipasc> argument must be a NUL-terminated ASCII string: dotted
+decimal quad for IPv4 and colon-separated hexadecimal for IPv6. The
+condensed "::" notation is supported for IPv6 addresses. Any previous
+names set via X509_VERIFY_PARAM_set1_ip(),
+X509_VERIFY_PARAM_add1_ip(), X509_VERIFY_PARAM_set1_ip_asc(), or
+X509_VERIFY_PARAM_add1_ip_asc() are retained. No change is made on
+failure. It is a failure if I<ip_asc> is NULL or the empty string.
+When multiple names are configured, the peer is considered verified
+when any one of the specified addresses matches a corresponding IP address SAN in the certificate.
+
+X509_VERIFY_PARAM_set1_host_input_validation(),
+X509_VERIFY_PARAM_set1_rfc822_input_validation(),
+X509_VERIFY_PARAM_set1_smtputf8_input_validation(), and
+X509_VERIFY_PARAM_set1_ip_input_validation() set a verification
+function to validate the input on setting the corresponding validation
+parameter expected values. These functions bypass OpenSSL's input validation
+to these commands. if the provided function succeeds, the corresponding
+input will be accepted and attempted to be used when verifying certificates.
+
=head1 RETURN VALUES
X509_VERIFY_PARAM_set_flags(), X509_VERIFY_PARAM_clear_flags(),
X509_VERIFY_PARAM_set_purpose(), X509_VERIFY_PARAM_set_trust(),
X509_VERIFY_PARAM_add0_policy() X509_VERIFY_PARAM_set1_policies(),
X509_VERIFY_PARAM_set1_host(), X509_VERIFY_PARAM_add1_host(),
-X509_VERIFY_PARAM_set1_email(), X509_VERIFY_PARAM_set1_ip() and
-X509_VERIFY_PARAM_set1_ip_asc() return 1 for success and 0 for
+X509_VERIFY_PARAM_set1_email(),
+X509_VERIFY_PARAM_set1_ip(), X509_VERIFY_PARAM_add1_ip(),
+X509_VERIFY_PARAM_set1_ip_asc(),
+X509_VERIFY_PARAM_add1_ip_asc() return 1 for success and 0 for
failure.
X509_VERIFY_PARAM_get0_host(), X509_VERIFY_PARAM_get0_email(), and
__owur int SSL_CTX_set_trust(SSL_CTX *ctx, int trust);
__owur int SSL_set_trust(SSL *ssl, int trust);
-__owur int SSL_set1_host(SSL *s, const char *host);
-__owur int SSL_add1_host(SSL *s, const char *host);
+#ifndef OPENSSL_NO_DEPRECATED_4_0
+OSSL_DEPRECATEDIN_4_0 __owur int SSL_set1_host(SSL *s, const char *host);
+OSSL_DEPRECATEDIN_4_0 __owur int SSL_add1_host(SSL *s, const char *host);
+#endif /* OPENSSL_NO_DEPRECATED_4_0 */
+__owur int SSL_set1_dnsname(SSL *s, const char *dnsname);
+__owur int SSL_add1_dnsname(SSL *s, const char *dnsname);
+__owur int SSL_set1_ipaddr(SSL *s, const char *ipaddr);
+__owur int SSL_add1_ipaddr(SSL *s, const char *ipaddr);
__owur const char *SSL_get0_peername(SSL *s);
void SSL_set_hostflags(SSL *s, unsigned int flags);
const char *name, size_t namelen);
int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
const char *name, size_t namelen);
+void X509_VERIFY_PARAM_set1_host_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_host)(const char *name, size_t len));
void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
unsigned int flags);
unsigned int X509_VERIFY_PARAM_get_hostflags(const X509_VERIFY_PARAM *param);
char *X509_VERIFY_PARAM_get0_email(X509_VERIFY_PARAM *param);
int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
const char *email, size_t emaillen);
+int X509_VERIFY_PARAM_set1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
+int X509_VERIFY_PARAM_add1_rfc822(X509_VERIFY_PARAM *param,
+ const char *email, size_t len);
+void X509_VERIFY_PARAM_set1_rfc822_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_rfc822)(const char *name, size_t len));
+int X509_VERIFY_PARAM_set1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t emaillen);
+int X509_VERIFY_PARAM_add1_smtputf8(X509_VERIFY_PARAM *param,
+ const char *email, size_t len);
+void X509_VERIFY_PARAM_set1_smtputf8_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_smtputf8)(const char *name, size_t len));
char *X509_VERIFY_PARAM_get1_ip_asc(X509_VERIFY_PARAM *param);
int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
- const unsigned char *ip, size_t iplen);
+ const uint8_t *ip, size_t iplen);
+void X509_VERIFY_PARAM_set1_ip_input_validation(X509_VERIFY_PARAM *param,
+ int (*validate_ip)(const uint8_t *name, size_t len));
int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param,
const char *ipasc);
+int X509_VERIFY_PARAM_add1_ip(X509_VERIFY_PARAM *param,
+ const uint8_t *ip, size_t len);
+int X509_VERIFY_PARAM_add1_ip_asc(X509_VERIFY_PARAM *param,
+ const char *ipasc);
int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param);
int X509_VERIFY_PARAM_get_auth_level(const X509_VERIFY_PARAM *param);
return X509_VERIFY_PARAM_set_trust(sc->param, trust);
}
+int SSL_set1_dnsname(SSL *s, const char *host)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+
+ if (sc == NULL)
+ return 0;
+
+ return X509_VERIFY_PARAM_set1_host(sc->param, host, 0);
+}
+
+int SSL_add1_dnsname(SSL *s, const char *host)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+
+ if (sc == NULL)
+ return 0;
+
+ return X509_VERIFY_PARAM_add1_host(sc->param, host, strlen(host));
+}
+
+int SSL_set1_ipaddr(SSL *s, const char *ipaddr)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+
+ if (sc == NULL)
+ return 0;
+
+ return X509_VERIFY_PARAM_set1_ip_asc(sc->param, ipaddr);
+}
+
+int SSL_add1_ipaddr(SSL *s, const char *ipaddr)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
+
+ if (sc == NULL)
+ return 0;
+
+ return X509_VERIFY_PARAM_add1_ip_asc(sc->param, ipaddr);
+}
+
+#if !defined(OPENSSL_NO_DEPRECATED_4_0)
int SSL_set1_host(SSL *s, const char *host)
{
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
return X509_VERIFY_PARAM_add1_host(sc->param, host, 0);
}
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_0) */
void SSL_set_hostflags(SSL *s, unsigned int flags)
{
* Virtually all clients should do this unless you really know what you
* are doing.
*/
- if (!SSL_set1_host(*ssl, hostname)) {
+ if (!SSL_set1_dnsname(*ssl, hostname)) {
fprintf(stderr, "Failed to set the certificate verification hostname");
goto end;
}
return ret;
}
+static const char *multiname_cert[] = {
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIFnDCCBISgAwIBAgIUTgfdSQm2hjgUZoA8jeQX7sDPAoowDQYJKoZIhvcNAQEL\n"
+ "BQAwgYUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdBbGJlcnRhMREwDwYDVQQHDAhF\n"
+ "ZG1vbnRvbjERMA8GA1UECgwITXVwcGV0cnkxITAfBgNVBAsMGFN0YXRsZXIgYW5k\n"
+ "IFdhbGRvcmYgUiBVUzEbMBkGA1UEAwwSYmVha2VyLm11cHBldHJ5LmNhMB4XDTI2\n"
+ "MDExMjIwNTUwOVoXDTI3MDExMjIwNTUwOVowgYUxCzAJBgNVBAYTAkNBMRAwDgYD\n"
+ "VQQIDAdBbGJlcnRhMREwDwYDVQQHDAhFZG1vbnRvbjERMA8GA1UECgwITXVwcGV0\n"
+ "cnkxITAfBgNVBAsMGFN0YXRsZXIgYW5kIFdhbGRvcmYgUiBVUzEbMBkGA1UEAwwS\n"
+ "YmVha2VyLm11cHBldHJ5LmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n"
+ "AQEA+EsGQCX4YyZF3QbVcFUcWpYDp8MJHr5vF0cosvj9afGPhpLREWR7EmnNA8Gf\n"
+ "wb+ef/jNrDg8W81uDD3N29PvbM+hHAQPaHrRupQZ+W+uIVEAu/lpI359jIRS1Sey\n"
+ "IcU2vIgn3Tlnv4UX3o3QMyH8+RcCvSNrWu4+f9ipMAy/xq3PWBm+fHi/+bI03eDy\n"
+ "0xNm8kpXbhqZQiZ1tAhsTa3V2pIufqAnctDgl2GUHtfmKO095OHimjhQXHxO8Ctk\n"
+ "R+vFv0nleJoAAfkmaMdtdTd1O8m3AtQv6xQC4X5Tu/+FKKQOXjf/8OtqW2lrlxxR\n"
+ "pbFuy66I9HVyf+gGWEbZyqbCpwIDAQABo4ICADCCAfwwggG3BgNVHREEggGuMIIB\n"
+ "qoILbXVwcGV0cnkuY2GCD3d3dy5tdXBwZXRyeS5jYYITc3RhdGxlci5tdXBwZXRy\n"
+ "eS5jYYITd2FsZG9yZi5tdXBwZXRyeS5jYYETc3RhdGxlckBtdXBwdGVyeS5jYYET\n"
+ "d2FsZG9yZkBtdXBwdGVyeS5jYYcExikABIcQIAEFA7o+AAAAAAAAAAIAMIcEqveq\n"
+ "AocQKAEBuAAQAAAAAAAAAAAAC4cEwCEEDIcQIAEFAAACAAAAAAAAAAAADIcExwdb\n"
+ "DYcQIAEFAAAtAAAAAAAAAAAADYcEwMvmCocQIAEFAACoAAAAAAAAAAAADocEwAUF\n"
+ "8YcQIAEFAAAvAAAAAAAAAAAAD4cEwHAkBIcQIAEFAAASAAAAAAAAAAANDYcExmG+\n"
+ "NYcQIAEFAAABAAAAAAAAAAAAU4cEwCSUEYcQIAEH/gAAAAAAAAAAAAAAU4cEwDqA\n"
+ "HocQIAEFAwwnAAAAAAAAAAIAMIcEwQAOgYcQIAEH/QAAAAAAAAAAAAAAAYcExwdT\n"
+ "KocQIAEFAACfAAAAAAAAAAAAQocEygwbIYcQIAENwwAAAAAAAAAAAAAANTALBgNV\n"
+ "HQ8EBAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFCutBN63ufhB\n"
+ "IY4dOuFcYfC3p+mMMA0GCSqGSIb3DQEBCwUAA4IBAQBBWfTvwxV1s3xaS5Ko6T7B\n"
+ "vS7TPih0MO8auv0mvZXG3jy/LfAfgu05PbGIf0dzFhBpoZD0VrrugmdemLkJd+u6\n"
+ "pbEttGFZtcGb//MtjUAYQnEq6fYgDeT0dGU0upwQPWGgh5LpFSab+71C6Ofc3YFM\n"
+ "WPH7UaRBUV2mqNtUokOce6kYtl97St7p6cGpQW9Q1uFQODvAm3ZPq/YNGnTJAOdb\n"
+ "9UX8Td1T5fH86H0hb6qB0AEhVdgjPUgs33zYNWRPg8fYleT6w1MpE2HaUqqhld3B\n"
+ "ZtVZ5IznkY+8qH0rua89m4TV3qzUqNVUL0uxkWnQI3W8g3Adin7QN3EA6ZYrTD3q\n"
+ "-----END CERTIFICATE-----\n",
+ NULL,
+};
+
+static const time_t multiname_valid_at = 1768253189;
+
+static const char *multiname_dnsnames[] = {
+ "muppetry.ca",
+ "www.muppetry.ca",
+ "statler.muppetry.ca",
+ "waldorf.muppetry.ca",
+ NULL,
+};
+
+static const char *multiname_emails[] = {
+ "statler@mupptery.ca",
+ "waldorf@mupptery.ca",
+ NULL,
+};
+
+static const char *multiname_ips[] = {
+ "198.41.0.4",
+ "2001:503:ba3e::2:30",
+ "170.247.170.2",
+ "2801:1b8:10::b",
+ "192.33.4.12",
+ "2001:500:2::c",
+ "199.7.91.13",
+ "2001:500:2d::d",
+ "192.203.230.10",
+ "2001:500:a8::e",
+ "192.5.5.241",
+ "2001:500:2f::f",
+ "192.112.36.4",
+ "2001:500:12::d0d",
+ "198.97.190.53",
+ "2001:500:1::53",
+ "192.36.148.17",
+ "2001:7fe::53",
+ "192.58.128.30",
+ "2001:503:c27::2:30",
+ "193.0.14.129",
+ "2001:7fd::1",
+ "199.7.83.42",
+ "2001:500:9f::42",
+ "202.12.27.33",
+ "2001:dc3::35",
+ NULL,
+};
+
+static int test_multiname_selfsigned(void)
+{
+ X509 *cert = NULL;
+ X509_STORE_CTX *ctx = NULL;
+ X509_STORE *store = NULL;
+ X509_VERIFY_PARAM *vpm = NULL;
+ int fails = 0;
+ int ret = 0;
+
+ if (!TEST_ptr((cert = X509_from_strings(multiname_cert))))
+ goto err;
+
+ if (!TEST_true(X509_self_signed(cert, 1)))
+ goto err;
+
+ if (!TEST_ptr(store = X509_STORE_new()))
+ goto err;
+
+ if (!TEST_true(X509_STORE_add_cert(store, cert)))
+ goto err;
+
+ if (!TEST_ptr((vpm = X509_STORE_get0_param(store))))
+ goto err;
+
+ if (!TEST_ptr(ctx = X509_STORE_CTX_new()))
+ goto err;
+
+ X509_VERIFY_PARAM_set_time(vpm, multiname_valid_at);
+
+ for (size_t i = 0; multiname_dnsnames[i] != NULL; i++) {
+ /* Try one not in the certificate */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, "bunsen.muppetry.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx))) {
+ TEST_info("Verify succeeded for non-present name bunsen.muppetry.ca\n");
+ goto err;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, NULL, 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx)))
+ goto err;
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, multiname_dnsnames[i], strlen(multiname_dnsnames[i]))))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed for initial name %s\n", multiname_dnsnames[i]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ for (size_t j = 0; multiname_dnsnames[j] != NULL; j++) {
+ if (j != i) {
+ if (!TEST_true(X509_VERIFY_PARAM_add1_host(vpm, multiname_dnsnames[j], 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed with added name %s\n", multiname_dnsnames[j]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ }
+ }
+ /* Try the CN */
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, "beaker.muppetry.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed for CN name beaker.muppetry.ca\n");
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, NULL, 0)))
+ goto err;
+ }
+
+ for (size_t i = 0; multiname_emails[i] != NULL; i++) {
+ /* Try one not in the certificate */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, "bunsen@muppetry.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx))) {
+ TEST_info("Verify succeeded for non-present name bunsen@muppetry.ca\n");
+ goto err;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, NULL, 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx)))
+ goto err;
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, multiname_emails[i], strlen(multiname_emails[i]))))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed for initial name %s\n", multiname_emails[i]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ for (size_t j = 0; multiname_emails[j] != NULL; j++) {
+ if (j != i) {
+ if (!TEST_true(X509_VERIFY_PARAM_add1_rfc822(vpm, multiname_emails[j], 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed with added name %s\n", multiname_emails[j]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ }
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, NULL, 0)))
+ goto err;
+ }
+
+ for (size_t i = 0; multiname_ips[i] != NULL; i++) {
+ /* Try one not in the certificate */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, "8.8.8.8")))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx))) {
+ TEST_info("Verify succeeded for non-present name 8.8.8.8\n");
+ goto err;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, NULL)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx)))
+ goto err;
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, multiname_ips[i])))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed for initial name %s\n", multiname_ips[i]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ for (size_t j = 0; multiname_ips[j] != NULL; j++) {
+ if (j != i) {
+ if (!TEST_true(X509_VERIFY_PARAM_add1_ip_asc(vpm, multiname_ips[j])))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx))) {
+ TEST_info("Verify failed with added name %s\n", multiname_ips[j]);
+ fails++;
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ }
+ }
+ X509_STORE_CTX_cleanup(ctx);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, NULL)))
+ goto err;
+ }
+
+ /*
+ * Test that individual categories work together, and a non-match will still fail validation
+ */
+
+ /* A dnsname, email and ip that are all valid in the cert should succeed */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, "www.muppetry.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, "2001:503:ba3e::2:30")))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, "waldorf@mupptery.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx)))
+ fails++;
+ X509_STORE_CTX_cleanup(ctx);
+
+ /* Setting an non-matching email should fail validation even with valid dnsname and ip */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, "bunsen@mupptery.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx)))
+ fails++;
+ X509_STORE_CTX_cleanup(ctx);
+ /* reset */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, "waldorf@mupptery.ca", 0)))
+ goto err;
+
+ /* Setting an non-matching ip should fail validation even with valid dnsname and email */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, "199.185.178.80")))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx)))
+ fails++;
+ X509_STORE_CTX_cleanup(ctx);
+ /* reset */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, "2001:503:ba3e::2:30")))
+ goto err;
+
+ /* Setting an non-matching dnsname should fail validation even with valid ip and email */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, "www.libressl.org", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_false(X509_verify_cert(ctx)))
+ fails++;
+ X509_STORE_CTX_cleanup(ctx);
+ /* reset */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, "www.muppetry.ca", 0)))
+ goto err;
+
+ /* Adding non-matching values to each category with a match will still succeed */
+ if (!TEST_true(X509_VERIFY_PARAM_add1_host(vpm, "www.libressl.org", 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_add1_ip_asc(vpm, "199.185.178.80")))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_add1_rfc822(vpm, "beck@openbsd.org", 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_add1_smtputf8(vpm, "学生@muppetry.ca", 0)))
+ goto err;
+ if (!TEST_true(X509_STORE_CTX_init(ctx, store, cert, NULL)))
+ goto err;
+ if (!TEST_true(X509_verify_cert(ctx)))
+ fails++;
+ X509_STORE_CTX_cleanup(ctx);
+
+ ret = fails == 0;
+
+err:
+ X509_STORE_free(store);
+ X509_STORE_CTX_free(ctx);
+ X509_free(cert);
+ return ret;
+}
+
+static int yolo_name_validation(const char *name, size_t len)
+{
+ return 1;
+}
+
+static int yolo_ip_validation(const uint8_t *name, size_t len)
+{
+ return 1;
+}
+
+static int test_vpm_input_validation(void)
+{
+ const char *utf8mail = "学生@muppetry.ca";
+ const char *rfc822mail = "beaker@muppetry.ca";
+ X509_VERIFY_PARAM *vpm = NULL;
+ int ret = 0;
+
+ if (!TEST_ptr(vpm = X509_VERIFY_PARAM_new()))
+ goto err;
+
+ if (!TEST_false(X509_VERIFY_PARAM_set1_rfc822(vpm, utf8mail, 0)))
+ goto err;
+ if (!TEST_false(X509_VERIFY_PARAM_set1_smtputf8(vpm, rfc822mail, 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_rfc822(vpm, rfc822mail, 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_smtputf8(vpm, utf8mail, 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, rfc822mail, 0)))
+ goto err;
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, utf8mail, 0)))
+ goto err;
+
+ for (size_t i = 0; multiname_dnsnames[i] != NULL; i++) {
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, multiname_dnsnames[i], 0)))
+ goto err;
+ if (!TEST_false(X509_VERIFY_PARAM_set1_email(vpm, multiname_dnsnames[i], 0)))
+ goto err;
+ }
+ for (size_t i = 0; multiname_emails[i] != NULL; i++) {
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, multiname_emails[i], 0)))
+ goto err;
+ if (!TEST_false(X509_VERIFY_PARAM_set1_host(vpm, multiname_emails[i], 0)))
+ goto err;
+ }
+ for (size_t i = 0; multiname_ips[i] != NULL; i++) {
+ size_t l = strlen(multiname_ips[i]);
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, multiname_ips[i])))
+ goto err;
+ if (l == 4 || l == 16) {
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip(vpm, (const uint8_t *)multiname_ips[i], l)))
+ goto err;
+ } else {
+ if (!TEST_false(X509_VERIFY_PARAM_set1_ip(vpm, (const uint8_t *)multiname_ips[i], l)))
+ goto err;
+ }
+ }
+
+ X509_VERIFY_PARAM_set1_host_input_validation(vpm, yolo_name_validation);
+ X509_VERIFY_PARAM_set1_rfc822_input_validation(vpm, yolo_name_validation);
+ X509_VERIFY_PARAM_set1_smtputf8_input_validation(vpm, yolo_name_validation);
+ X509_VERIFY_PARAM_set1_ip_input_validation(vpm, yolo_ip_validation);
+ for (size_t i = 0; multiname_dnsnames[i] != NULL; i++) {
+ /* should still work */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, multiname_dnsnames[i], 0)))
+ goto err;
+ /* should be accepted now */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, multiname_dnsnames[i], 0)))
+ goto err;
+ }
+ for (size_t i = 0; multiname_emails[i] != NULL; i++) {
+ /* should still work */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_email(vpm, multiname_emails[i], 0)))
+ goto err;
+ /* should be accepted now */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_host(vpm, multiname_emails[i], 0)))
+ goto err;
+ }
+ for (size_t i = 0; multiname_ips[i] != NULL; i++) {
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip_asc(vpm, multiname_ips[i])))
+ goto err;
+ /* should be accepted now */
+ if (!TEST_true(X509_VERIFY_PARAM_set1_ip(vpm, (const uint8_t *)multiname_ips[i], strlen(multiname_ips[i]))))
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ X509_VERIFY_PARAM_free(vpm);
+ return ret;
+}
+
static int test_self_signed_good(void)
{
return test_self_signed(root_f, 1, 1);
ADD_TEST(test_purpose_ssl_client);
ADD_TEST(test_purpose_ssl_server);
ADD_TEST(test_purpose_any);
+ ADD_TEST(test_multiname_selfsigned);
+ ADD_TEST(test_vpm_input_validation);
return 1;
err:
cleanup_tests();
X509_VERIFY_PARAM_get0_host ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_set1_host ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_add1_host ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_host_input_validation ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_set_hostflags ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get_hostflags ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get0_peername ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_move_peername ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get0_email ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_set1_email ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_rfc822 ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_add1_rfc822 ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_rfc822_input_validation ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_smtputf8 ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_add1_smtputf8 ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_smtputf8_input_validation ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get1_ip_asc ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_set1_ip ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_set1_ip_asc ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_add1_ip ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_add1_ip_asc ? 4_0_0 EXIST::FUNCTION:
+X509_VERIFY_PARAM_set1_ip_input_validation ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get_depth ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get_auth_level ? 4_0_0 EXIST::FUNCTION:
X509_VERIFY_PARAM_get0_name ? 4_0_0 EXIST::FUNCTION:
SSL_set_purpose ? 4_0_0 EXIST::FUNCTION:
SSL_CTX_set_trust ? 4_0_0 EXIST::FUNCTION:
SSL_set_trust ? 4_0_0 EXIST::FUNCTION:
-SSL_set1_host ? 4_0_0 EXIST::FUNCTION:
-SSL_add1_host ? 4_0_0 EXIST::FUNCTION:
+SSL_set1_host ? 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_0
+SSL_add1_host ? 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_0
+SSL_set1_dnsname ? 4_0_0 EXIST::FUNCTION:
+SSL_add1_dnsname ? 4_0_0 EXIST::FUNCTION:
+SSL_set1_ipaddr ? 4_0_0 EXIST::FUNCTION:
+SSL_add1_ipaddr ? 4_0_0 EXIST::FUNCTION:
SSL_get0_peername ? 4_0_0 EXIST::FUNCTION:
SSL_set_hostflags ? 4_0_0 EXIST::FUNCTION:
SSL_CTX_dane_enable ? 4_0_0 EXIST::FUNCTION: