diff --git a/ChangeLog b/ChangeLog index f09c7bf..8976e14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,10 @@ vNEXT: a time); when SNI/ALPN are defined, all combinations are tested. + Old 'tls' probe removed, 'sni_alpn' probe renamed as 'tls'. + You'll need to change 'sni_alpn' to 'tls' in + your configuration file, if ever you used it. + v1.19: 20JAN2018 Added 'syslog_facility' configuration option to specify where to log. diff --git a/example.cfg b/example.cfg index fd3cb1f..182f508 100644 --- a/example.cfg +++ b/example.cfg @@ -43,20 +43,25 @@ listen: # (only useful for sslh-select) # # Probe-specific options: +# (sslh will try each probe in order they are declared, and +# connect to the first that matches.) +# # tls: # sni_hostnames: list of FQDN for that target # alpn_protocols: list of ALPN protocols for that target, see: # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids # # if both sni_hostnames AND alpn_protocols are specified, both must match +# # if neither are set, it is just checked whether this is the TLS protocol or not +# +# Obviously set the most specific probes +# first, and if you use TLS with no ALPN/SNI +# set it as the last TLS probe # regex: # regex_patterns: list of patterns to match for # that target. # -# sslh will try each probe in order they are declared, and -# connect to the first that matches. -# # You can specify several of 'regex' and 'tls'. protocols: diff --git a/probe.c b/probe.c index b01d40b..7cde14c 100644 --- a/probe.c +++ b/probe.c @@ -226,7 +226,8 @@ static int is_http_protocol(const char *p, int len, struct proto *proto) return PROBE_NEXT; } -static int is_sni_alpn_protocol(const char *p, int len, struct proto *proto) +/* Says if it's TLS, optionally with SNI and ALPN lists in proto->data */ +static int is_tls_protocol(const char *p, int len, struct proto *proto) { int valid_tls; @@ -239,34 +240,6 @@ static int is_sni_alpn_protocol(const char *p, int len, struct proto *proto) return PROBE_MATCH; } -static int is_tls_protocol(const char *p, int len, struct proto *proto) -{ - if (len < 6) - return PROBE_AGAIN; - - /* TLS packet starts with a record "Hello" (0x16), followed by the number of - * the highest version of SSL/TLS supported. - * - * A SSLv2 record header contains a two or three byte length code. If the - * most significant bit is set in the first byte of the record length code - * then the record has no padding and the total header length will be 2 - * bytes, otherwise the record has padding and the total header length will - * be 3 bytes. Next, a 1 char sized client-hello (0x01) is expected, - * followed by a 2 char sized version that indicates the highest version of - * TLS/SSL supported by the sender. [SSL2] Hickman, Kipp, "The SSL Protocol" - * - * We're checking the highest version of TLS/SSL supported against - * (0x03 0x00-0x03) (RFC6101 A.1). This means we reject the usage of SSLv2 - * and lower, which is actually a good thing (RFC6176). - */ - if (p[0] == 0x16) // TLS client-hello - return p[1] == 0x03 && ( p[2] >= 0 && p[2] <= 0x03); - if ((p[0] & 0x80) != 0) // SSLv2 client-hello, no padding - return p[2] == 0x01 && p[3] == 0x03 && ( p[4] >= 0 && p[4] <= 0x03); - else // SSLv2 client-hello, padded - return p[3] == 0x01 && p[4] == 0x03 && ( p[5] >= 0 && p[5] <= 0x03); -} - static int probe_adb_cnxn_message(const char *p) { /* The initial ADB host->device packet has a command type of CNXN, and a @@ -450,10 +423,6 @@ T_PROBE* get_probe(const char* description) { if (!strcmp(description, "regex")) return regex_probe; - /* Special case of "sni/alpn" probe for same reason as above*/ - if (!strcmp(description, "sni_alpn")) - return is_sni_alpn_protocol; - /* Special case of "timeout" is allowed as a probe name in the * configuration file even though it's not really a probe */ if (!strcmp(description, "timeout")) diff --git a/sslh-main.c b/sslh-main.c index 682d286..801119f 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -290,11 +290,9 @@ static void setup_sni_alpn(struct proto *p, config_setting_t* prot) alpn_protocols = config_setting_get_member(prot, "alpn_protocols"); if(sni_hostnames && config_setting_is_array(sni_hostnames)) { - p->probe = get_probe("sni_alpn"); setup_sni_alpn_list(p, sni_hostnames, "sni_hostnames", 0); } if(alpn_protocols && config_setting_is_array(alpn_protocols)) { - p->probe = get_probe("sni_alpn"); setup_sni_alpn_list(p, alpn_protocols, "alpn_protocols", 1); } } @@ -341,7 +339,7 @@ static int config_protocols(config_t *config, struct proto **prots) } p->probe = get_probe(name); - if (!p->probe || !strcmp(name, "sni_alpn")) { + if (!p->probe) { fprintf(stderr, "line %d: %s: probe unknown\n", config_setting_source_line(prot), name); exit(1); } diff --git a/t b/t index d78f854..dfc8f05 100755 --- a/t +++ b/t @@ -113,7 +113,6 @@ sub test_probes { 'http' => { data => "GET index.html HTTP/1.1", no_frag => 1 }, - 'ssl' => { data => "\x16\x03\x031234" }, 'tls' => { # Packet with SNI and ALPN (`openssl s_client -connect localhost:443 -alpn alpn1 -servername sni1`) data_sni_alpn => "\x16\x03\x01\x00\xc4\x01\x00\x00\xc0\x03\x03\x03\x19\x01\x00\x40\x14\x13\xcc\x1b\x94\xad\x20\x5d\x13\x1a\x8d\xd2\x65\x23\x70\xde\xd1\x3c\x5d\x05\x19\xcb\x27\x0d\x7c\x2c\x89\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x5f\x00\x00\x00\x09\x00\x07\x00\x00\x04\$sni\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x10\x00\x08\x00\x06\x05\$alpn\x00\x16\x00\x00\x00\x17\x00\x00hello sni/alpn", @@ -121,6 +120,8 @@ sub test_probes { data_sni => "\x16\x03\x01\x00\xb8\x01\x00\x00\xb4\x03\x03\x97\xe4\xe9\xad\x86\xe1\x21\xfd\xc4\x5b\x27\x0e\xad\x4b\x55\xc2\x50\xe4\x1c\x86\x2f\x37\x25\xde\xe8\x9c\x59\xfc\x1b\xa9\x37\x32\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x53\x00\x00\x00\x09\x00\x07\x00\x00\x04\$sni\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello sni", # packet with ALPN alone data_alpn => "\x16\x03\x01\x00\xb7\x01\x00\x00\xb3\x03\x03\xe2\x90\xa2\x29\x03\x31\xad\x98\x44\x51\x54\x90\x5b\xd9\x51\x0e\x66\xb5\x3f\xe8\x8b\x09\xc9\xe4\x2b\x97\x24\xef\xad\x56\x06\xc9\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x52\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x10\x00\x08\x00\x06\x05\$alpn\x00\x16\x00\x00\x00\x17\x00\x00hello alpn", + # packet with no SNI, no ALPN + data => "\x16\x03\x01\x00\xab\x01\x00\x00\xa7\x03\x03\x89\x22\x33\x95\x43\x7a\xc3\x89\x45\x51\x12\x3c\x28\x24\x1b\x6a\x78\xbf\xbe\x95\xd8\x90\x58\xd7\x65\xf7\xbb\x2d\xb2\x8d\xa0\x75\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x46\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello tls alone" }, 'openvpn' => { data => "\x00\x00" }, 'tinc' => { data => "0 hello" }, @@ -181,6 +182,7 @@ foreach my $s (@{$conf->fetch_array("protocols")}) { my @binaries = ('sslh-select', 'sslh-fork'); +@binaries = ('sslh-select'); for my $binary (@binaries) { warn "Testing $binary\n"; @@ -199,8 +201,7 @@ for my $binary (@binaries) { my $test_data = "hello world\n"; -# my $ssl_test_data = (pack 'n', ((length $test_data) + 2)) . $test_data; - my $ssl_test_data = "\x16\x03\x03$test_data\n"; + my $ssl_test_data = "\x16\x03\x01\x00\xab\x01\x00\x00\xa7\x03\x03\x89\x22\x33\x95\x43\x7a\xc3\x89\x45\x51\x12\x3c\x28\x24\x1b\x6a\x78\xbf\xbe\x95\xd8\x90\x58\xd7\x65\xf7\xbb\x2d\xb2\x8d\xa0\x75\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x46\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello tls alone"; # Test: Shy SSH connection if ($SSH_SHY_CNX) { @@ -232,7 +233,7 @@ for my $binary (@binaries) { } my $data; my $n = sysread $cnx_l, $data, 1024; - is($data, "ssl: $ssl_test_data", "SSL connection interrupted by SSH"); + is($data, "tls: $ssl_test_data", "SSL connection interrupted by SSH"); } } @@ -249,7 +250,7 @@ for my $binary (@binaries) { print $cnx_l $ssl_test_data; my $data; my $n = sysread $cnx_l, $data, 1024; - is($data, "ssl: $ssl_test_data", "SSL during SSH being established"); + is($data, "tls: $ssl_test_data", "SSL during SSH being established"); } print $cnx_h $test_data; my $data = <$cnx_h>; @@ -300,7 +301,8 @@ if ($RB_CNX_NOSERVER) { my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->fetch_array("protocols")})[0]; my $ssh_address = $ssh_conf->{host} . ":" . $ssh_conf->{port}; -my $ssl_conf = (grep { $_->{name} eq "ssl" } @{$conf->fetch_array("protocols")})[0]; +# Use the last TLS echoserv (no SNI/ALPN) +my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->fetch_array("protocols")})[-1]; my $ssl_address = $ssl_conf->{host} . ":" . $ssl_conf->{port}; diff --git a/test.cfg b/test.cfg index a6ed779..a8ceb29 100644 --- a/test.cfg +++ b/test.cfg @@ -28,13 +28,13 @@ protocols: { name: "http"; host: "localhost"; port: "9002"; }, { name: "tinc"; host: "localhost"; port: "9003"; }, { name: "openvpn"; host: "localhost"; port: "9004"; }, - { name: "ssl"; host: "localhost"; port: "9005"; }, { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, - { name: "tls"; host: "localhost"; port: "9020"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; }, - { name: "tls"; host: "localhost"; port: "9021"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; }, + { name: "tls"; host: "localhost"; port: "9021"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; }, + { name: "tls"; host: "localhost"; port: "9022"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; }, { name: "tls"; host: "localhost"; port: "9023"; alpn_protocols: [ "alpn3" ]; }, { name: "tls"; host: "localhost"; port: "9024"; sni_hostnames: [ "sni3" ]; }, + { name: "tls"; host: "localhost"; port: "9025"; }, { name: "anyprot"; host: "localhost"; port: "9099"; } ); diff --git a/tls.c b/tls.c index 17352d0..6b98b8b 100644 --- a/tls.c +++ b/tls.c @@ -152,7 +152,13 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da if (pos + len > data_len) return -5; - return parse_extensions(tls_data, data + pos, len); + + /* By now we know it's TLS. if SNI/ALPN is set, parse extensions to see if + * they match. Otherwise, it's a match already */ + if (tls_data->use_alpn != -1) + return parse_extensions(tls_data, data + pos, len); + else + return 1; } static int