git ssb

0+

cel / sslh



Commit 8af039d3eb3e890482fd6755532d7786a104cae8

Add ALPN protocol based probe

moparisthebest committed on 1/5/2016, 5:32:10 AM
Parent: 8758a298ba730832838ef982fa8a2e07d3ca6f69

Files changed

ChangeLogchanged
example.cfgchanged
probe.cchanged
probe.hchanged
sslh-main.cchanged
tls.cchanged
tls.hchanged
ChangeLogView
@@ -1,18 +1,18 @@
11 vNEXT:
22 Added USELIBPCRE to make use of regex engine
33 optional.
44
5- Added support for RFC4366 SNI
5+ Added support for RFC4366 SNI and RFC7301 ALPN
66 (Travis Burtrum)
77
88 Changed connection log to include the name of the probe that
99 triggered.
1010
1111 Changed configuration file format: 'probe' field is
1212 no longer required, 'name' field can now contain
13- 'sni' or 'regex', with corresponding options (see
14- example.org)
13+ 'tls' or 'regex', with corresponding options (see
14+ example.cfg)
1515 Added 'log_level' option to each protocol, which
1616 allows to turn off generation of log at each
1717 connection.
1818
example.cfgView
@@ -23,33 +23,49 @@
2323 # List of protocols
2424 #
2525 # Each protocol entry consists of:
2626 # name: name of the probe. These are listed on the command
27-# line (ssh -?), plus 'regex', 'sni' and 'timeout'.
27+# line (ssh -?), plus 'regex' and 'timeout'.
2828
2929 # service: (optional) libwrap service name (see hosts_access(5))
3030 # host, port: where to connect when this probe succeeds
3131 #
3232 # Probe-specific options:
33-# sni:
34-# sni_hotnames: list of FQDN for that target
33+# tls:
34+# sni_hostnames: list of FQDN for that target
35+# alpn_protocols: list of ALPN protocols for that target, see:
36+# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
37+#
38+# if both sni_hostnames AND alpn_protocols are specified, both must match
39+# if neither are set, it is just checked whether this is the TLS protocol or not
3540 # regex:
3641 # regex_patterns: list of patterns to match for
3742 # that target.
3843 #
3944 # sslh will try each probe in order they are declared, and
4045 # connect to the first that matches.
4146 #
42-# You can specify several of 'regex' and 'sni'.
47+# You can specify several of 'regex' and 'tls'.
4348
4449 protocols:
4550 (
4651 { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; },
4752 { name: "http"; host: "localhost"; port: "80"; },
4853
49- { name: "sni"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; },
50- { name: "sni"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;},
54+# match BOTH ALPN/SNI
55+ { name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0;},
5156
57+# just match ALPN
58+ { name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; },
59+ { name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0;},
60+
61+# just match SNI
62+ { name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; },
63+ { name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0;},
64+
65+# catch anything else TLS
66+ { name: "tls"; host: "localhost"; port: "443"; },
67+
5268 # OpenVPN
5369 { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
5470 # Jabber
5571 { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; },
probe.cView
@@ -216,34 +216,19 @@
216216
217217 return PROBE_NEXT;
218218 }
219219
220-static int is_sni_protocol(const char *p, int len, struct proto *proto)
220+static int is_sni_alpn_protocol(const char *p, int len, struct proto *proto)
221221 {
222222 int valid_tls;
223- char *hostname;
224- char **sni_hostname;
225223
226- valid_tls = parse_tls_header(p, len, &hostname);
224+ valid_tls = parse_tls_header(proto->data, p, len);
227225
228226 if(valid_tls < 0)
229227 return -1 == valid_tls ? PROBE_AGAIN : PROBE_NEXT;
230228
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;
229+ /* There *was* a valid match */
230+ return PROBE_MATCH;
246231 }
247232
248233 static int is_tls_protocol(const char *p, int len, struct proto *proto)
249234 {
@@ -362,11 +347,11 @@
362347 * regexp is not legal on the command line)*/
363348 if (!strcmp(description, "regex"))
364349 return regex_probe;
365350
366- /* Special case of "sni" probe for same reason as above*/
367- if (!strcmp(description, "sni"))
368- return is_sni_protocol;
351+ /* Special case of "sni/alpn" probe for same reason as above*/
352+ if (!strcmp(description, "sni_alpn"))
353+ return is_sni_alpn_protocol;
369354
370355 /* Special case of "timeout" is allowed as a probe name in the
371356 * configuration file even though it's not really a probe */
372357 if (!strcmp(description, "timeout"))
probe.hView
@@ -26,9 +26,10 @@
2626
2727 /* function to probe that protocol; parameters are buffer and length
2828 * containing the data to probe, and a pointer to the protocol structure */
2929 T_PROBE* probe;
30- void* data; /* opaque pointer ; used to pass list of regex to regex probe, or sni hostnames to sni probe */
30+ /* opaque pointer ; used to pass list of regex to regex probe, or TLSProtocol struct to sni/alpn probe */
31+ void* data;
3132 struct proto *next; /* pointer to next protocol in list, NULL if last */
3233 };
3334
3435 /* Returns a pointer to the array of builtin protocols */
sslh-main.cView
@@ -212,46 +212,59 @@
212212 }
213213 #endif
214214
215215 #ifdef LIBCONFIG
216-static void setup_sni_hostnames(struct proto *p, config_setting_t* sni_hostnames)
216+static void setup_sni_alpn_list(struct proto *p, config_setting_t* config_items, const char* name, int alpn)
217217 {
218218 int num_probes, i, max_server_name_len, server_name_len;
219- const char * sni_hostname;
219+ const char * config_item;
220220 char** sni_hostname_list;
221221
222- num_probes = config_setting_length(sni_hostnames);
222+ if(!config_items || !config_setting_is_array(config_items)) {
223+ fprintf(stderr, "%s: no %s specified\n", p->description, name);
224+ return;
225+ }
226+ num_probes = config_setting_length(config_items);
223227 if (!num_probes) {
224- fprintf(stderr, "%s: no sni_hostnames specified\n", p->description);
225- exit(1);
228+ fprintf(stderr, "%s: no %s specified\n", p->description, name);
229+ return;
226230 }
227231
228232 max_server_name_len = 0;
229233 for (i = 0; i < num_probes; i++) {
230- server_name_len = strlen(config_setting_get_string_elem(sni_hostnames, i));
234+ server_name_len = strlen(config_setting_get_string_elem(config_items, i));
231235 if(server_name_len > max_server_name_len)
232236 max_server_name_len = server_name_len;
233237 }
234238
235239 sni_hostname_list = calloc(num_probes + 1, ++max_server_name_len);
236- p->data = (void*)sni_hostname_list;
237240
238241 for (i = 0; i < num_probes; i++) {
239- sni_hostname = config_setting_get_string_elem(sni_hostnames, i);
242+ config_item = config_setting_get_string_elem(config_items, i);
240243 sni_hostname_list[i] = malloc(max_server_name_len);
241- strcpy (sni_hostname_list[i], sni_hostname);
242- if(verbose) fprintf(stderr, "sni_hostnames[%d]: %s\n", i, sni_hostname_list[i]);
244+ strcpy (sni_hostname_list[i], config_item);
245+ if(verbose) fprintf(stderr, "%s: %s[%d]: %s\n", p->description, name, i, sni_hostname_list[i]);
243246 }
247+
248+ p->data = (void*)tls_data_set_list(p->data, alpn, sni_hostname_list);
244249 }
250+
251+static void setup_sni_alpn(struct proto *p, config_setting_t* sni_hostnames, config_setting_t* alpn_protocols)
252+{
253+ p->data = (void*)new_tls_data();
254+ p->probe = get_probe("sni_alpn");
255+ setup_sni_alpn_list(p, sni_hostnames, "sni_hostnames", 0);
256+ setup_sni_alpn_list(p, alpn_protocols, "alpn_protocols", 1);
257+}
245258 #endif
246259
247260 /* Extract configuration for protocols to connect to.
248261 * out: newly-allocated list of protocols
249262 */
250263 #ifdef LIBCONFIG
251264 static int config_protocols(config_t *config, struct proto **prots)
252265 {
253- config_setting_t *setting, *prot, *patterns, *sni_hostnames;
266+ config_setting_t *setting, *prot, *patterns, *sni_hostnames, *alpn_protocols;
254267 const char *hostname, *port, *name;
255268 int i, num_prots;
256269 struct proto *p, *prev = NULL;
257270
@@ -278,9 +291,9 @@
278291
279292 resolve_split_name(&(p->saddr), hostname, port);
280293
281294 p->probe = get_probe(name);
282- if (!p->probe) {
295+ if (!p->probe || !strcmp(name, "sni_alpn")) {
283296 fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name);
284297 exit(1);
285298 }
286299
@@ -291,15 +304,18 @@
291304 setup_regex_probe(p, patterns);
292305 }
293306 }
294307
295- /* Probe-specific options: SNI hostnames */
296- if (!strcmp(name, "sni")) {
308+ /* Probe-specific options: SNI/ALPN */
309+ if (!strcmp(name, "tls")) {
297310 sni_hostnames = config_setting_get_member(prot, "sni_hostnames");
298- if (sni_hostnames && config_setting_is_array(sni_hostnames)) {
299- setup_sni_hostnames(p, sni_hostnames);
311+ alpn_protocols = config_setting_get_member(prot, "alpn_protocols");
312+
313+ if((sni_hostnames && config_setting_is_array(sni_hostnames)) || (alpn_protocols && config_setting_is_array(alpn_protocols))) {
314+ setup_sni_alpn(p, sni_hostnames, alpn_protocols);
300315 }
301316 }
317+
302318 }
303319 }
304320 }
305321
tls.cView
@@ -29,10 +29,8 @@
2929 * TLS handshake and RFC4366.
3030 */
3131 #include <stdio.h>
3232 #include <stdlib.h> /* malloc() */
33-#include <string.h> /* strncpy() */
34-#include <sys/socket.h>
3533 #include "tls.h"
3634
3735 #define TLS_HEADER_LEN 5
3836 #define TLS_HANDSHAKE_CONTENT_TYPE 0x16
@@ -41,57 +39,43 @@
4139 #ifndef MIN
4240 #define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
4341 #endif
4442
45-static int parse_extensions(const char *, size_t, char **);
46-static int parse_server_name_extension(const char *, size_t, char **);
4743
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 */
44+struct TLSProtocol {
45+ int use_alpn;
46+ char** sni_hostname_list;
47+ char** alpn_protocol_list;
5348 };
5449
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)
50+static int parse_extensions(const struct TLSProtocol *, const char *, size_t);
51+static int parse_server_name_extension(const struct TLSProtocol *, const char *, size_t);
52+static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
53+static 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
5857 *
5958 * Returns:
6059 * >=0 - length of the hostname and updates *hostname
6160 * caller is responsible for freeing *hostname
6261 * -1 - Incomplete request
6362 * -2 - No Host header included in this request
6463 * -3 - Invalid hostname pointer
65- * -4 - malloc failure
6664 * < -4 - Invalid TLS client hello
6765 */
6866 int
69-parse_tls_header(const char *data, size_t data_len, char **hostname) {
67+parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
7068 char tls_content_type;
7169 char tls_version_major;
7270 char tls_version_minor;
7371 size_t pos = TLS_HEADER_LEN;
7472 size_t len;
7573
76- if (hostname == NULL)
77- return -3;
78-
7974 /* Check that our TCP payload is at least large enough for a TLS header */
8075 if (data_len < TLS_HEADER_LEN)
8176 return -1;
8277
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-
9478 tls_content_type = data[0];
9579 if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
9680 if (verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n");
9781 return -5;
@@ -99,17 +83,17 @@
9983
10084 tls_version_major = data[1];
10185 tls_version_minor = data[2];
10286 if (tls_version_major < 3) {
103- if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which which can not support SNI.\n",
87+ if (verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n",
10488 tls_version_major, tls_version_minor);
10589
10690 return -2;
10791 }
10892
10993 /* TLS record length */
11094 len = ((unsigned char)data[3] << 8) +
111- (unsigned char)data[4] + TLS_HEADER_LEN;
95+ (unsigned char)data[4] + TLS_HEADER_LEN;
11296 data_len = MIN(data_len, len);
11397
11498 /* Check we received entire TLS record length */
11599 if (data_len < len)
@@ -166,65 +150,103 @@
166150 pos += 2;
167151
168152 if (pos + len > data_len)
169153 return -5;
170- return parse_extensions(data + pos, len, hostname);
154+ return parse_extensions(tls_data, data + pos, len);
171155 }
172156
173-int
174-parse_extensions(const char *data, size_t data_len, char **hostname) {
157+static int
158+parse_extensions(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
175159 size_t pos = 0;
176160 size_t len;
161+ int last_matched = 0;
177162
163+ if (tls_data == NULL)
164+ return -3;
165+
178166 /* Parse each 4 bytes for the extension header */
179167 while (pos + 4 <= data_len) {
180168 /* Extension Length */
181- len = ((unsigned char)data[pos + 2] << 8) +
182- (unsigned char)data[pos + 3];
169+ len = ((unsigned char) data[pos + 2] << 8) +
170+ (unsigned char) data[pos + 3];
183171
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+
184179 /* 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);
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);
191218 }
219+
192220 pos += 4 + len; /* Advance to the next extension header */
193221 }
222+
194223 /* Check we ended where we expected to */
195224 if (pos != data_len)
196225 return -5;
197226
198227 return -2;
199228 }
200229
201-int
202-parse_server_name_extension(const char *data, size_t data_len,
203- char **hostname) {
230+static int
231+parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data, size_t data_len) {
204232 size_t pos = 2; /* skip server name list length */
205233 size_t len;
206234
207235 while (pos + 3 < data_len) {
208236 len = ((unsigned char)data[pos + 1] << 8) +
209- (unsigned char)data[pos + 2];
237+ (unsigned char)data[pos + 2];
210238
211239 if (pos + 3 + len > data_len)
212240 return -5;
213241
214242 switch (data[pos]) { /* name type */
215243 case 0x00: /* host_name */
216- *hostname = malloc(len + 1);
217- if (*hostname == NULL) {
218- if (verbose) fprintf(stderr, "malloc() failure\n");
219- return -4;
244+ if(has_match(tls_data->sni_hostname_list, data + pos + 3, len)) {
245+ return len;
246+ } else {
247+ return -2;
220248 }
221-
222- strncpy(*hostname, data + pos + 3, len);
223-
224- (*hostname)[len] = '\0';
225-
226- return len;
227249 default:
228250 if (verbose) fprintf(stderr, "Unknown server name extension name type: %d\n",
229251 data[pos]);
230252 }
@@ -235,4 +257,71 @@
235257 return -5;
236258
237259 return -2;
238260 }
261+
262+static int
263+parse_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+
287+static int
288+has_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+
300+struct TLSProtocol *
301+new_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+
310+struct TLSProtocol *
311+tls_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+}
tls.hView
@@ -27,7 +27,12 @@
2727 #define TLS_H
2828
2929 #include "common.h"
3030
31-int parse_tls_header(const char *data, size_t data_len, char **hostname);
31+struct TLSProtocol;
3232
33+int parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t data_len);
34+
35+struct TLSProtocol *new_tls_data();
36+struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, char**);
37+
3338 #endif

Built with git-ssb-web