Commit ca461ea077d748dde25eb6610e66514fa0151baf
Added support for RFC4366 SNI (Server Name Indication). Changed configuration file format accordingly.
Yves Rutschle committed on 7/17/2015, 1:05:06 PMParent: 5886bd2d43db3ee6a9ae58e3e47c34e07cf80f00
Parent: 8fdaf6eb08555e01c9993ffbf52f7e4ac40c3d15
Files changed
ChangeLog | changed |
Makefile | changed |
basic.cfg | changed |
example.cfg | changed |
probe.c | changed |
probe.h | changed |
sslh-main.c | changed |
sslh.pod | changed |
tls.c | added |
tls.h | added |
ChangeLog | ||
---|---|---|
@@ -1,8 +1,16 @@ | ||
1 | 1 | vNEXT: |
2 | 2 | Added USELIBPCRE to make use of regex engine |
3 | 3 | optional. |
4 | 4 | |
5 | + Added support for RFC4366 SNI | |
6 | + (Travis Burtrum) | |
7 | + | |
8 | + Changed configuration file format: 'probe' field is | |
9 | + no longer required, 'name' field can now contain | |
10 | + 'sni' or 'regex', with corresponding options (see | |
11 | + example.org) | |
12 | + | |
5 | 13 | v1.17: 09MAR2015 |
6 | 14 | Support RFC5952-style IPv6 addresses, e.g. [::]:443. |
7 | 15 | |
8 | 16 | Transparant proxy support for FreeBSD. |
Makefile | ||
---|---|---|
@@ -22,9 +22,9 @@ | ||
22 | 22 | CC ?= gcc |
23 | 23 | CFLAGS ?=-Wall -g $(CFLAGS_COV) |
24 | 24 | |
25 | 25 | LIBS= |
26 | -OBJS=common.o sslh-main.o probe.o | |
26 | +OBJS=common.o sslh-main.o probe.o tls.o | |
27 | 27 | |
28 | 28 | ifneq ($(strip $(USELIBWRAP)),) |
29 | 29 | LIBS:=$(LIBS) -lwrap |
30 | 30 | CPPFLAGS+=-DLIBWRAP |
@@ -62,9 +62,9 @@ | ||
62 | 62 | $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS) |
63 | 63 | #strip sslh-select |
64 | 64 | |
65 | 65 | echosrv: $(OBJS) echosrv.o |
66 | - $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o $(LIBS) | |
66 | + $(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o probe.o common.o tls.o $(LIBS) | |
67 | 67 | |
68 | 68 | $(MAN): sslh.pod Makefile |
69 | 69 | pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN) |
70 | 70 |
basic.cfg | ||
---|---|---|
@@ -18,12 +18,12 @@ | ||
18 | 18 | ); |
19 | 19 | |
20 | 20 | protocols: |
21 | 21 | ( |
22 | - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, | |
23 | - { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, | |
24 | - { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, | |
25 | - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, | |
26 | - { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; }, | |
27 | - { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } | |
22 | + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; }, | |
23 | + { name: "openvpn"; host: "localhost"; port: "1194"; }, | |
24 | + { name: "xmpp"; host: "localhost"; port: "5222"; }, | |
25 | + { name: "http"; host: "localhost"; port: "80"; }, | |
26 | + { name: "ssl"; host: "localhost"; port: "443"; }, | |
27 | + { name: "anyprot"; host: "localhost"; port: "443"; } | |
28 | 28 | ); |
29 | 29 |
example.cfg | ||
---|---|---|
@@ -22,32 +22,50 @@ | ||
22 | 22 | |
23 | 23 | # List of protocols |
24 | 24 | # |
25 | 25 | # Each protocol entry consists of: |
26 | -# name: name of the protocol | |
26 | +# name: name of the probe. These are listed on the command | |
27 | +# line (ssh -?), plus 'regex', 'sni' and 'timeout'. | |
28 | + | |
27 | 29 | # service: (optional) libwrap service name (see hosts_access(5)) |
28 | -# host: host name to connect that protocol | |
29 | -# port: port number to connect that protocol | |
30 | -# probe: "builtin" or a list of regular expressions | |
31 | -# (can be left out, e.g. to use with on-timeout) | |
30 | +# host, port: where to connect when this probe succeeds | |
31 | +# | |
32 | +# Probe-specific options: | |
33 | +# sni: | |
34 | +# sni_hotnames: list of FQDN for that target | |
35 | +# regex: | |
36 | +# regex_patterns: list of patterns to match for | |
37 | +# that target. | |
32 | 38 | # |
33 | 39 | # sslh will try each probe in order they are declared, and |
34 | 40 | # connect to the first that matches. |
35 | - | |
41 | +# | |
42 | +# You can specify several of 'regex' and 'sni'. | |
43 | + | |
36 | 44 | protocols: |
37 | 45 | ( |
38 | - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, | |
39 | - { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, | |
40 | - { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; }, | |
41 | - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, | |
42 | - { name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; }, | |
46 | + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; }, | |
47 | + { name: "http"; host: "localhost"; port: "80"; }, | |
48 | + | |
49 | + { name: "sni"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; }, | |
50 | + { name: "sni"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; }, | |
51 | + | |
52 | +# OpenVPN | |
53 | + { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, | |
54 | +# Jabber | |
55 | + { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; }, | |
56 | + | |
57 | +# Catch-all | |
58 | + { name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; }, | |
59 | + | |
60 | +# Where to connect in case of timeout (defaults to ssh) | |
43 | 61 | { name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; } |
44 | 62 | ); |
45 | 63 | |
46 | 64 | # Optionally, specify to which protocol to connect in case |
47 | 65 | # of timeout (defaults to "ssh"). |
48 | -# You can timeout to any arbitrary address by setting a | |
49 | -# protocol with no probe, as is the case with this example. | |
66 | +# You can timeout to any arbitrary address by setting an | |
67 | +# entry in 'protocols' named "timeout". | |
50 | 68 | # This enables you to set a tcpd service name for this |
51 | 69 | # protocol too. |
52 | 70 | on-timeout: "timeout"; |
53 | 71 |
probe.c | ||
---|---|---|
@@ -1,8 +1,8 @@ | ||
1 | 1 | /* |
2 | 2 | |
3 | 3 | # |
4 | -# Copyright (C) 2007-2012 Yves Rutschle | |
4 | +# Copyright (C) 2007-2015 Yves Rutschle | |
5 | 5 | # |
6 | 6 | # This program is free software; you can redistribute it |
7 | 7 | |
8 | 8 | # License as published by the Free Software Foundation; either |
@@ -216,8 +216,36 @@ | ||
216 | 216 | |
217 | 217 | return PROBE_NEXT; |
218 | 218 | } |
219 | 219 | |
220 | +static int is_sni_protocol(const char *p, int len, struct proto *proto) | |
221 | +{ | |
222 | + int valid_tls; | |
223 | + char *hostname; | |
224 | + char **sni_hostname; | |
225 | + | |
226 | + valid_tls = parse_tls_header(p, len, &hostname); | |
227 | + | |
228 | + if(valid_tls < 0) | |
229 | + return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT; | |
230 | + | |
231 | + if (verbose) fprintf(stderr, "sni hostname: %s\n", hostname); | |
232 | + | |
233 | + /* Assume does not match */ | |
234 | + valid_tls = PROBE_NEXT; | |
235 | + | |
236 | + for (sni_hostname = proto->data; *sni_hostname; sni_hostname++) { | |
237 | + fprintf(stderr, "matching [%s] with [%s]\n", hostname, *sni_hostname); | |
238 | + if(!strcmp(hostname, *sni_hostname)) { | |
239 | + valid_tls = PROBE_MATCH; | |
240 | + break; | |
241 | + } | |
242 | + } | |
243 | + | |
244 | + free(hostname); | |
245 | + return valid_tls; | |
246 | +} | |
247 | + | |
220 | 248 | static int is_tls_protocol(const char *p, int len, struct proto *proto) |
221 | 249 | { |
222 | 250 | if (len < 3) |
223 | 251 | return PROBE_AGAIN; |
@@ -334,8 +362,17 @@ | ||
334 | 362 | * regexp is not legal on the command line)*/ |
335 | 363 | if (!strcmp(description, "regex")) |
336 | 364 | return regex_probe; |
337 | 365 | |
366 | + /* Special case of "sni" probe for same reason as above*/ | |
367 | + if (!strcmp(description, "sni")) | |
368 | + return is_sni_protocol; | |
369 | + | |
370 | + /* Special case of "timeout" is allowed as a probe name in the | |
371 | + * configuration file even though it's not really a probe */ | |
372 | + if (!strcmp(description, "timeout")) | |
373 | + return is_true; | |
374 | + | |
338 | 375 | return NULL; |
339 | 376 | } |
340 | 377 | |
341 | 378 |
probe.h | ||
---|---|---|
@@ -3,8 +3,9 @@ | ||
3 | 3 | |
4 | 4 | |
5 | 5 | |
6 | 6 | |
7 | + | |
7 | 8 | |
8 | 9 | typedef enum { |
9 | 10 | PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */ |
10 | 11 | PROBE_MATCH, /* Enough data, probe successful -- it's the current protocol */ |
@@ -22,9 +23,9 @@ | ||
22 | 23 | |
23 | 24 | /* function to probe that protocol; parameters are buffer and length |
24 | 25 | * containing the data to probe, and a pointer to the protocol structure */ |
25 | 26 | T_PROBE* probe; |
26 | - void* data; /* opaque pointer ; used to pass list of regex to regex probe */ | |
27 | + void* data; /* opaque pointer ; used to pass list of regex to regex probe, or sni hostnames to sni probe */ | |
27 | 28 | struct proto *next; /* pointer to next protocol in list, NULL if last */ |
28 | 29 | }; |
29 | 30 | |
30 | 31 | /* Returns a pointer to the array of builtin protocols */ |
sslh-main.c | ||
---|---|---|
@@ -210,15 +210,47 @@ | ||
210 | 210 | |
211 | 211 | } |
212 | 212 | |
213 | 213 | |
214 | + | |
215 | +static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames) | |
216 | +{ | |
217 | + int num_probes, i, max_server_name_len, server_name_len; | |
218 | + const char * sni_hostname; | |
219 | + char** sni_hostname_list; | |
220 | + | |
221 | + num_probes = config_setting_length(sni_hostnames); | |
222 | + if (!num_probes) { | |
223 | + fprintf(stderr, "%s: no sni_hostnames specified\n", p->description); | |
224 | + exit(1); | |
225 | + } | |
226 | + | |
227 | + max_server_name_len = 0; | |
228 | + for (i = 0; i < num_probes; i++) { | |
229 | + server_name_len = strlen(config_setting_get_string_elem(sni_hostnames, i)); | |
230 | + if(server_name_len > max_server_name_len) | |
231 | + max_server_name_len = server_name_len; | |
232 | + } | |
233 | + | |
234 | + sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len); | |
235 | + p->data = (void*)sni_hostname_list; | |
236 | + | |
237 | + for (i = 0; i < num_probes; i++) { | |
238 | + sni_hostname = config_setting_get_string_elem(sni_hostnames, i); | |
239 | + sni_hostname_list[i] = malloc(max_server_name_len); | |
240 | + strcpy (sni_hostname_list[i], sni_hostname); | |
241 | + if(verbose) fprintf(stderr, "sni_hostnames[%d]: %s\n", i, sni_hostname_list[i]); | |
242 | + } | |
243 | +} | |
244 | + | |
245 | + | |
214 | 246 | /* Extract configuration for protocols to connect to. |
215 | 247 | * out: newly-allocated list of protocols |
216 | 248 | */ |
217 | 249 | |
218 | 250 | static int config_protocols(config_t *config, struct proto **prots) |
219 | 251 | { |
220 | - config_setting_t *setting, *prot, *probes; | |
252 | + config_setting_t *setting, *prot, *patterns, *sni_hostnames; | |
221 | 253 | const char *hostname, *port, *name; |
222 | 254 | int i, num_prots; |
223 | 255 | struct proto *p, *prev = NULL; |
224 | 256 | |
@@ -240,30 +272,27 @@ | ||
240 | 272 | config_setting_lookup_string(prot, "service", &(p->service)); |
241 | 273 | |
242 | 274 | resolve_split_name(&(p->saddr), hostname, port); |
243 | 275 | |
276 | + p->probe = get_probe(name); | |
277 | + if (!p->probe) { | |
278 | + fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name); | |
279 | + exit(1); | |
280 | + } | |
244 | 281 | |
245 | - probes = config_setting_get_member(prot, "probe"); | |
246 | - if (probes) { | |
247 | - if (config_setting_is_array(probes)) { | |
248 | - /* If 'probe' is an array, setup a regex probe using the | |
249 | - * array of strings as pattern */ | |
282 | + /* Probe-specific options: regex patterns */ | |
283 | + if (!strcmp(name, "regex")) { | |
284 | + patterns = config_setting_get_member(prot, "regex_patterns"); | |
285 | + if (patterns && config_setting_is_array(patterns)) { | |
286 | + setup_regex_probe(p, patterns); | |
287 | + } | |
288 | + } | |
250 | 289 | |
251 | - setup_regex_probe(p, probes); | |
252 | - | |
253 | - } else { | |
254 | - /* if 'probe' is 'builtin', set the probe to the | |
255 | - * appropriate builtin protocol */ | |
256 | - if (!strcmp(config_setting_get_string(probes), "builtin")) { | |
257 | - p->probe = get_probe(name); | |
258 | - if (!p->probe) { | |
259 | - fprintf(stderr, "%s: no builtin probe for this protocol\n", name); | |
260 | - exit(1); | |
261 | - } | |
262 | - } else { | |
263 | - fprintf(stderr, "%s: illegal probe name\n", name); | |
264 | - exit(1); | |
265 | - } | |
290 | + /* Probe-specific options: SNI hostnames */ | |
291 | + if (!strcmp(name, "sni")) { | |
292 | + sni_hostnames = config_setting_get_member(prot, "sni_hostnames"); | |
293 | + if (sni_hostnames && config_setting_is_array(sni_hostnames)) { | |
294 | + setup_sni_hostnames(p, sni_hostnames); | |
266 | 295 | } |
267 | 296 | } |
268 | 297 | } |
269 | 298 | } |
sslh.pod | ||
---|---|---|
@@ -50,16 +50,12 @@ | ||
50 | 50 | and the list of protocols). |
51 | 51 | |
52 | 52 | The configuration file makes it possible to specify |
53 | 53 | protocols using regular expressions: a list of regular |
54 | -expressions is given as the I<probe> parameter, and if the | |
54 | +expressions is given as the I<regex_patterns> parameter, and if the | |
55 | 55 | first packet received from the client matches any of these |
56 | 56 | expressions, B<sslh> connects to that protocol. |
57 | 57 | |
58 | -Alternatively, the I<probe> parameter can be set to | |
59 | -"builtin", to use the compiled probes which are much faster | |
60 | -than regular expressions. | |
61 | - | |
62 | 58 | =head2 Probing protocols |
63 | 59 | |
64 | 60 | When receiving an incoming connection, B<sslh> will read the |
65 | 61 | first bytes sent be the connecting client. It will then |
tls.c | ||
---|---|---|
@@ -1,0 +1,238 @@ | ||
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 | +static int parse_extensions(const char *, size_t, char **); | |
46 | +static int parse_server_name_extension(const char *, size_t, char **); | |
47 | + | |
48 | +const char tls_alert[] = { | |
49 | + 0x15, /* TLS Alert */ | |
50 | + 0x03, 0x01, /* TLS version */ | |
51 | + 0x00, 0x02, /* Payload length */ | |
52 | + 0x02, 0x28, /* Fatal, handshake failure */ | |
53 | +}; | |
54 | + | |
55 | +/* Parse a TLS packet for the Server Name Indication extension in the client | |
56 | + * hello handshake, returning the first servername found (pointer to static | |
57 | + * array) | |
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 - malloc failure | |
66 | + * < -4 - Invalid TLS client hello | |
67 | + */ | |
68 | +int | |
69 | +parse_tls_header(const char *data, size_t data_len, char **hostname) { | |
70 | + char tls_content_type; | |
71 | + char tls_version_major; | |
72 | + char tls_version_minor; | |
73 | + size_t pos = TLS_HEADER_LEN; | |
74 | + size_t len; | |
75 | + | |
76 | + if (hostname == NULL) | |
77 | + return -3; | |
78 | + | |
79 | + /* Check that our TCP payload is at least large enough for a TLS header */ | |
80 | + if (data_len < TLS_HEADER_LEN) | |
81 | + return -1; | |
82 | + | |
83 | + /* SSL 2.0 compatible Client Hello | |
84 | + * | |
85 | + * High bit of first byte (length) and content type is Client Hello | |
86 | + * | |
87 | + * See RFC5246 Appendix E.2 | |
88 | + */ | |
89 | + if (data[0] & 0x80 && data[2] == 1) { | |
90 | + if (verbose) fprintf(stderr, "Received SSL 2.0 Client Hello which can not support SNI.\n"); | |
91 | + return -2; | |
92 | + } | |
93 | + | |
94 | + tls_content_type = data[0]; | |
95 | + if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { | |
96 | + if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); | |
97 | + return -5; | |
98 | + } | |
99 | + | |
100 | + tls_version_major = data[1]; | |
101 | + tls_version_minor = data[2]; | |
102 | + if (tls_version_major < 3) { | |
103 | + if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n", | |
104 | + tls_version_major, tls_version_minor); | |
105 | + | |
106 | + return -2; | |
107 | + } | |
108 | + | |
109 | + /* TLS record length */ | |
110 | + len = ((unsigned char)data[3] << 8) + | |
111 | + (unsigned char)data[4] + TLS_HEADER_LEN; | |
112 | + data_len = MIN(data_len, len); | |
113 | + | |
114 | + /* Check we received entire TLS record length */ | |
115 | + if (data_len < len) | |
116 | + return -1; | |
117 | + | |
118 | + /* | |
119 | + * Handshake | |
120 | + */ | |
121 | + if (pos + 1 > data_len) { | |
122 | + return -5; | |
123 | + } | |
124 | + if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { | |
125 | + if (verbose) fprintf(stderr, "Not a client hello\n"); | |
126 | + | |
127 | + return -5; | |
128 | + } | |
129 | + | |
130 | + /* Skip past fixed length records: | |
131 | + 1 Handshake Type | |
132 | + 3 Length | |
133 | + 2 Version (again) | |
134 | + 32 Random | |
135 | + to Session ID Length | |
136 | + */ | |
137 | + pos += 38; | |
138 | + | |
139 | + /* Session ID */ | |
140 | + if (pos + 1 > data_len) | |
141 | + return -5; | |
142 | + len = (unsigned char)data[pos]; | |
143 | + pos += 1 + len; | |
144 | + | |
145 | + /* Cipher Suites */ | |
146 | + if (pos + 2 > data_len) | |
147 | + return -5; | |
148 | + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; | |
149 | + pos += 2 + len; | |
150 | + | |
151 | + /* Compression Methods */ | |
152 | + if (pos + 1 > data_len) | |
153 | + return -5; | |
154 | + len = (unsigned char)data[pos]; | |
155 | + pos += 1 + len; | |
156 | + | |
157 | + if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { | |
158 | + if (verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n"); | |
159 | + return -2; | |
160 | + } | |
161 | + | |
162 | + /* Extensions */ | |
163 | + if (pos + 2 > data_len) | |
164 | + return -5; | |
165 | + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; | |
166 | + pos += 2; | |
167 | + | |
168 | + if (pos + len > data_len) | |
169 | + return -5; | |
170 | + return parse_extensions(data + pos, len, hostname); | |
171 | +} | |
172 | + | |
173 | +int | |
174 | +parse_extensions(const char *data, size_t data_len, char **hostname) { | |
175 | + size_t pos = 0; | |
176 | + size_t len; | |
177 | + | |
178 | + /* Parse each 4 bytes for the extension header */ | |
179 | + while (pos + 4 <= data_len) { | |
180 | + /* Extension Length */ | |
181 | + len = ((unsigned char)data[pos + 2] << 8) + | |
182 | + (unsigned char)data[pos + 3]; | |
183 | + | |
184 | + /* Check if it's a server name extension */ | |
185 | + if (data[pos] == 0x00 && data[pos + 1] == 0x00) { | |
186 | + /* There can be only one extension of each type, so we break | |
187 | + our state and move p to beinnging of the extension here */ | |
188 | + if (pos + 4 + len > data_len) | |
189 | + return -5; | |
190 | + return parse_server_name_extension(data + pos + 4, len, hostname); | |
191 | + } | |
192 | + pos += 4 + len; /* Advance to the next extension header */ | |
193 | + } | |
194 | + /* Check we ended where we expected to */ | |
195 | + if (pos != data_len) | |
196 | + return -5; | |
197 | + | |
198 | + return -2; | |
199 | +} | |
200 | + | |
201 | +int | |
202 | +parse_server_name_extension(const char *data, size_t data_len, | |
203 | + char **hostname) { | |
204 | + size_t pos = 2; /* skip server name list length */ | |
205 | + size_t len; | |
206 | + | |
207 | + while (pos + 3 < data_len) { | |
208 | + len = ((unsigned char)data[pos + 1] << 8) + | |
209 | + (unsigned char)data[pos + 2]; | |
210 | + | |
211 | + if (pos + 3 + len > data_len) | |
212 | + return -5; | |
213 | + | |
214 | + switch (data[pos]) { /* name type */ | |
215 | + case 0x00: /* host_name */ | |
216 | + *hostname = malloc(len + 1); | |
217 | + if (*hostname == NULL) { | |
218 | + if (verbose) fprintf(stderr, "malloc() failure\n"); | |
219 | + return -4; | |
220 | + } | |
221 | + | |
222 | + strncpy(*hostname, data + pos + 3, len); | |
223 | + | |
224 | + (*hostname)[len] = '\0'; | |
225 | + | |
226 | + return len; | |
227 | + default: | |
228 | + if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", | |
229 | + data[pos]); | |
230 | + } | |
231 | + pos += 3 + len; | |
232 | + } | |
233 | + /* Check we ended where we expected to */ | |
234 | + if (pos != data_len) | |
235 | + return -5; | |
236 | + | |
237 | + return -2; | |
238 | +} |
tls.h | ||
---|---|---|
@@ -1,0 +1,33 @@ | ||
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 | + | |
28 | + | |
29 | + | |
30 | + | |
31 | +int parse_tls_header(const char *data, size_t data_len, char **hostname); | |
32 | + | |
33 | + |
Built with git-ssb-web