# HG changeset patch # User Sergey Kandaurov # Date 1484834360 -10800 # Node ID c5df4742ad40c0c2d213af32c988118c4f3f3104 # Parent 41690e007ad851bf78e5de1afb09974bc3c18acf Tests: more http/stream ssl_verify_client tests borrowed from mail. Additionally, this includes test for ssl_trusted_certificate. diff --git a/ssl_verify_client.t b/ssl_verify_client.t --- a/ssl_verify_client.t +++ b/ssl_verify_client.t @@ -12,6 +12,8 @@ use strict; use Test::More; +use Socket qw/ :DEFAULT CRLF /; + BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; @@ -22,13 +24,23 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -eval { require IO::Socket::SSL; }; -plan(skip_all => 'IO::Socket::SSL not installed') if $@; -eval { IO::Socket::SSL->can_client_sni() or die; }; -plan(skip_all => 'IO::Socket::SSL with OpenSSL SNI support required') if $@; +eval { + require Net::SSLeay; + Net::SSLeay::load_error_strings(); + Net::SSLeay::SSLeay_add_ssl_algorithms(); + Net::SSLeay::randomize(); +}; +plan(skip_all => 'Net::SSLeay not installed') if $@; + +eval { + my $ctx = Net::SSLeay::CTX_new() or die; + my $ssl = Net::SSLeay::new($ctx) or die; + Net::SSLeay::set_tlsext_host_name($ssl, 'example.org') == 1 or die; +}; +plan(skip_all => 'Net::SSLeay with OpenSSL SNI support required') if $@; my $t = Test::Nginx->new()->has(qw/http http_ssl sni/) - ->has_daemon('openssl')->plan(3); + ->has_daemon('openssl')->plan(10); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -42,27 +54,42 @@ events { http { %%TEST_GLOBALS_HTTP%% - ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; + add_header X-Verify x$ssl_client_verify:${ssl_client_cert}x; - ssl_verify_client optional_no_ca; - - add_header X-Verify $ssl_client_verify; + ssl_certificate_key 1.example.com.key; + ssl_certificate 1.example.com.crt; server { - listen 127.0.0.1:8080 ssl; + listen 127.0.0.1:8080; server_name localhost; - ssl_client_certificate client.crt; - - location / { } + ssl_verify_client on; + ssl_client_certificate 2.example.com.crt; } server { - listen 127.0.0.1:8080 ssl; - server_name example.com; + listen 127.0.0.1:8081 ssl; + server_name on; + + ssl_verify_client on; + ssl_client_certificate 2.example.com.crt; + } + + server { + listen 127.0.0.1:8081 ssl; + server_name optional; - location / { } + ssl_verify_client optional; + ssl_client_certificate 2.example.com.crt; + ssl_trusted_certificate 3.example.com.crt; + } + + server { + listen 127.0.0.1:8081 ssl; + server_name optional_no_ca; + + ssl_verify_client optional_no_ca; + ssl_client_certificate 2.example.com.crt; } } @@ -78,7 +105,7 @@ EOF my $d = $t->testdir(); -foreach my $name ('localhost', 'client') { +foreach my $name ('1.example.com', '2.example.com', '3.example.com') { system('openssl req -x509 -new ' . "-config '$d/openssl.conf' -subj '/CN=$name/' " . "-out '$d/$name.crt' -keyout '$d/$name.key' " @@ -92,46 +119,61 @@ foreach my $name ('localhost', 'client') ############################################################################### -like(get('localhost'), qr/SUCCESS/, 'success'); -like(get('example.com'), qr/FAILED/, 'failed'); -like(get('localhost', 'example.com'), qr/421 Misdirected/, 'misdirected'); +like(http_get('/t'), qr/x:x/, 'plain connection'); +like(get('on'), qr/400 Bad Request/, 'no cert'); +like(get('optional'), qr/NONE:x/, 'no optional cert'); +like(get('optional', '1.example.com'), qr/400 Bad/, 'bad optional cert'); +like(get('optional_no_ca', '1.example.com'), qr/FAILED.*BEGIN/, + 'bad optional_no_ca cert'); + +like(get('localhost', '2.example.com'), qr/SUCCESS.*BEGIN/, 'good cert'); +like(get('optional', '2.example.com'), qr/SUCCESS.*BEGI/, 'good cert optional'); +like(get('optional', '3.example.com'), qr/SUCCESS.*BEGIN/, 'good cert trusted'); + +SKIP: { +skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; + +my $ca = join ' ', get('optional', '3.example.com'); +is($ca, '/CN=2.example.com', 'no trusted sent'); + +} + +like(get('optional', undef, 'localhost'), qr/421 Misdirected/, 'misdirected'); ############################################################################### sub get { - my ($sni, $host) = @_; - my $s; + my ($sni, $cert, $host) = @_; $host = $sni if !defined $host; - eval { - local $SIG{ALRM} = sub { die "timeout\n" }; - local $SIG{PIPE} = sub { die "sigpipe\n" }; - alarm(2); - $s = IO::Socket::SSL->new( - Proto => 'tcp', - PeerAddr => '127.0.0.1', - PeerPort => port(8080), - SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), - SSL_hostname => $sni, - SSL_cert_file => "$d/client.crt", - SSL_key_file => "$d/client.key", - SSL_error_trap => sub { die $_[1] } - ); - alarm(0); - }; - alarm(0); + my $dest_ip = inet_aton('127.0.0.1'); + my $dest_serv_params = sockaddr_in(port(8081), $dest_ip); + + socket(my $s, &AF_INET, &SOCK_STREAM, 0) or die "socket: $!"; + connect($s, $dest_serv_params) or die "connect: $!"; + + my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); + Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") + or die if $cert; + my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); + Net::SSLeay::set_tlsext_host_name($ssl, $sni) == 1 or die; + Net::SSLeay::set_fd($ssl, fileno($s)); + Net::SSLeay::connect($ssl) or die("ssl connect"); - if ($@) { - log_in("died: $@"); - return undef; - } + Net::SSLeay::write($ssl, 'GET /t HTTP/1.0' . CRLF); + Net::SSLeay::write($ssl, "Host: $host" . CRLF . CRLF); + my $buf = Net::SSLeay::read($ssl); + log_in($buf); + return $buf unless wantarray(); - return http(< $s); -GET /t HTTP/1.0 -Host: $host - -EOF + my $list = Net::SSLeay::get_client_CA_list($ssl); + my @names; + for my $i (0 .. Net::SSLeay::sk_X509_NAME_num($list) - 1) { + my $name = Net::SSLeay::sk_X509_NAME_value($list, $i); + push @names, Net::SSLeay::X509_NAME_oneline($name); + } + return @names; } ############################################################################### diff --git a/stream_ssl_verify_client.t b/stream_ssl_verify_client.t --- a/stream_ssl_verify_client.t +++ b/stream_ssl_verify_client.t @@ -47,28 +47,49 @@ events { } stream { - ssl_certificate_key localhost.key; - ssl_certificate localhost.crt; + log_format status $status; - ssl_verify_client optional_no_ca; + ssl_certificate_key 1.example.com.key; + ssl_certificate 1.example.com.crt; server { - listen 127.0.0.1:8080 ssl; - return $ssl_client_verify; + listen 127.0.0.1:8080; + return $ssl_client_verify:$ssl_client_cert; - ssl_client_certificate client.crt; + ssl_verify_client on; + ssl_client_certificate 2.example.com.crt; } server { listen 127.0.0.1:8081 ssl; - return $ssl_client_verify; + return $ssl_client_verify:$ssl_client_cert; + + ssl_verify_client on; + ssl_client_certificate 2.example.com.crt; + + access_log %%TESTDIR%%/status.log status; + } + + server { + listen 127.0.0.1:8082 ssl; + return $ssl_client_verify:$ssl_client_cert; + + ssl_verify_client optional; + ssl_client_certificate 2.example.com.crt; + ssl_trusted_certificate 3.example.com.crt; + } + + server { + listen 127.0.0.1:8083 ssl; + return $ssl_client_verify:$ssl_client_cert; + + ssl_verify_client optional_no_ca; + ssl_client_certificate 2.example.com.crt; } } EOF -my $d = $t->testdir(); - $t->write_file('openssl.conf', <testdir(); + +foreach my $name ('1.example.com', '2.example.com', '3.example.com') { system('openssl req -x509 -new ' . "-config '$d/openssl.conf' -subj '/CN=$name/' " . "-out '$d/$name.crt' -keyout '$d/$name.key' " @@ -85,34 +108,79 @@ foreach my $name ('localhost', 'client') or die "Can't create certificate for $name: $!\n"; } -my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); -Net::SSLeay::set_cert_and_key($ctx, "$d/client.crt", "$d/client.key") or die; - -$t->try_run('no ssl_verify_client')->plan(2); +$t->try_run('no ssl_verify_client')->plan(10); ############################################################################### -my ($s, $ssl) = get_ssl_socket(port(8080)); -is(Net::SSLeay::read($ssl), 'SUCCESS', 'success'); +TODO: { +todo_skip 'leaves coredump', 1 unless $t->has_version('1.11.9'); + +is(stream()->read(), ':', 'plain connection'); + +} + +TODO: { +local $TODO = 'fails on one-pass ngx_ssl_handshake' + unless $t->has_version('1.11.9'); + +is(get(8081), '', 'no cert'); +is(get(8082, '1.example.com'), '', 'bad optional cert'); + +} + +is(get(8082), 'NONE:', 'no optional cert'); +like(get(8083, '1.example.com'), qr/FAILED.*BEGIN/, 'bad optional_no_ca cert'); -($s, $ssl) = get_ssl_socket(port(8081)); -like(Net::SSLeay::read($ssl), qr/FAILED/, 'failed'); +like(get(8081, '2.example.com'), qr/SUCCESS.*BEGIN/, 'good cert'); +like(get(8082, '2.example.com'), qr/SUCCESS.*BEGIN/, 'good cert optional'); +like(get(8082, '3.example.com'), qr/SUCCESS.*BEGIN/, 'good cert trusted'); + +SKIP: { +skip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36; + +my $ca = join ' ', get(8082, '3.example.com'); +is($ca, '/CN=2.example.com', 'no trusted sent'); + +} + +TODO: { +local $TODO = 'not yet, see above' unless $t->has_version('1.11.9'); + +$t->stop(); + +is($t->read_file('status.log'), "500\n200\n", 'log'); + +} ############################################################################### -sub get_ssl_socket { - my ($port) = @_; +sub get { + my ($port, $cert) = @_; my $dest_ip = inet_aton('127.0.0.1'); - my $dest_serv_params = sockaddr_in($port, $dest_ip); + my $dest_serv_params = sockaddr_in(port($port), $dest_ip); socket(my $s, &AF_INET, &SOCK_STREAM, 0) or die "socket: $!"; connect($s, $dest_serv_params) or die "connect: $!"; + my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); + Net::SSLeay::set_cert_and_key($ctx, "$d/$cert.crt", "$d/$cert.key") + or die if $cert; my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); Net::SSLeay::set_fd($ssl, fileno($s)); Net::SSLeay::connect($ssl) or die("ssl connect"); - return ($s, $ssl); + + my $buf = Net::SSLeay::read($ssl); + log_in($buf); + return $buf unless wantarray(); + + my $list = Net::SSLeay::get_client_CA_list($ssl); + my @names; + for my $i (0 .. Net::SSLeay::sk_X509_NAME_num($list) - 1) { + my $name = Net::SSLeay::sk_X509_NAME_value($list, $i); + push @names, Net::SSLeay::X509_NAME_oneline($name); + } + return @names; } ###############################################################################