1 /* Matching a host name to wildcards in SSL certificates */
9 #include "intl/charsets.h"
10 #include "network/ssl/match-hostname.h"
11 #include "util/conv.h"
12 #include "util/error.h"
13 #include "util/string.h"
15 /** Checks whether a host name matches a pattern that may contain
19 * The host name to which the user wanted to connect.
20 * Should be in UTF-8 and need not be null-terminated.
21 * @param[in] hostname_length
22 * The length of @a hostname, in bytes.
24 * A pattern that the host name might match.
25 * Should be in UTF-8 and need not be null-terminated.
26 * The pattern may contain wildcards, as specified in
27 * RFC 2818 section 3.1.
28 * @param[in] pattern_length
29 * The length of @a pattern, in bytes.
32 * Nonzero if the host name matches. Zero if it doesn't.
34 * According to RFC 2818 section 3.1, '*' matches any number of
35 * characters except '.'. For example, "*r*.example.org" matches
36 * "random.example.org" or "history.example.org" but not
37 * "frozen.fruit.example.org".
39 * This function does not allocate memory, and consumes at most
40 * O(@a hostname_length * @a pattern_length) time. */
42 match_hostname_pattern(const unsigned char *hostname
,
43 size_t hostname_length
,
44 const unsigned char *pattern
,
45 size_t pattern_length
)
47 const unsigned char *const hostname_end
= hostname
+ hostname_length
;
48 const unsigned char *const pattern_end
= pattern
+ pattern_length
;
50 assert(hostname
<= hostname_end
);
51 assert(pattern
<= pattern_end
);
52 if_assert_failed
return 0;
54 while (pattern
< pattern_end
) {
55 if (*pattern
== '*') {
56 const unsigned char *next_wildcard
;
57 size_t literal_length
;
60 next_wildcard
= memchr(pattern
, '*',
61 pattern_end
- pattern
);
62 if (next_wildcard
== NULL
)
63 literal_length
= pattern_end
- pattern
;
65 literal_length
= next_wildcard
- pattern
;
68 size_t hostname_left
= hostname_end
- hostname
;
71 if (hostname_left
< literal_length
)
74 /* If next_wildcard == NULL, then the
75 * literal string is at the end of the
76 * pattern, so anchor the match to the
77 * end of the hostname. The end of
78 * this function can then easily
79 * verify that the whole hostname was
82 * But do not jump directly there;
83 * first verify that there are no '.'
84 * characters in between. */
85 if ((next_wildcard
!= NULL
86 || hostname_left
== literal_length
)
87 && !c_strlcasecmp(pattern
, literal_length
,
88 hostname
, literal_length
))
91 /* The literal string doesn't match here.
92 * Skip one character of the hostname and
93 * retry. If the skipped character is '.'
94 * or one of the equivalent characters
95 * listed in RFC 3490 section 3.1
96 * requirement 1, then return 0, because
97 * '*' must not match such characters.
98 * Do the same if invalid UTF-8 is found.
100 uni
= utf8_to_unicode((unsigned char **) &hostname
,
106 || uni
== UCS_NO_CHAR
)
110 pattern
+= literal_length
;
111 hostname
+= literal_length
;
113 if (hostname
== hostname_end
)
116 if (c_toupper(*pattern
) != c_toupper(*hostname
))
124 return hostname
== hostname_end
;