Fix hostname verification code.
[elinks/elinks-j605.git] / src / network / ssl / match-hostname.c
blob80d93b0263f61f9bda990e2166bfce1f4a56c913
1 /* Matching a host name to wildcards in SSL certificates */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
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
16 * wildcards.
18 * @param[in] hostname
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.
23 * @param[in] pattern
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.
31 * @return
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. */
41 int
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;
59 ++pattern;
60 next_wildcard = memchr(pattern, '*',
61 pattern_end - pattern);
62 if (next_wildcard == NULL)
63 literal_length = pattern_end - pattern;
64 else
65 literal_length = next_wildcard - pattern;
67 for (;;) {
68 size_t hostname_left = hostname_end - hostname;
69 unicode_val_T uni;
71 if (hostname_left < literal_length)
72 return 0;
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
80 * matched.
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))
89 break;
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.
99 * Cast away const. */
100 uni = utf8_to_unicode((unsigned char **) &hostname,
101 hostname_end);
102 if (uni == 0x002E
103 || uni == 0x3002
104 || uni == 0xFF0E
105 || uni == 0xFF61
106 || uni == UCS_NO_CHAR)
107 return 0;
110 pattern += literal_length;
111 hostname += literal_length;
112 } else {
113 if (hostname == hostname_end)
114 return 0;
116 if (c_toupper(*pattern) != c_toupper(*hostname))
117 return 0;
119 ++pattern;
120 ++hostname;
124 return hostname == hostname_end;