hostcheck.c (4321B)
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 #include "hostcheck.h" 39 #include "../hostip.h" 40 41 #include "../curl_memory.h" 42 /* The last #include file should be: */ 43 #include "../memdebug.h" 44 45 /* check the two input strings with given length, but do not 46 assume they end in nul-bytes */ 47 static bool pmatch(const char *hostname, size_t hostlen, 48 const char *pattern, size_t patternlen) 49 { 50 if(hostlen != patternlen) 51 return FALSE; 52 return curl_strnequal(hostname, pattern, hostlen); 53 } 54 55 /* 56 * Match a hostname against a wildcard pattern. 57 * E.g. 58 * "foo.host.com" matches "*.host.com". 59 * 60 * We use the matching rule described in RFC6125, section 6.4.3. 61 * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 62 * 63 * In addition: ignore trailing dots in the hostnames and wildcards, so that 64 * the names are used normalized. This is what the browsers do. 65 * 66 * Do not allow wildcard matching on IP numbers. There are apparently 67 * certificates being used with an IP address in the CN field, thus making no 68 * apparent distinction between a name and an IP. We need to detect the use of 69 * an IP address and not wildcard match on such names. 70 * 71 * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor 72 * "*b". 73 * 74 * Return TRUE on a match. FALSE if not. 75 * 76 * @unittest: 1397 77 */ 78 79 static bool hostmatch(const char *hostname, 80 size_t hostlen, 81 const char *pattern, 82 size_t patternlen) 83 { 84 const char *pattern_label_end; 85 86 DEBUGASSERT(pattern); 87 DEBUGASSERT(patternlen); 88 DEBUGASSERT(hostname); 89 DEBUGASSERT(hostlen); 90 91 /* normalize pattern and hostname by stripping off trailing dots */ 92 if(hostname[hostlen-1]=='.') 93 hostlen--; 94 if(pattern[patternlen-1]=='.') 95 patternlen--; 96 97 if(strncmp(pattern, "*.", 2)) 98 return pmatch(hostname, hostlen, pattern, patternlen); 99 100 /* detect IP address as hostname and fail the match if so */ 101 else if(Curl_host_is_ipnum(hostname)) 102 return FALSE; 103 104 /* We require at least 2 dots in the pattern to avoid too wide wildcard 105 match. */ 106 pattern_label_end = memchr(pattern, '.', patternlen); 107 if(!pattern_label_end || 108 (memrchr(pattern, '.', patternlen) == pattern_label_end)) 109 return pmatch(hostname, hostlen, pattern, patternlen); 110 else { 111 const char *hostname_label_end = memchr(hostname, '.', hostlen); 112 if(hostname_label_end) { 113 size_t skiphost = hostname_label_end - hostname; 114 size_t skiplen = pattern_label_end - pattern; 115 return pmatch(hostname_label_end, hostlen - skiphost, 116 pattern_label_end, patternlen - skiplen); 117 } 118 } 119 return FALSE; 120 } 121 122 /* 123 * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. 124 */ 125 bool Curl_cert_hostcheck(const char *match, size_t matchlen, 126 const char *hostname, size_t hostlen) 127 { 128 if(match && *match && hostname && *hostname) 129 return hostmatch(hostname, hostlen, match, matchlen); 130 return FALSE; 131 } 132 133 #endif /* OPENSSL or SCHANNEL */