git ssb

0+

cel / sslh



Tree: 6cc33820d166bc790c79f8e42aec8f53bcefd2ba

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#include <stdio.h>
32#include <stdlib.h> /* malloc() */
33#include <fnmatch.h> /* fnmatch() */
34#include "tls.h"
35
36#define TLS_HEADER_LEN 5
37#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
38#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
39
40#ifndef MIN
41#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
42#endif
43
44
45struct TLSProtocol {
46 int use_alpn;
47 char** sni_hostname_list;
48 char** alpn_protocol_list;
49};
50
51static int parse_extensions(const struct TLSProtocol *, const char *, size_t);
52static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t);
53static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
54static 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 */
67int
68parse_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
158static int
159parse_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
231static int
232parse_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
263static int
264parse_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
288static int
289has_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
301struct TLSProtocol *
302new_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
311struct TLSProtocol *
312tls_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