Mercurial > hg > nginx-tests
comparison ssl_stapling.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 | af47a0b348a5 |
children | 231b14e2041a |
comparison
equal
deleted
inserted
replaced
1864:46351d990aee | 1865:0e1865aa9b33 |
---|---|
22 ############################################################################### | 22 ############################################################################### |
23 | 23 |
24 select STDERR; $| = 1; | 24 select STDERR; $| = 1; |
25 select STDOUT; $| = 1; | 25 select STDOUT; $| = 1; |
26 | 26 |
27 eval { | 27 my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/) |
28 require Net::SSLeay; | 28 ->has_daemon('openssl'); |
29 Net::SSLeay::load_error_strings(); | 29 |
30 Net::SSLeay::SSLeay_add_ssl_algorithms(); | 30 eval { defined &Net::SSLeay::set_tlsext_status_type or die; }; |
31 Net::SSLeay::randomize(); | 31 plan(skip_all => 'Net::SSLeay too old') if $@; |
32 Net::SSLeay::SSLeay(); | 32 eval { defined &IO::Socket::SSL::SSL_OCSP_TRY_STAPLE or die; }; |
33 defined &Net::SSLeay::set_tlsext_status_type or die; | 33 plan(skip_all => 'IO::Socket::SSL too old') if $@; |
34 }; | |
35 plan(skip_all => 'Net::SSLeay not installed or too old') if $@; | |
36 | |
37 my $t = Test::Nginx->new()->has(qw/http http_ssl/)->has_daemon('openssl'); | |
38 | 34 |
39 plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); | 35 plan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL'); |
40 | 36 |
41 $t->plan(10)->write_file_expand('nginx.conf', <<'EOF'); | 37 $t->plan(10)->write_file_expand('nginx.conf', <<'EOF'); |
42 | 38 |
244 | 240 |
245 $t->waitforsocket("127.0.0.1:" . port(8081)); | 241 $t->waitforsocket("127.0.0.1:" . port(8081)); |
246 | 242 |
247 ############################################################################### | 243 ############################################################################### |
248 | 244 |
249 my $version = get_version(); | |
250 | |
251 staple(8443, 'RSA'); | 245 staple(8443, 'RSA'); |
252 staple(8443, 'ECDSA'); | 246 staple(8443, 'ECDSA'); |
253 staple(8444, 'RSA'); | 247 staple(8444, 'RSA'); |
254 staple(8444, 'ECDSA'); | 248 staple(8444, 'ECDSA'); |
255 staple(8445, 'ECDSA'); | 249 staple(8445, 'ECDSA'); |
260 | 254 |
261 ok(!staple(8443, 'RSA'), 'staple revoked'); | 255 ok(!staple(8443, 'RSA'), 'staple revoked'); |
262 | 256 |
263 TODO: { | 257 TODO: { |
264 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' | 258 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' |
265 if $t->has_module('LibreSSL') && $version > 0x303; | 259 if $t->has_module('LibreSSL') && test_tls13(); |
266 | 260 |
267 ok(staple(8443, 'ECDSA'), 'staple success'); | 261 ok(staple(8443, 'ECDSA'), 'staple success'); |
268 | 262 |
269 } | 263 } |
270 | 264 |
271 ok(!staple(8444, 'RSA'), 'responder revoked'); | 265 ok(!staple(8444, 'RSA'), 'responder revoked'); |
272 | 266 |
273 TODO: { | 267 TODO: { |
274 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' | 268 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' |
275 if $t->has_module('LibreSSL') && $version > 0x303; | 269 if $t->has_module('LibreSSL') && test_tls13(); |
276 | 270 |
277 ok(staple(8444, 'ECDSA'), 'responder success'); | 271 ok(staple(8444, 'ECDSA'), 'responder success'); |
278 | 272 |
279 } | 273 } |
280 | 274 |
287 | 281 |
288 ok(!staple(8449, 'ECDSA'), 'ocsp error'); | 282 ok(!staple(8449, 'ECDSA'), 'ocsp error'); |
289 | 283 |
290 TODO: { | 284 TODO: { |
291 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' | 285 local $TODO = 'broken TLSv1.3 sigalgs in LibreSSL' |
292 if $t->has_module('LibreSSL') && $version > 0x303; | 286 if $t->has_module('LibreSSL') && test_tls13(); |
293 | 287 |
294 like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); | 288 like(`grep -F '[crit]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no crit'); |
295 | 289 |
296 } | 290 } |
297 | 291 |
300 sub staple { | 294 sub staple { |
301 my ($port, $ciphers, $ca) = @_; | 295 my ($port, $ciphers, $ca) = @_; |
302 my (@resp); | 296 my (@resp); |
303 | 297 |
304 my $staple_cb = sub { | 298 my $staple_cb = sub { |
305 my ($ssl, $resp) = @_; | 299 my ($s, $resp) = @_; |
306 push @resp, !!$resp; | 300 push @resp, !!$resp; |
307 return 1 unless $resp; | 301 return 1 unless $resp; |
302 | |
303 # Contrary to the documentation, IO::Socket::SSL calls the | |
304 # SSL_ocsp_staple_callback with the socket, and not the | |
305 # Net::SSLeay object. | |
306 | |
307 my $ssl = $s->_get_ssl_object(); | |
308 | |
308 my $cert = Net::SSLeay::get_peer_certificate($ssl); | 309 my $cert = Net::SSLeay::get_peer_certificate($ssl); |
309 my $certid = eval { Net::SSLeay::OCSP_cert2ids($ssl, $cert) } | 310 my $certid = eval { Net::SSLeay::OCSP_cert2ids($ssl, $cert) } |
310 or do { die "no OCSP_CERTID for certificate: $@"; }; | 311 or do { die "no OCSP_CERTID for certificate: $@"; }; |
311 | 312 |
312 my @res = Net::SSLeay::OCSP_response_results($resp, $certid); | 313 my @res = Net::SSLeay::OCSP_response_results($resp, $certid); |
313 push @resp, $res[0][2]->{'statusType'}; | 314 push @resp, $res[0][2]->{'statusType'}; |
314 }; | 315 }; |
315 | 316 |
316 my $s; | 317 my $ctx_cb = sub { |
317 | 318 my $ctx = shift; |
318 eval { | 319 return unless defined $ciphers; |
319 local $SIG{ALRM} = sub { die "timeout\n" }; | 320 my $ssleay = Net::SSLeay::SSLeay(); |
320 local $SIG{PIPE} = sub { die "sigpipe\n" }; | 321 return if ($ssleay < 0x1000200f || $ssleay == 0x20000000); |
321 alarm(8); | 322 my $sigalgs = 'RSA+SHA256:PSS+SHA256'; |
322 $s = IO::Socket::INET->new('127.0.0.1:' . port($port)); | 323 $sigalgs = $ciphers . '+SHA256' unless $ciphers eq 'RSA'; |
323 alarm(0); | 324 # SSL_CTRL_SET_SIGALGS_LIST |
325 Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs) | |
326 or die("Failed to set sigalgs"); | |
324 }; | 327 }; |
325 alarm(0); | 328 |
326 | 329 my $s = http_get( |
327 if ($@) { | 330 '/', start => 1, PeerAddr => '127.0.0.1:' . port($port), |
328 log_in("died: $@"); | 331 SSL => 1, |
329 return undef; | 332 SSL_cipher_list => $ciphers, |
330 } | 333 SSL_create_ctx_callback => $ctx_cb, |
331 | 334 SSL_ocsp_staple_callback => $staple_cb, |
332 my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); | 335 SSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_TRY_STAPLE(), |
333 | 336 SSL_ca_file => $ca |
334 my $ssleay = Net::SSLeay::SSLeay(); | 337 ); |
335 if ($ssleay < 0x1000200f || $ssleay == 0x20000000) { | 338 |
336 Net::SSLeay::CTX_set_cipher_list($ctx, $ciphers) | 339 return $s unless $s; |
337 or die("Failed to set cipher list"); | |
338 } else { | |
339 # SSL_CTRL_SET_SIGALGS_LIST | |
340 $ciphers = 'PSS' if $ciphers eq 'RSA' && $version > 0x0303; | |
341 Net::SSLeay::CTX_ctrl($ctx, 98, 0, $ciphers . '+SHA256') | |
342 or die("Failed to set sigalgs"); | |
343 } | |
344 | |
345 Net::SSLeay::CTX_load_verify_locations($ctx, $ca || '', ''); | |
346 Net::SSLeay::CTX_set_tlsext_status_cb($ctx, $staple_cb); | |
347 my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); | |
348 Net::SSLeay::set_tlsext_status_type($ssl, | |
349 Net::SSLeay::TLSEXT_STATUSTYPE_ocsp()); | |
350 Net::SSLeay::set_fd($ssl, fileno($s)); | |
351 Net::SSLeay::connect($ssl) or die("ssl connect"); | |
352 | |
353 return join ' ', @resp; | 340 return join ' ', @resp; |
354 } | 341 } |
355 | 342 |
356 sub get_version { | 343 sub test_tls13 { |
357 my $s; | 344 return http_get('/', start => 1, SSL => 1) =~ /TLSv1.3/; |
358 | |
359 eval { | |
360 local $SIG{ALRM} = sub { die "timeout\n" }; | |
361 local $SIG{PIPE} = sub { die "sigpipe\n" }; | |
362 alarm(8); | |
363 $s = IO::Socket::INET->new('127.0.0.1:' . port(8443)); | |
364 alarm(0); | |
365 }; | |
366 alarm(0); | |
367 | |
368 if ($@) { | |
369 log_in("died: $@"); | |
370 return undef; | |
371 } | |
372 | |
373 my $ctx = Net::SSLeay::CTX_new() or die("Failed to create SSL_CTX $!"); | |
374 my $ssl = Net::SSLeay::new($ctx) or die("Failed to create SSL $!"); | |
375 Net::SSLeay::set_fd($ssl, fileno($s)); | |
376 Net::SSLeay::connect($ssl) or die("ssl connect"); | |
377 | |
378 Net::SSLeay::version($ssl); | |
379 } | 345 } |
380 | 346 |
381 ############################################################################### | 347 ############################################################################### |
382 | 348 |
383 sub http_daemon { | 349 sub http_daemon { |