1*26433cb1Stb /* $OpenBSD: tls_verify.c,v 1.32 2024/12/10 08:40:30 tb Exp $ */ 2b600beedSjsing /* 3b600beedSjsing * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> 4b600beedSjsing * 5b600beedSjsing * Permission to use, copy, modify, and distribute this software for any 6b600beedSjsing * purpose with or without fee is hereby granted, provided that the above 7b600beedSjsing * copyright notice and this permission notice appear in all copies. 8b600beedSjsing * 9b600beedSjsing * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10b600beedSjsing * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b600beedSjsing * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12b600beedSjsing * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b600beedSjsing * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b600beedSjsing * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b600beedSjsing * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b600beedSjsing */ 17b600beedSjsing 18b600beedSjsing #include <sys/socket.h> 19b600beedSjsing 20b600beedSjsing #include <arpa/inet.h> 21b600beedSjsing #include <netinet/in.h> 22b600beedSjsing 23b600beedSjsing #include <string.h> 24b600beedSjsing 25b600beedSjsing #include <openssl/x509v3.h> 26b600beedSjsing 27ed19021fSbcook #include <tls.h> 28b600beedSjsing #include "tls_internal.h" 29b600beedSjsing 3040a2182fSjsing static int 310ca7b9dfSjsing tls_match_name(const char *cert_name, const char *name) 32b600beedSjsing { 33b600beedSjsing const char *cert_domain, *domain, *next_dot; 34b600beedSjsing 350ca7b9dfSjsing if (strcasecmp(cert_name, name) == 0) 36b600beedSjsing return 0; 37b600beedSjsing 38b600beedSjsing /* Wildcard match? */ 390ca7b9dfSjsing if (cert_name[0] == '*') { 40b600beedSjsing /* 41b600beedSjsing * Valid wildcards: 42b600beedSjsing * - "*.domain.tld" 43b600beedSjsing * - "*.sub.domain.tld" 44b600beedSjsing * - etc. 45b600beedSjsing * Reject "*.tld". 46b600beedSjsing * No attempt to prevent the use of eg. "*.co.uk". 47b600beedSjsing */ 480ca7b9dfSjsing cert_domain = &cert_name[1]; 49b600beedSjsing /* Disallow "*" */ 50b600beedSjsing if (cert_domain[0] == '\0') 51b600beedSjsing return -1; 52b600beedSjsing /* Disallow "*foo" */ 53b600beedSjsing if (cert_domain[0] != '.') 54b600beedSjsing return -1; 55b600beedSjsing /* Disallow "*.." */ 56b600beedSjsing if (cert_domain[1] == '.') 57b600beedSjsing return -1; 58b600beedSjsing next_dot = strchr(&cert_domain[1], '.'); 59b600beedSjsing /* Disallow "*.bar" */ 60b600beedSjsing if (next_dot == NULL) 61b600beedSjsing return -1; 62b600beedSjsing /* Disallow "*.bar.." */ 63b600beedSjsing if (next_dot[1] == '.') 64b600beedSjsing return -1; 65b600beedSjsing 660ca7b9dfSjsing domain = strchr(name, '.'); 67b600beedSjsing 68e6171fc4Sbeck /* No wildcard match against a name with no host part. */ 69e6171fc4Sbeck if (name[0] == '.') 70e6171fc4Sbeck return -1; 710ca7b9dfSjsing /* No wildcard match against a name with no domain part. */ 72b600beedSjsing if (domain == NULL || strlen(domain) == 1) 73b600beedSjsing return -1; 74b600beedSjsing 75b600beedSjsing if (strcasecmp(cert_domain, domain) == 0) 76b600beedSjsing return 0; 77b600beedSjsing } 78b600beedSjsing 79b600beedSjsing return -1; 80b600beedSjsing } 81b600beedSjsing 825f3c5205Sjsing /* 835f3c5205Sjsing * See RFC 5280 section 4.2.1.6 for SubjectAltName details. 845f3c5205Sjsing * alt_match is set to 1 if a matching alternate name is found. 855f3c5205Sjsing * alt_exists is set to 1 if any known alternate name exists in the certificate. 865f3c5205Sjsing */ 8740a2182fSjsing static int 885f3c5205Sjsing tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name, 895f3c5205Sjsing int *alt_match, int *alt_exists) 90b600beedSjsing { 91b600beedSjsing STACK_OF(GENERAL_NAME) *altname_stack = NULL; 92fc5c813dSjsing union tls_addr addrbuf; 93b600beedSjsing int addrlen, type; 94b600beedSjsing int count, i; 95c25290f5Stb int critical = 0; 966adcb85dStb int rv = -1; 975f3c5205Sjsing 985f3c5205Sjsing *alt_match = 0; 995f3c5205Sjsing *alt_exists = 0; 100b600beedSjsing 101c25290f5Stb altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, &critical, 102c25290f5Stb NULL); 103c25290f5Stb if (altname_stack == NULL) { 104c25290f5Stb if (critical != -1) { 1057a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, 1067a756d37Sjoshua "error decoding subjectAltName"); 1076adcb85dStb goto err; 108c25290f5Stb } 1096adcb85dStb goto done; 110c25290f5Stb } 111b600beedSjsing 1120ca7b9dfSjsing if (inet_pton(AF_INET, name, &addrbuf) == 1) { 113b600beedSjsing type = GEN_IPADD; 114b600beedSjsing addrlen = 4; 1150ca7b9dfSjsing } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { 116b600beedSjsing type = GEN_IPADD; 117b600beedSjsing addrlen = 16; 118b600beedSjsing } else { 119b600beedSjsing type = GEN_DNS; 120b600beedSjsing addrlen = 0; 121b600beedSjsing } 122b600beedSjsing 123b600beedSjsing count = sk_GENERAL_NAME_num(altname_stack); 124b600beedSjsing for (i = 0; i < count; i++) { 125b600beedSjsing GENERAL_NAME *altname; 126b600beedSjsing 127b600beedSjsing altname = sk_GENERAL_NAME_value(altname_stack, i); 1285f3c5205Sjsing 1295f3c5205Sjsing if (altname->type == GEN_DNS || altname->type == GEN_IPADD) 1305f3c5205Sjsing *alt_exists = 1; 1315f3c5205Sjsing 132b600beedSjsing if (altname->type != type) 133b600beedSjsing continue; 134b600beedSjsing 135b600beedSjsing if (type == GEN_DNS) { 1363130d2a2Sop const unsigned char *data; 137c81cfcc3Sdoug int format, len; 138b600beedSjsing 139b600beedSjsing format = ASN1_STRING_type(altname->d.dNSName); 140b600beedSjsing if (format == V_ASN1_IA5STRING) { 1413130d2a2Sop data = ASN1_STRING_get0_data(altname->d.dNSName); 142c81cfcc3Sdoug len = ASN1_STRING_length(altname->d.dNSName); 143b600beedSjsing 144c8403018Sjsing if (len < 0 || (size_t)len != strlen(data)) { 1457a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, 1460ca7b9dfSjsing "error verifying name '%s': " 1479a94eeb1Sbcook "NUL byte in subjectAltName, " 1489a94eeb1Sbcook "probably a malicious certificate", 1490ca7b9dfSjsing name); 1506adcb85dStb goto err; 151b600beedSjsing } 152b600beedSjsing 153da51b25bSdoug /* 154da51b25bSdoug * Per RFC 5280 section 4.2.1.6: 155da51b25bSdoug * " " is a legal domain name, but that 156da51b25bSdoug * dNSName must be rejected. 157da51b25bSdoug */ 158da51b25bSdoug if (strcmp(data, " ") == 0) { 1597a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, 160da51b25bSdoug "error verifying name '%s': " 161da51b25bSdoug "a dNSName of \" \" must not be " 162da51b25bSdoug "used", name); 1636adcb85dStb goto err; 164da51b25bSdoug } 165da51b25bSdoug 1660ca7b9dfSjsing if (tls_match_name(data, name) == 0) { 1675f3c5205Sjsing *alt_match = 1; 1686adcb85dStb goto done; 169b600beedSjsing } 1709a94eeb1Sbcook } else { 1719a94eeb1Sbcook #ifdef DEBUG 17231a3a711Sbcook fprintf(stdout, "%s: unhandled subjectAltName " 17331a3a711Sbcook "dNSName encoding (%d)\n", getprogname(), 17431a3a711Sbcook format); 1759a94eeb1Sbcook #endif 1769a94eeb1Sbcook } 177b600beedSjsing 178b600beedSjsing } else if (type == GEN_IPADD) { 1793130d2a2Sop const unsigned char *data; 180b600beedSjsing int datalen; 181b600beedSjsing 182b600beedSjsing datalen = ASN1_STRING_length(altname->d.iPAddress); 1833130d2a2Sop data = ASN1_STRING_get0_data(altname->d.iPAddress); 184b600beedSjsing 185c81cfcc3Sdoug if (datalen < 0) { 1867a756d37Sjoshua tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, 187c81cfcc3Sdoug "Unexpected negative length for an " 188c81cfcc3Sdoug "IP address: %d", datalen); 1896adcb85dStb goto err; 190c81cfcc3Sdoug } 191c81cfcc3Sdoug 192da51b25bSdoug /* 193da51b25bSdoug * Per RFC 5280 section 4.2.1.6: 194da51b25bSdoug * IPv4 must use 4 octets and IPv6 must use 16 octets. 195da51b25bSdoug */ 196b600beedSjsing if (datalen == addrlen && 197b600beedSjsing memcmp(data, &addrbuf, addrlen) == 0) { 1985f3c5205Sjsing *alt_match = 1; 1996adcb85dStb goto done; 200b600beedSjsing } 201b600beedSjsing } 202b600beedSjsing } 203b600beedSjsing 2046adcb85dStb done: 2056adcb85dStb rv = 0; 2066adcb85dStb 2076adcb85dStb err: 208cc8f79ddSjsing sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); 209b600beedSjsing return rv; 210b600beedSjsing } 211b600beedSjsing 21240a2182fSjsing static int 213*26433cb1Stb tls_get_common_name_internal(X509 *cert, char **out_common_name, 214*26433cb1Stb unsigned int *out_tlserr, const char **out_errstr) 215b600beedSjsing { 216f23ec8efSbeck unsigned char *utf8_bytes = NULL; 2170ca7b9dfSjsing X509_NAME *subject_name; 218b600beedSjsing char *common_name = NULL; 219b600beedSjsing int common_name_len; 220f23ec8efSbeck ASN1_STRING *data; 221f23ec8efSbeck int lastpos = -1; 222d78b98e7Stb int rv = -1; 2235f3c5205Sjsing 224*26433cb1Stb *out_tlserr = TLS_ERROR_UNKNOWN; 225*26433cb1Stb *out_errstr = "unknown"; 226*26433cb1Stb 227*26433cb1Stb free(*out_common_name); 228*26433cb1Stb *out_common_name = NULL; 229b600beedSjsing 2300ca7b9dfSjsing subject_name = X509_get_subject_name(cert); 2310ca7b9dfSjsing if (subject_name == NULL) 232ecb88bd1Stb goto err; 233b600beedSjsing 234f23ec8efSbeck lastpos = X509_NAME_get_index_by_NID(subject_name, 235f23ec8efSbeck NID_commonName, lastpos); 236f23ec8efSbeck if (lastpos == -1) 2377add217bSjsing goto done; 2389f395b46Sbeck if (lastpos < 0) 2399f395b46Sbeck goto err; 240f23ec8efSbeck if (X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos) 241f23ec8efSbeck != -1) { 242f23ec8efSbeck /* 243f23ec8efSbeck * Having multiple CN's is possible, and even happened back in 244f23ec8efSbeck * the glory days of mullets and Hammer pants. In anything like 245f23ec8efSbeck * a modern TLS cert, CN is as close to deprecated as it gets, 246f23ec8efSbeck * and having more than one is bad. We therefore fail if we have 247f23ec8efSbeck * more than one CN fed to us in the subject, treating the 248f23ec8efSbeck * certificate as hostile. 249f23ec8efSbeck */ 250*26433cb1Stb *out_tlserr = TLS_ERROR_UNKNOWN; 251*26433cb1Stb *out_errstr = "error getting common name: " 252f3e820cfSop "Certificate subject contains multiple Common Name fields, " 253*26433cb1Stb "probably a malicious or malformed certificate"; 254d78b98e7Stb goto err; 255d78b98e7Stb } 256b600beedSjsing 257f23ec8efSbeck data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, 258f23ec8efSbeck lastpos)); 259f23ec8efSbeck /* 2609f395b46Sbeck * Fail if we cannot encode the CN bytes as UTF-8. 261f23ec8efSbeck */ 262f23ec8efSbeck if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) { 263*26433cb1Stb *out_tlserr = TLS_ERROR_UNKNOWN; 264*26433cb1Stb *out_errstr = "error getting common name: " 265f23ec8efSbeck "Common Name field cannot be encoded as a UTF-8 string, " 266*26433cb1Stb "probably a malicious certificate"; 267f23ec8efSbeck goto err; 268f23ec8efSbeck } 2699f395b46Sbeck /* 2709f395b46Sbeck * Fail if the CN is of invalid length. RFC 5280 specifies that a CN 2719f395b46Sbeck * must be between 1 and 64 bytes long. 2729f395b46Sbeck */ 273f23ec8efSbeck if (common_name_len < 1 || common_name_len > 64) { 274*26433cb1Stb *out_tlserr = TLS_ERROR_UNKNOWN; 275*26433cb1Stb *out_errstr = "error getting common name: " 276f23ec8efSbeck "Common Name field has invalid length, " 277*26433cb1Stb "probably a malicious certificate"; 278f23ec8efSbeck goto err; 279f23ec8efSbeck } 2809f395b46Sbeck /* 2819f395b46Sbeck * Fail if the resulting text contains a NUL byte. 2829f395b46Sbeck */ 283f23ec8efSbeck if (memchr(utf8_bytes, 0, common_name_len) != NULL) { 284*26433cb1Stb *out_tlserr = TLS_ERROR_UNKNOWN; 285*26433cb1Stb *out_errstr = "error getting common name: " 2869a94eeb1Sbcook "NUL byte in Common Name field, " 287*26433cb1Stb "probably a malicious certificate"; 288d78b98e7Stb goto err; 289b600beedSjsing } 290b600beedSjsing 291f23ec8efSbeck common_name = strndup(utf8_bytes, common_name_len); 292f23ec8efSbeck if (common_name == NULL) { 293*26433cb1Stb *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; 294*26433cb1Stb *out_errstr = "out of memory"; 295f23ec8efSbeck goto err; 296f23ec8efSbeck } 297f23ec8efSbeck 298*26433cb1Stb *out_common_name = common_name; 299*26433cb1Stb common_name = NULL; 300*26433cb1Stb 301*26433cb1Stb done: 302*26433cb1Stb if (*out_common_name == NULL) 303*26433cb1Stb *out_common_name = strdup(""); 304*26433cb1Stb if (*out_common_name == NULL) { 305*26433cb1Stb *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; 306*26433cb1Stb *out_errstr = "out of memory"; 307*26433cb1Stb goto err; 308*26433cb1Stb } 309*26433cb1Stb 310*26433cb1Stb rv = 0; 311*26433cb1Stb 312*26433cb1Stb err: 313*26433cb1Stb free(utf8_bytes); 314*26433cb1Stb free(common_name); 315*26433cb1Stb return rv; 316*26433cb1Stb } 317*26433cb1Stb 318*26433cb1Stb int 319*26433cb1Stb tls_get_common_name(struct tls *ctx, X509 *cert, const char *in_name, 320*26433cb1Stb char **out_common_name) 321*26433cb1Stb { 322*26433cb1Stb unsigned int errcode = TLS_ERROR_UNKNOWN; 323*26433cb1Stb const char *errstr = "unknown"; 324*26433cb1Stb 325*26433cb1Stb if (tls_get_common_name_internal(cert, out_common_name, &errcode, 326*26433cb1Stb &errstr) == -1) { 327*26433cb1Stb const char *name = in_name; 328*26433cb1Stb const char *space = " "; 329*26433cb1Stb 330*26433cb1Stb if (name == NULL) 331*26433cb1Stb name = space = ""; 332*26433cb1Stb 333*26433cb1Stb tls_set_errorx(ctx, errcode, "%s%s%s", name, space, errstr); 334*26433cb1Stb return -1; 335*26433cb1Stb } 336*26433cb1Stb 337*26433cb1Stb return 0; 338*26433cb1Stb } 339*26433cb1Stb 340*26433cb1Stb static int 341*26433cb1Stb tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, 342*26433cb1Stb int *cn_match) 343*26433cb1Stb { 344*26433cb1Stb char *common_name = NULL; 345*26433cb1Stb union tls_addr addrbuf; 346*26433cb1Stb int rv = -1; 347*26433cb1Stb 348*26433cb1Stb if (tls_get_common_name(ctx, cert, name, &common_name) == -1) 349*26433cb1Stb goto err; 350*26433cb1Stb if (strlen(common_name) == 0) 351*26433cb1Stb goto done; 352*26433cb1Stb 3535f3c5205Sjsing /* 3545f3c5205Sjsing * We don't want to attempt wildcard matching against IP addresses, 3555f3c5205Sjsing * so perform a simple comparison here. 3565f3c5205Sjsing */ 3575f3c5205Sjsing if (inet_pton(AF_INET, name, &addrbuf) == 1 || 3585f3c5205Sjsing inet_pton(AF_INET6, name, &addrbuf) == 1) { 3595f3c5205Sjsing if (strcmp(common_name, name) == 0) 3605f3c5205Sjsing *cn_match = 1; 3617add217bSjsing goto done; 3625f3c5205Sjsing } 3635f3c5205Sjsing 3640ca7b9dfSjsing if (tls_match_name(common_name, name) == 0) 3655f3c5205Sjsing *cn_match = 1; 3665f3c5205Sjsing 3677add217bSjsing done: 368d78b98e7Stb rv = 0; 369d78b98e7Stb 370d78b98e7Stb err: 371b600beedSjsing free(common_name); 372b600beedSjsing return rv; 373b600beedSjsing } 374b600beedSjsing 375b600beedSjsing int 3765f3c5205Sjsing tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match) 377b600beedSjsing { 3785f3c5205Sjsing int alt_exists; 379b600beedSjsing 3805f3c5205Sjsing *match = 0; 381b600beedSjsing 3825f3c5205Sjsing if (tls_check_subject_altname(ctx, cert, name, match, 3835f3c5205Sjsing &alt_exists) == -1) 3845f3c5205Sjsing return -1; 3855f3c5205Sjsing 3865f3c5205Sjsing /* 3875f3c5205Sjsing * As per RFC 6125 section 6.4.4, if any known alternate name existed 3885f3c5205Sjsing * in the certificate, we do not attempt to match on the CN. 3895f3c5205Sjsing */ 3905f3c5205Sjsing if (*match || alt_exists) 3915f3c5205Sjsing return 0; 3925f3c5205Sjsing 3935f3c5205Sjsing return tls_check_common_name(ctx, cert, name, match); 394b600beedSjsing } 395