comparison ssl_certificates.t @ 1865:0e1865aa9b33

Tests: reworked http SSL tests to use IO::Socket::SSL. Relevant infrastructure is provided in Test::Nginx http() functions. This also ensures that SSL handshake and various read and write operations are guarded with timeouts. The ssl_sni_reneg.t test uses IO::Socket::SSL::_get_ssl_object() to access the Net::SSLeay object directly and trigger renegotation. While not exactly correct, this seems to be good enough for tests. Similarly, IO::Socket::SSL::_get_ssl_object() is used in ssl_stapling.t, since SSL_ocsp_staple_callback is called with the socket instead of the Net::SSLeay object. Similarly, IO::Socket::SSL::_get_ssl_object() is used in ssl_verify_client.t, since there seems to be no way to obtain CA list with IO::Socket::SSL. Notable change to http() request interface is that http_end() now closes the socket. This is to make sure that SSL connections are properly closed and SSL sessions are not removed from the IO::Socket::SSL session cache. This affected access_log.t, which was modified accordingly.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 18 May 2023 18:07:17 +0300
parents dbce8fb5f5f8
children 1ba5108b6c24
comparison
equal deleted inserted replaced
1864:46351d990aee 1865:0e1865aa9b33
20 ############################################################################### 20 ###############################################################################
21 21
22 select STDERR; $| = 1; 22 select STDERR; $| = 1;
23 select STDOUT; $| = 1; 23 select STDOUT; $| = 1;
24 24
25 eval { 25 my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)
26 require Net::SSLeay; 26 ->has_daemon('openssl');
27 Net::SSLeay::load_error_strings();
28 Net::SSLeay::SSLeay_add_ssl_algorithms();
29 Net::SSLeay::randomize();
30 Net::SSLeay::SSLeay();
31 };
32 plan(skip_all => 'Net::SSLeay not installed or too old') if $@;
33
34 my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl');
35 27
36 plan(skip_all => 'no multiple certificates') if $t->has_module('BoringSSL'); 28 plan(skip_all => 'no multiple certificates') if $t->has_module('BoringSSL');
37 29
38 $t->write_file_expand('nginx.conf', <<'EOF'); 30 $t->write_file_expand('nginx.conf', <<'EOF');
39 31
49 41
50 ssl_certificate_key rsa.key; 42 ssl_certificate_key rsa.key;
51 ssl_certificate rsa.crt; 43 ssl_certificate rsa.crt;
52 ssl_ciphers DEFAULT:ECCdraft; 44 ssl_ciphers DEFAULT:ECCdraft;
53 45
46 add_header X-SSL-Protocol $ssl_protocol;
47
54 server { 48 server {
55 listen 127.0.0.1:8080 ssl; 49 listen 127.0.0.1:8443 ssl;
56 server_name localhost; 50 server_name localhost;
57 51
58 ssl_certificate_key ec.key; 52 ssl_certificate_key ec.key;
59 ssl_certificate ec.crt; 53 ssl_certificate ec.crt;
60 54
89 . "-out $d/$name.crt -keyout $d/$name.key " 83 . "-out $d/$name.crt -keyout $d/$name.key "
90 . ">>$d/openssl.out 2>&1") == 0 84 . ">>$d/openssl.out 2>&1") == 0
91 or die "Can't create certificate for $name: $!\n"; 85 or die "Can't create certificate for $name: $!\n";
92 } 86 }
93 87
88 $t->write_file('index.html', '');
89
94 $t->run()->plan(2); 90 $t->run()->plan(2);
95 91
96 ############################################################################### 92 ###############################################################################
97 93
98 like(get_cert('RSA'), qr/CN=rsa/, 'ssl cert RSA'); 94 TODO: {
99 like(get_cert('ECDSA'), qr/CN=ec/, 'ssl cert ECDSA'); 95 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'
96 if $t->has_module('LibreSSL') && test_tls13();
97
98 like(cert('RSA'), qr/CN=rsa/, 'ssl cert RSA');
99
100 }
101
102 like(cert('ECDSA'), qr/CN=ec/, 'ssl cert ECDSA');
100 103
101 ############################################################################### 104 ###############################################################################
102 105
103 sub get_version { 106 sub test_tls13 {
104 my ($s, $ssl) = get_ssl_socket(); 107 return http_get('/', SSL => 1) =~ /TLSv1.3/;
105 return Net::SSLeay::version($ssl);
106 } 108 }
107 109
108 sub get_cert { 110 sub cert {
109 my ($type) = @_; 111 my $s = get_socket(@_) || return;
110 $type = 'PSS' if $type eq 'RSA' && get_version() > 0x0303; 112 return $s->dump_peer_certificate();
111 my ($s, $ssl) = get_ssl_socket($type);
112 my $cipher = Net::SSLeay::get_cipher($ssl);
113 Test::Nginx::log_core('||', "cipher: $cipher");
114 return Net::SSLeay::dump_peer_certificate($ssl);
115 } 113 }
116 114
117 sub get_ssl_socket { 115 sub get_socket {
118 my ($type) = @_; 116 my ($type) = @_;
119 my $s;
120 117
121 eval { 118 my $ctx_cb = sub {
122 local $SIG{ALRM} = sub { die "timeout\n" }; 119 my $ctx = shift;
123 local $SIG{PIPE} = sub { die "sigpipe\n" }; 120 return unless defined $type;
124 alarm(8); 121 my $ssleay = Net::SSLeay::SSLeay();
125 $s = IO::Socket::INET->new('127.0.0.1:' . port(8080)); 122 return if ($ssleay < 0x1000200f || $ssleay == 0x20000000);
126 alarm(0); 123 my $sigalgs = 'RSA+SHA256:PSS+SHA256';
124 $sigalgs = $type . '+SHA256' unless $type eq 'RSA';
125 # SSL_CTRL_SET_SIGALGS_LIST
126 Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs)
127 or die("Failed to set sigalgs");
127 }; 128 };
128 alarm(0);
129 129
130 if ($@) { 130 return http_get(
131 log_in("died: $@"); 131 '/', start => 1,
132 return undef; 132 SSL => 1,
133 } 133 SSL_cipher_list => $type,
134 134 SSL_create_ctx_callback => $ctx_cb
135 my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); 135 );
136
137 if (defined $type) {
138 my $ssleay = Net::SSLeay::SSLeay();
139 if ($ssleay < 0x1000200f || $ssleay == 0x20000000) {
140 Net::SSLeay::CTX_set_cipher_list($ctx, $type)
141 or die("Failed to set cipher list");
142 } else {
143 # SSL_CTRL_SET_SIGALGS_LIST
144 Net::SSLeay::CTX_ctrl($ctx, 98, 0, $type . '+SHA256')
145 or die("Failed to set sigalgs");
146 }
147 }
148
149 my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!");
150 Net::SSLeay::set_fd($ssl, fileno($s));
151 Net::SSLeay::connect($ssl) or die("ssl connect");
152 return ($s, $ssl);
153 } 136 }
154 137
155 ############################################################################### 138 ###############################################################################