1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#if defined(USE_OPENSSL) \ 28 || defined(USE_SCHANNEL) 29/* these backends use functions from this file */ 30 31#ifdef HAVE_NETINET_IN_H 32#include <netinet/in.h> 33#endif 34#ifdef HAVE_NETINET_IN6_H 35#include <netinet/in6.h> 36#endif 37#include "curl_memrchr.h" 38 39#include "hostcheck.h" 40#include "strcase.h" 41#include "hostip.h" 42 43#include "curl_memory.h" 44/* The last #include file should be: */ 45#include "memdebug.h" 46 47/* check the two input strings with given length, but do not 48 assume they end in nul-bytes */ 49static bool pmatch(const char *hostname, size_t hostlen, 50 const char *pattern, size_t patternlen) 51{ 52 if(hostlen != patternlen) 53 return FALSE; 54 return strncasecompare(hostname, pattern, hostlen); 55} 56 57/* 58 * Match a hostname against a wildcard pattern. 59 * E.g. 60 * "foo.host.com" matches "*.host.com". 61 * 62 * We use the matching rule described in RFC6125, section 6.4.3. 63 * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 64 * 65 * In addition: ignore trailing dots in the host names and wildcards, so that 66 * the names are used normalized. This is what the browsers do. 67 * 68 * Do not allow wildcard matching on IP numbers. There are apparently 69 * certificates being used with an IP address in the CN field, thus making no 70 * apparent distinction between a name and an IP. We need to detect the use of 71 * an IP address and not wildcard match on such names. 72 * 73 * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor 74 * "*b". 75 * 76 * Return TRUE on a match. FALSE if not. 77 * 78 * @unittest: 1397 79 */ 80 81static bool hostmatch(const char *hostname, 82 size_t hostlen, 83 const char *pattern, 84 size_t patternlen) 85{ 86 const char *pattern_label_end; 87 88 DEBUGASSERT(pattern); 89 DEBUGASSERT(patternlen); 90 DEBUGASSERT(hostname); 91 DEBUGASSERT(hostlen); 92 93 /* normalize pattern and hostname by stripping off trailing dots */ 94 if(hostname[hostlen-1]=='.') 95 hostlen--; 96 if(pattern[patternlen-1]=='.') 97 patternlen--; 98 99 if(strncmp(pattern, "*.", 2)) 100 return pmatch(hostname, hostlen, pattern, patternlen); 101 102 /* detect IP address as hostname and fail the match if so */ 103 else if(Curl_host_is_ipnum(hostname)) 104 return FALSE; 105 106 /* We require at least 2 dots in the pattern to avoid too wide wildcard 107 match. */ 108 pattern_label_end = memchr(pattern, '.', patternlen); 109 if(!pattern_label_end || 110 (memrchr(pattern, '.', patternlen) == pattern_label_end)) 111 return pmatch(hostname, hostlen, pattern, patternlen); 112 else { 113 const char *hostname_label_end = memchr(hostname, '.', hostlen); 114 if(hostname_label_end) { 115 size_t skiphost = hostname_label_end - hostname; 116 size_t skiplen = pattern_label_end - pattern; 117 return pmatch(hostname_label_end, hostlen - skiphost, 118 pattern_label_end, patternlen - skiplen); 119 } 120 } 121 return FALSE; 122} 123 124/* 125 * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. 126 */ 127bool Curl_cert_hostcheck(const char *match, size_t matchlen, 128 const char *hostname, size_t hostlen) 129{ 130 if(match && *match && hostname && *hostname) 131 return hostmatch(hostname, hostlen, match, matchlen); 132 return FALSE; 133} 134 135#endif /* OPENSSL or SCHANNEL */ 136