git ssb

0+

cel / sslh



Commit fbebdaf66c4d724f2b37639a256feb53a634a686

Add support for Server Name Indication (SNI, RFC4366)

Yves Rutschle committed on 7/15/2015, 12:07:16 PM
Parent: fecfb170c84cd6119165581a419a6545286f203a
Parent: b9885401050ad27d9fa13ffa67d5e43441f495c0

Files changed

Makefilechanged
example.cfgchanged
probe.cchanged
probe.hchanged
sslh-main.cchanged
tls.cadded
tls.hadded
MakefileView
@@ -22,9 +22,9 @@
2222 CC ?= gcc
2323 CFLAGS ?=-Wall -g $(CFLAGS_COV)
2424
2525 LIBS=
26-OBJS=common.o sslh-main.o probe.o
26+OBJS=common.o sslh-main.o probe.o tls.o
2727
2828 ifneq ($(strip $(USELIBWRAP)),)
2929 LIBS:=$(LIBS) -lwrap
3030 CPPFLAGS+=-DLIBWRAP
@@ -62,9 +62,9 @@
6262 $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS)
6363 #strip sslh-select
6464
6565 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)
6767
6868 $(MAN): sslh.pod Makefile
6969 pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
7070
example.cfgView
@@ -35,8 +35,9 @@
3535
3636 protocols:
3737 (
3838 { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; },
39+ { name: "sni"; host: "localhost"; port: "993"; probe: "builtin"; sni_hostnames: [ "imap.example.org", "imap.example.com" ]; },
3940 { name: "openvpn"; host: "localhost"; port: "1194"; probe: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
4041 { name: "xmpp"; host: "localhost"; port: "5222"; probe: [ "jabber" ]; },
4142 { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; },
4243 { name: "ssl"; host: "localhost"; port: "443"; probe: [ "" ]; },
probe.cView
@@ -216,8 +216,35 @@
216216
217217 return PROBE_NEXT;
218218 }
219219
220+static int is_sni_protocol(const char *p, int len, struct proto *proto)
221+{
222+ int valid_tls;
223+ char *hostname;
224+
225+ valid_tls = parse_tls_header(p, len, &hostname);
226+
227+ if(valid_tls < 0)
228+ return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT;
229+
230+ if (verbose) fprintf(stderr, "sni hostname: %s\n", hostname);
231+
232+ /* Assume does not match */
233+ valid_tls = PROBE_NEXT;
234+
235+ char **sni_hostname = proto->data;
236+
237+ for (; *sni_hostname; sni_hostname++)
238+ if(!strcmp(hostname, *sni_hostname)) {
239+ valid_tls = PROBE_MATCH;
240+ break;
241+ }
242+
243+ free(hostname);
244+ return valid_tls;
245+}
246+
220247 static int is_tls_protocol(const char *p, int len, struct proto *proto)
221248 {
222249 if (len < 3)
223250 return PROBE_AGAIN;
@@ -334,8 +361,12 @@
334361 * regexp is not legal on the command line)*/
335362 if (!strcmp(description, "regex"))
336363 return regex_probe;
337364
365+ /* Special case of "sni" probe for same reason as above*/
366+ if (!strcmp(description, "sni"))
367+ return is_sni_protocol;
368+
338369 return NULL;
339370 }
340371
341372
probe.hView
@@ -3,8 +3,9 @@
33 #ifndef __PROBE_H_
44 #define __PROBE_H_
55
66 #include "common.h"
7+#include "tls.h"
78
89 typedef enum {
910 PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
1011 PROBE_MATCH, /* Enough data, probe successful -- it's the current protocol */
@@ -22,9 +23,9 @@
2223
2324 /* function to probe that protocol; parameters are buffer and length
2425 * containing the data to probe, and a pointer to the protocol structure */
2526 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 */
2728 struct proto *next; /* pointer to next protocol in list, NULL if last */
2829 };
2930
3031 /* Returns a pointer to the array of builtin protocols */
sslh-main.cView
@@ -210,15 +210,47 @@
210210 #endif
211211 }
212212 #endif
213213
214+#ifdef LIBCONFIG
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+#endif
245+
214246 /* Extract configuration for protocols to connect to.
215247 * out: newly-allocated list of protocols
216248 */
217249 #ifdef LIBCONFIG
218250 static int config_protocols(config_t *config, struct proto **prots)
219251 {
220- config_setting_t *setting, *prot, *probes;
252+ config_setting_t *setting, *prot, *probes, *sni_hostnames;
221253 const char *hostname, *port, *name;
222254 int i, num_prots;
223255 struct proto *p, *prev = NULL;
224256
@@ -264,8 +296,13 @@
264296 exit(1);
265297 }
266298 }
267299 }
300+
301+ sni_hostnames = config_setting_get_member(prot, "sni_hostnames");
302+ if (sni_hostnames && config_setting_is_array(sni_hostnames)) {
303+ setup_sni_hostnames(p, sni_hostnames);
304+ }
268305 }
269306 }
270307 }
271308
tls.cView
@@ -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+#include <stdio.h>
32+#include <stdlib.h> /* malloc() */
33+#include <string.h> /* strncpy() */
34+#include <sys/socket.h>
35+#include "tls.h"
36+
37+#define TLS_HEADER_LEN 5
38+#define TLS_HANDSHAKE_CONTENT_TYPE 0x16
39+#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
40+
41+#ifndef MIN
42+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
43+#endif
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.hView
@@ -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+#ifndef TLS_H
27+#define TLS_H
28+
29+#include "common.h"
30+
31+int parse_tls_header(const char *data, size_t data_len, char **hostname);
32+
33+#endif

Built with git-ssb-web