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 {