From ffe997162460c277fe565a88c33a0a44b0cc71df Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 30 Aug 2018 19:50:53 +0200 Subject: [PATCH] test suite for SNI/ALPN with multiple targets and all combinations covered --- ChangeLog | 7 ++++++ t | 72 ++++++++++++++++++++++++++++++++++++++----------------- test.cfg | 11 +++++---- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6289c91..f09c7bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,13 @@ vNEXT: thing to know when writing the configuration file is that 'anyprot' needs to be last. + Test suite heavily refactored; `t` uses `test.cfg` + to decide which probes to test and all setup is + automatic; probes get tested with 'fast' (entire + first message in one packet) and 'slow' (one byte at + a time); when SNI/ALPN are defined, all combinations + are tested. + v1.19: 20JAN2018 Added 'syslog_facility' configuration option to specify where to log. diff --git a/t b/t index f6324f7..d78f854 100755 --- a/t +++ b/t @@ -51,6 +51,17 @@ sub verbose_exec +# For SNI/ALPN, build a protocol name as such: +# tls:sni1,sni2,...;alpn1,alpn2,... +# input: a protocol entry from Libconfig +sub make_sni_alpn_name { + my ($prot) = @_; + + return "tls:" . (join ",", @{$prot->{sni_hostnames} // []}) + . ";" . (join ",", @{$prot->{alpn_protocols} // [] }); +} + + # Tests one probe: given input data, connect, verify we get # the expected server, verify shoveling works # Named options: @@ -103,8 +114,13 @@ sub test_probes { data => "GET index.html HTTP/1.1", no_frag => 1 }, 'ssl' => { data => "\x16\x03\x031234" }, - # Capture of `openssl s_client -connect server2.org:443 -alpn alpn1 -servername sni1` - 'tls' => { data => "\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\x73\x6e\x69\x31\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\x61\x6c\x70\x6e\x31\x00\x16\x00\x00\x00\x17\x00\x00hello alpn" + '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", + # Packet with SNI alone + 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", }, 'openvpn' => { data => "\x00\x00" }, 'tinc' => { data => "0 hello" }, @@ -115,36 +131,49 @@ sub test_probes { my $pattern = $protocols{$p->{name}}->{data}; - # protocol name with SNI/ALPN spec if present - my $pname; - if ($p->{sni_hostnames} or $p->{alpn_protocols}) { - $pname = $p->{name} . ":" . (join ",", @{$p->{sni_hostnames}}) - . ";" . (join ",", @{$p->{alpn_protocols}}); - } - $opts{no_frag} = 1 if $protocols{$p->{name}}->{no_frag}; - test_probe( - data => $pattern, - expected => $p->{name}, - %opts - ); + if ($p->{sni_hostnames} or $p->{alpn_protocols}) { + my $pname = make_sni_alpn_name($p); + + my @sni = @{$p->{sni_hostnames} // [""] }; + my @alpn = @{$p->{alpn_protocols} // [""] }; + + foreach my $sni ( @sni ) { + foreach my $alpn ( @alpn ) { + print "sni: $sni\nalpn: $alpn\n"; + $pattern = $protocols{tls}->{ + "data". ($sni ? "_sni" : "") . + ($alpn ? "_alpn": "") + }; + $pattern =~ s/(\$\w+)/$1/eeg; + + test_probe( + data => $pattern, + expected => $pname, + %opts + ); + } + } + } else { + test_probe( + data => $pattern, + expected => $p->{name}, + %opts + ); + + } } } + # Start an echoserver for each service foreach my $s (@{$conf->fetch_array("protocols")}) { my $prefix = $s->{name}; - # For SNI/ALPN, build a protocol name as such: - # tls:sni1,sni2,...;alpn1,alpn2,... - - if ($s->{sni_hostnames} or $s->{alpn_protocols}) { - $prefix .= ":" . join ",", @{$s->{sni_hostnames}}; - $prefix .= ";"; - $prefix .= join ",", @{$s->{alpn_protocols}}; + $prefix = make_sni_alpn_name($s); } verbose_exec "./echosrv --listen $s->{host}:$s->{port} --prefix '$prefix: '"; @@ -152,7 +181,6 @@ foreach my $s (@{$conf->fetch_array("protocols")}) { my @binaries = ('sslh-select', 'sslh-fork'); -@binaries = ('sslh-select'); for my $binary (@binaries) { warn "Testing $binary\n"; diff --git a/test.cfg b/test.cfg index 7a95a9c..a6ed779 100644 --- a/test.cfg +++ b/test.cfg @@ -1,7 +1,7 @@ # Configuration file for testing (use both by sslh under # test and the test script `t`) -verbose: true; +verbose: 2; foreground: true; inetd: false; numeric: false; @@ -29,12 +29,13 @@ protocols: { name: "tinc"; host: "localhost"; port: "9003"; }, { name: "openvpn"; host: "localhost"; port: "9004"; }, { name: "ssl"; host: "localhost"; port: "9005"; }, - { name: "tls"; host: "localhost"; port: "9006"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; }, - { name: "tls"; host: "localhost"; port: "9007"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; } - { name: "tls"; host: "localhost"; port: "9008"; alpn_protocols: [ "alpn1" ]; sni_hostnames: [ "sni3" ]; } { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, - { name: "anyprot"; host: "localhost"; port: "9011"; } + { 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: "9023"; alpn_protocols: [ "alpn3" ]; }, + { name: "tls"; host: "localhost"; port: "9024"; sni_hostnames: [ "sni3" ]; }, + { name: "anyprot"; host: "localhost"; port: "9099"; } ); on-timeout: "ssh";