git ssb

0+

cel / sslh



Tree: b4cb91043812c7ff6fb13d369a65288adb6fbc8a

Files: b4cb91043812c7ff6fb13d369a65288adb6fbc8a / tls.c

10330 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 "tls.h"
34
35#define TLS_HEADER_LEN 5
36#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
37#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
38
39#ifndef MIN
40#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
41#endif
42
43
44struct TLSProtocol {
45 int use_alpn;
46 char** sni_hostname_list;
47 char** alpn_protocol_list;
48};
49
50static int parse_extensions(const struct TLSProtocol *, const char *, size_t);
51static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t);
52static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
53static int has_match(char**, const char*, size_t);
54
55/* Parse a TLS packet for the Server Name Indication and ALPN extension in the client
56 * hello handshake, returning a status code
57 *
58 * Returns:
59 * >=0 - length of the hostname and updates *hostname
60 * caller is responsible for freeing *hostname
61 * -1 - Incomplete request
62 * -2 - No Host header included in this request
63 * -3 - Invalid hostname pointer
64 * < -4 - Invalid TLS client hello
65 */
66int
67parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
68 char tls_content_type;
69 char tls_version_major;
70 char tls_version_minor;
71 size_t pos = TLS_HEADER_LEN;
72 size_t len;
73
74 /* Check that our TCP payload is at least large enough for a TLS header */
75 if (data_len < TLS_HEADER_LEN)
76 return -1;
77
78 tls_content_type = data[0];
79 if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
80 if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n");
81 return -5;
82 }
83
84 tls_version_major = data[1];
85 tls_version_minor = data[2];
86 if (tls_version_major < 3) {
87 if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n",
88 tls_version_major, tls_version_minor);
89
90 return -2;
91 }
92
93 /* TLS record length */
94 len = ((unsigned char)data[3] << 8) +
95 (unsigned char)data[4] + TLS_HEADER_LEN;
96 data_len = MIN(data_len, len);
97
98 /* Check we received entire TLS record length */
99 if (data_len < len)
100 return -1;
101
102 /*
103 * Handshake
104 */
105 if (pos + 1 > data_len) {
106 return -5;
107 }
108 if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
109 if (verbose) fprintf(stderr, "Not a client hello\n");
110
111 return -5;
112 }
113
114 /* Skip past fixed length records:
115 1 Handshake Type
116 3 Length
117 2 Version (again)
118 32 Random
119 to Session ID Length
120 */
121 pos += 38;
122
123 /* Session ID */
124 if (pos + 1 > data_len)
125 return -5;
126 len = (unsigned char)data[pos];
127 pos += 1 + len;
128
129 /* Cipher Suites */
130 if (pos + 2 > data_len)
131 return -5;
132 len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
133 pos += 2 + len;
134
135 /* Compression Methods */
136 if (pos + 1 > data_len)
137 return -5;
138 len = (unsigned char)data[pos];
139 pos += 1 + len;
140
141 if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) {
142 if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n");
143 return -2;
144 }
145
146 /* Extensions */
147 if (pos + 2 > data_len)
148 return -5;
149 len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1];
150 pos += 2;
151
152 if (pos + len > data_len)
153 return -5;
154 return parse_extensions(tls_data, data + pos, len);
155}
156
157static int
158parse_extensions(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
159 size_t pos = 0;
160 size_t len;
161 int last_matched = 0;
162
163 if (tls_data == NULL)
164 return -3;
165
166 /* Parse each 4 bytes for the extension header */
167 while (pos + 4 <= data_len) {
168 /* Extension Length */
169 len = ((unsigned char) data[pos + 2] << 8) +
170 (unsigned char) data[pos + 3];
171
172 if (pos + 4 + len > data_len)
173 return -5;
174
175 size_t extension_type = ((unsigned char) data[pos] << 8) +
176 (unsigned char) data[pos + 1];
177
178
179 /* Check if it's a server name extension */
180 /* There can be only one extension of each type, so we break
181 our state and move pos to beginning of the extension here */
182 if (tls_data->use_alpn == 2) {
183 /* we want BOTH alpn and sni to match */
184 if (extension_type == 0x00) { /* Server Name */
185 if (parse_server_name_extension(tls_data, data + pos + 4, len)) {
186 /* SNI matched */
187 if(last_matched) {
188 /* this is only true if ALPN matched, so return true */
189 return last_matched;
190 } else {
191 /* otherwise store that SNI matched */
192 last_matched = 1;
193 }
194 } else {
195 // both can't match
196 return -2;
197 }
198 } else if (extension_type == 0x10) { /* ALPN */
199 if (parse_alpn_extension(tls_data, data + pos + 4, len)) {
200 /* ALPN matched */
201 if(last_matched) {
202 /* this is only true if SNI matched, so return true */
203 return last_matched;
204 } else {
205 /* otherwise store that ALPN matched */
206 last_matched = 1;
207 }
208 } else {
209 // both can't match
210 return -2;
211 }
212 }
213
214 } else if (extension_type == 0x00 && tls_data->use_alpn == 0) { /* Server Name */
215 return parse_server_name_extension(tls_data, data + pos + 4, len);
216 } else if (extension_type == 0x10 && tls_data->use_alpn == 1) { /* ALPN */
217 return parse_alpn_extension(tls_data, data + pos + 4, len);
218 }
219
220 pos += 4 + len; /* Advance to the next extension header */
221 }
222
223 /* Check we ended where we expected to */
224 if (pos != data_len)
225 return -5;
226
227 return -2;
228}
229
230static int
231parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
232 size_t pos = 2; /* skip server name list length */
233 size_t len;
234
235 while (pos + 3 < data_len) {
236 len = ((unsigned char)data[pos + 1] << 8) +
237 (unsigned char)data[pos + 2];
238
239 if (pos + 3 + len > data_len)
240 return -5;
241
242 switch (data[pos]) { /* name type */
243 case 0x00: /* host_name */
244 if(has_match(tls_data->sni_hostname_list, data + pos + 3, len)) {
245 return len;
246 } else {
247 return -2;
248 }
249 default:
250 if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
251 data[pos]);
252 }
253 pos += 3 + len;
254 }
255 /* Check we ended where we expected to */
256 if (pos != data_len)
257 return -5;
258
259 return -2;
260}
261
262static int
263parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
264 size_t pos = 2;
265 size_t len;
266
267 while (pos + 1 < data_len) {
268 len = (unsigned char)data[pos];
269
270 if (pos + 1 + len > data_len)
271 return -5;
272
273 if (len > 0 && has_match(tls_data->alpn_protocol_list, data + pos + 1, len)) {
274 return len;
275 } else if (len > 0) {
276 if (verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
277 }
278 pos += 1 + len;
279 }
280 /* Check we ended where we expected to */
281 if (pos != data_len)
282 return -5;
283
284 return -2;
285}
286
287static int
288has_match(char** list, const char* name, size_t name_len) {
289 char **item;
290
291 for (item = list; *item; item++) {
292 if (verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
293 if(!strncmp(*item, name, name_len)) {
294 return 1;
295 }
296 }
297 return 0;
298}
299
300struct TLSProtocol *
301new_tls_data() {
302 struct TLSProtocol *tls_data = malloc(sizeof(struct TLSProtocol));
303 if (tls_data != NULL) {
304 tls_data->use_alpn = -1;
305 }
306
307 return tls_data;
308}
309
310struct TLSProtocol *
311tls_data_set_list(struct TLSProtocol *tls_data, int alpn, char** list) {
312 if (alpn) {
313 tls_data->alpn_protocol_list = list;
314 if(tls_data->use_alpn == 0)
315 tls_data->use_alpn = 2;
316 else
317 tls_data->use_alpn = 1;
318 } else {
319 tls_data->sni_hostname_list = list;
320 if(tls_data->use_alpn == 1)
321 tls_data->use_alpn = 2;
322 else
323 tls_data->use_alpn = 0;
324 }
325
326 return tls_data;
327}
328

Built with git-ssb-web