Files: 6cc33820d166bc790c79f8e42aec8f53bcefd2ba / tls.c
10360 bytesRaw
1 | /* |
2 | * Copyright (c) 2011 and 2012, Dustin Lundquist <dustin@null-ptr.net> |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are met: |
7 | * |
8 | * 1. Redistributions of source code must retain the above copyright notice, |
9 | * this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
24 | * POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | /* |
27 | * This is a minimal TLS implementation intended only to parse the server name |
28 | * extension. This was created based primarily on Wireshark dissection of a |
29 | * TLS handshake and RFC4366. |
30 | */ |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | struct TLSProtocol { |
46 | int use_alpn; |
47 | char** sni_hostname_list; |
48 | char** alpn_protocol_list; |
49 | }; |
50 | |
51 | static int parse_extensions(const struct TLSProtocol *, const char *, size_t); |
52 | static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t); |
53 | static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t); |
54 | static int has_match(char**, const char*, size_t); |
55 | |
56 | /* Parse a TLS packet for the Server Name Indication and ALPN extension in the client |
57 | * hello handshake, returning a status code |
58 | * |
59 | * Returns: |
60 | * >=0 - length of the hostname and updates *hostname |
61 | * caller is responsible for freeing *hostname |
62 | * -1 - Incomplete request |
63 | * -2 - No Host header included in this request |
64 | * -3 - Invalid hostname pointer |
65 | * < -4 - Invalid TLS client hello |
66 | */ |
67 | int |
68 | parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { |
69 | char tls_content_type; |
70 | char tls_version_major; |
71 | char tls_version_minor; |
72 | size_t pos = TLS_HEADER_LEN; |
73 | size_t len; |
74 | |
75 | /* Check that our TCP payload is at least large enough for a TLS header */ |
76 | if (data_len < TLS_HEADER_LEN) |
77 | return -1; |
78 | |
79 | tls_content_type = data[0]; |
80 | if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { |
81 | if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); |
82 | return -5; |
83 | } |
84 | |
85 | tls_version_major = data[1]; |
86 | tls_version_minor = data[2]; |
87 | if (tls_version_major < 3) { |
88 | if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n", |
89 | tls_version_major, tls_version_minor); |
90 | |
91 | return -2; |
92 | } |
93 | |
94 | /* TLS record length */ |
95 | len = ((unsigned char)data[3] << 8) + |
96 | (unsigned char)data[4] + TLS_HEADER_LEN; |
97 | data_len = MIN(data_len, len); |
98 | |
99 | /* Check we received entire TLS record length */ |
100 | if (data_len < len) |
101 | return -1; |
102 | |
103 | /* |
104 | * Handshake |
105 | */ |
106 | if (pos + 1 > data_len) { |
107 | return -5; |
108 | } |
109 | if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { |
110 | if (verbose) fprintf(stderr, "Not a client hello\n"); |
111 | |
112 | return -5; |
113 | } |
114 | |
115 | /* Skip past fixed length records: |
116 | 1 Handshake Type |
117 | 3 Length |
118 | 2 Version (again) |
119 | 32 Random |
120 | to Session ID Length |
121 | */ |
122 | pos += 38; |
123 | |
124 | /* Session ID */ |
125 | if (pos + 1 > data_len) |
126 | return -5; |
127 | len = (unsigned char)data[pos]; |
128 | pos += 1 + len; |
129 | |
130 | /* Cipher Suites */ |
131 | if (pos + 2 > data_len) |
132 | return -5; |
133 | len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; |
134 | pos += 2 + len; |
135 | |
136 | /* Compression Methods */ |
137 | if (pos + 1 > data_len) |
138 | return -5; |
139 | len = (unsigned char)data[pos]; |
140 | pos += 1 + len; |
141 | |
142 | if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { |
143 | if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n"); |
144 | return -2; |
145 | } |
146 | |
147 | /* Extensions */ |
148 | if (pos + 2 > data_len) |
149 | return -5; |
150 | len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; |
151 | pos += 2; |
152 | |
153 | if (pos + len > data_len) |
154 | return -5; |
155 | return parse_extensions(tls_data, data + pos, len); |
156 | } |
157 | |
158 | static int |
159 | parse_extensions(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { |
160 | size_t pos = 0; |
161 | size_t len; |
162 | int last_matched = 0; |
163 | |
164 | if (tls_data == NULL) |
165 | return -3; |
166 | |
167 | /* Parse each 4 bytes for the extension header */ |
168 | while (pos + 4 <= data_len) { |
169 | /* Extension Length */ |
170 | len = ((unsigned char) data[pos + 2] << 8) + |
171 | (unsigned char) data[pos + 3]; |
172 | |
173 | if (pos + 4 + len > data_len) |
174 | return -5; |
175 | |
176 | size_t extension_type = ((unsigned char) data[pos] << 8) + |
177 | (unsigned char) data[pos + 1]; |
178 | |
179 | |
180 | /* Check if it's a server name extension */ |
181 | /* There can be only one extension of each type, so we break |
182 | our state and move pos to beginning of the extension here */ |
183 | if (tls_data->use_alpn == 2) { |
184 | /* we want BOTH alpn and sni to match */ |
185 | if (extension_type == 0x00) { /* Server Name */ |
186 | if (parse_server_name_extension(tls_data, data + pos + 4, len)) { |
187 | /* SNI matched */ |
188 | if(last_matched) { |
189 | /* this is only true if ALPN matched, so return true */ |
190 | return last_matched; |
191 | } else { |
192 | /* otherwise store that SNI matched */ |
193 | last_matched = 1; |
194 | } |
195 | } else { |
196 | // both can't match |
197 | return -2; |
198 | } |
199 | } else if (extension_type == 0x10) { /* ALPN */ |
200 | if (parse_alpn_extension(tls_data, data + pos + 4, len)) { |
201 | /* ALPN matched */ |
202 | if(last_matched) { |
203 | /* this is only true if SNI matched, so return true */ |
204 | return last_matched; |
205 | } else { |
206 | /* otherwise store that ALPN matched */ |
207 | last_matched = 1; |
208 | } |
209 | } else { |
210 | // both can't match |
211 | return -2; |
212 | } |
213 | } |
214 | |
215 | } else if (extension_type == 0x00 && tls_data->use_alpn == 0) { /* Server Name */ |
216 | return parse_server_name_extension(tls_data, data + pos + 4, len); |
217 | } else if (extension_type == 0x10 && tls_data->use_alpn == 1) { /* ALPN */ |
218 | return parse_alpn_extension(tls_data, data + pos + 4, len); |
219 | } |
220 | |
221 | pos += 4 + len; /* Advance to the next extension header */ |
222 | } |
223 | |
224 | /* Check we ended where we expected to */ |
225 | if (pos != data_len) |
226 | return -5; |
227 | |
228 | return -2; |
229 | } |
230 | |
231 | static int |
232 | parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { |
233 | size_t pos = 2; /* skip server name list length */ |
234 | size_t len; |
235 | |
236 | while (pos + 3 < data_len) { |
237 | len = ((unsigned char)data[pos + 1] << 8) + |
238 | (unsigned char)data[pos + 2]; |
239 | |
240 | if (pos + 3 + len > data_len) |
241 | return -5; |
242 | |
243 | switch (data[pos]) { /* name type */ |
244 | case 0x00: /* host_name */ |
245 | if(has_match(tls_data->sni_hostname_list, data + pos + 3, len)) { |
246 | return len; |
247 | } else { |
248 | return -2; |
249 | } |
250 | default: |
251 | if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", |
252 | data[pos]); |
253 | } |
254 | pos += 3 + len; |
255 | } |
256 | /* Check we ended where we expected to */ |
257 | if (pos != data_len) |
258 | return -5; |
259 | |
260 | return -2; |
261 | } |
262 | |
263 | static int |
264 | parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) { |
265 | size_t pos = 2; |
266 | size_t len; |
267 | |
268 | while (pos + 1 < data_len) { |
269 | len = (unsigned char)data[pos]; |
270 | |
271 | if (pos + 1 + len > data_len) |
272 | return -5; |
273 | |
274 | if (len > 0 && has_match(tls_data->alpn_protocol_list, data + pos + 1, len)) { |
275 | return len; |
276 | } else if (len > 0) { |
277 | if (verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); |
278 | } |
279 | pos += 1 + len; |
280 | } |
281 | /* Check we ended where we expected to */ |
282 | if (pos != data_len) |
283 | return -5; |
284 | |
285 | return -2; |
286 | } |
287 | |
288 | static int |
289 | has_match(char** list, const char* name, size_t name_len) { |
290 | char **item; |
291 | |
292 | for (item = list; *item; item++) { |
293 | if (verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); |
294 | if(!fnmatch(*item, name, 0)) { |
295 | return 1; |
296 | } |
297 | } |
298 | return 0; |
299 | } |
300 | |
301 | struct TLSProtocol * |
302 | new_tls_data() { |
303 | struct TLSProtocol *tls_data = malloc(sizeof(struct TLSProtocol)); |
304 | if (tls_data != NULL) { |
305 | tls_data->use_alpn = -1; |
306 | } |
307 | |
308 | return tls_data; |
309 | } |
310 | |
311 | struct TLSProtocol * |
312 | tls_data_set_list(struct TLSProtocol *tls_data, int alpn, char** list) { |
313 | if (alpn) { |
314 | tls_data->alpn_protocol_list = list; |
315 | if(tls_data->use_alpn == 0) |
316 | tls_data->use_alpn = 2; |
317 | else |
318 | tls_data->use_alpn = 1; |
319 | } else { |
320 | tls_data->sni_hostname_list = list; |
321 | if(tls_data->use_alpn == 1) |
322 | tls_data->use_alpn = 2; |
323 | else |
324 | tls_data->use_alpn = 0; |
325 | } |
326 | |
327 | return tls_data; |
328 | } |
329 |
Built with git-ssb-web