File Coverage

inc/Test/Net/SSLeay.pm
Criterion Covered Total %
statement 156 207 75.3
branch 44 92 47.8
condition 2 30 6.6
subroutine 36 40 90.0
pod 16 16 100.0
total 254 385 65.9


line stmt bran cond sub pod time code
1             package Test::Net::SSLeay;
2              
3 59     59   35159 use 5.008001;
  59         208  
4 59     56   326 use strict;
  56         89  
  56         1182  
5 56     56   250 use warnings;
  56         87  
  56         1660  
6 56     56   245 use base qw(Exporter);
  56         114  
  56         7835  
7              
8 56     56   356 use Carp qw(croak);
  56         110  
  56         2976  
9 56     56   336 use Config;
  56         84  
  56         2772  
10 56     56   931 use Cwd qw(abs_path);
  56         104  
  56         3399  
11 56     56   26299 use English qw( $EVAL_ERROR $OSNAME $PERL_VERSION -no_match_vars );
  56         101409  
  56         421  
12 56     56   11690 use File::Basename qw(dirname);
  56         107  
  56         5795  
13 56     56   24802 use File::Spec::Functions qw( abs2rel catfile );
  56         49102  
  56         3548  
14 56     56   37856 use Test::Builder;
  56         3207410  
  56         1950  
15 56     56   26171 use Test::Net::SSLeay::Socket;
  56         170  
  56         152994  
16              
17             our $VERSION = '1.93_02';
18              
19             our @EXPORT_OK = qw(
20             can_fork can_really_fork can_thread
21             data_file_path
22             dies_like
23             dies_ok
24             doesnt_warn
25             initialise_libssl
26             is_libressl is_openssl
27             is_protocol_usable
28             lives_ok
29             new_ctx
30             protocols
31             tcp_socket
32             warns_like
33             );
34              
35             my $tester = Test::Builder->new();
36              
37             my $data_path = catfile( dirname(__FILE__), '..', '..', '..', 't', 'data' );
38              
39             my $initialised = 0;
40              
41             my %protos = (
42             'TLSv1.3' => {
43             constant => \&Net::SSLeay::TLS1_3_VERSION,
44             constant_type => 'version',
45             priority => 6,
46             },
47             'TLSv1.2' => {
48             constant => \&Net::SSLeay::TLSv1_2_method,
49             constant_type => 'method',
50             priority => 5,
51             },
52             'TLSv1.1' => {
53             constant => \&Net::SSLeay::TLSv1_1_method,
54             constant_type => 'method',
55             priority => 4,
56             },
57             'TLSv1' => {
58             constant => \&Net::SSLeay::TLSv1_method,
59             constant_type => 'method',
60             priority => 3,
61             },
62             'SSLv3' => {
63             constant => \&Net::SSLeay::SSLv3_method,
64             constant_type => 'method',
65             priority => 2,
66             },
67             'SSLv2' => {
68             constant => \&Net::SSLeay::SSLv2_method,
69             constant_type => 'method',
70             priority => 1,
71             },
72             );
73              
74             my ( $test_no_warnings, $test_no_warnings_name, @warnings );
75              
76             END {
77 56 100   56   5820709 _test_no_warnings() if $test_no_warnings;
78             }
79              
80             sub _all {
81 2     2   5 my ( $sub, @list ) = @_;
82              
83 2         4 for (@list) {
84 2 50       3 $sub->() or return 0;
85             }
86              
87 2         7 return 1;
88             }
89              
90             sub _diag {
91 0     0   0 my (%args) = @_;
92              
93 0         0 $tester->diag( ' ' x 9, 'got: ', $args{got} );
94 0         0 $tester->diag( ' ' x 4, 'expected: ', $args{expected} );
95             }
96              
97             sub _libssl_fatal {
98 0     0   0 my ($context) = @_;
99              
100 0         0 croak "$context: "
101             . Net::SSLeay::ERR_error_string( Net::SSLeay::ERR_get_error() );
102             }
103              
104             sub _load_net_ssleay {
105 335 50   335   594 eval { require Net::SSLeay; 1; } or croak $EVAL_ERROR;
  335         3005  
  335         1043  
106              
107 335         496 return 1;
108             }
109              
110             sub _test_no_warnings {
111 1     1   5 my $got_str = join q{, }, map { qq{'$_'} } @warnings;
  0         0  
112 1 50       6 my $got_type = @warnings == 1 ? 'warning' : 'warnings';
113              
114 1 50       7 $tester->ok( @warnings == 0, $test_no_warnings_name )
115             or _diag(
116             got => "$got_type $got_str",
117             expected => 'no warnings',
118             );
119             }
120              
121             sub import {
122 59     59   3007 my ( $class, @imports ) = @_;
123              
124             # Enable strict and warnings in the caller
125 59         375 strict->import;
126 59         538 warnings->import;
127              
128             # Import common modules into the caller's namespace
129 59         135 my $caller = caller;
130 59         142 for (qw(Test::More)) {
131 56 50   56   35827 eval "package $caller; use $_; 1;" or croak $EVAL_ERROR;
  56         345105  
  56         432  
  59         4160  
132             }
133              
134             # Import requested Test::Net::SSLeay symbols into the caller's namespace
135 59         8090 __PACKAGE__->export_to_level( 1, $class, @imports );
136              
137 59         3044925 return 1;
138             }
139              
140             sub can_fork {
141 23 50   23 1 1628 return 1 if can_really_fork();
142              
143             # Some platforms provide fork emulation using ithreads
144 0 0       0 return 1 if $Config{d_pseudofork};
145              
146             # d_pseudofork was added in Perl 5.10.0 - this is an approximation for
147             # older Perls
148 0 0 0     0 if ( ( $OSNAME eq 'Win32' or $OSNAME eq 'NetWare' )
      0        
      0        
149             and $Config{useithreads}
150             and $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/ )
151             {
152 0         0 return 1;
153             }
154              
155 0         0 return can_thread();
156             }
157              
158             sub can_really_fork {
159 25 50   25 1 2128 return 1 if $Config{d_fork};
160              
161 0         0 return 0;
162             }
163              
164             sub can_thread {
165 2 50   2 1 199 return 0 if not $Config{useithreads};
166              
167             # Threads are broken in Perl 5.10.0 when compiled with GCC 4.8 or above
168             # (see GH #175)
169 0 0 0     0 if ( $PERL_VERSION == 5.010000
      0        
      0        
170             and $Config{ccname} eq 'gcc'
171             and defined $Config{gccversion}
172             # gccversion is sometimes defined for non-GCC compilers (see GH-350);
173             # compilers that are truly GCC are identified with a version number in
174             # gccversion
175             and $Config{gccversion} =~ /^\d+\.\d+/ )
176             {
177 0         0 my ( $gcc_major, $gcc_minor ) = split /[.\s]+/, $Config{gccversion};
178              
179 0 0 0     0 return 0
      0        
180             if ( $gcc_major > 4 or ( $gcc_major == 4 and $gcc_minor >= 8 ) );
181             }
182              
183             # Devel::Cover doesn't (currently) work with threads
184 0 0       0 return 0 if $INC{'Devel/Cover.pm'};
185              
186 0         0 return 1;
187             }
188              
189             sub data_file_path {
190 90     90 1 91608 my ($data_file) = @_;
191              
192 90         5073 my $abs_path = catfile( abs_path($data_path), $data_file );
193 90         605 my $rel_path = abs2rel($abs_path);
194              
195 90 50       10328 croak "$rel_path: data file does not exist"
196             if not -e $abs_path;
197              
198 90         3608 return $rel_path;
199             }
200              
201             sub dies_like {
202 677     677 1 12625 my ( $sub, $expected, $name ) = @_;
203              
204 677         789 my ( $got, $ok );
205              
206 677 50       803 if ( eval { $sub->(); 1 } ) {
  677         1122  
  0         0  
207 0         0 $ok = $tester->ok ( 0, $name );
208              
209 0         0 _diag(
210             got => 'subroutine lived',
211             expected => "subroutine died with exception matching $expected",
212             );
213             }
214             else {
215 677         5363 $got = $EVAL_ERROR;
216              
217 677         3299 my $test = $got =~ $expected;
218              
219 677 50       1967 $ok = $tester->ok( $test, $name )
220             or _diag(
221             got => qq{subroutine died with exception '$got'},
222             expected => "subroutine died with exception matching $expected",
223             );
224             }
225              
226 677         185291 $EVAL_ERROR = $got;
227              
228 677         1710 return $ok;
229             }
230              
231             sub dies_ok {
232 0     0 1 0 my ( $sub, $name ) = @_;
233              
234 0         0 my ( $got, $ok );
235              
236 0 0       0 if ( eval { $sub->(); 1 } ) {
  0         0  
  0         0  
237 0         0 $got = $EVAL_ERROR;
238              
239 0         0 $ok = $tester->ok ( 0, $name );
240              
241 0         0 _diag(
242             got => 'subroutine lived',
243             expected => 'subroutine died',
244             );
245             }
246             else {
247 0         0 $got = $EVAL_ERROR;
248              
249 0         0 $ok = $tester->ok( 1, $name );
250             }
251              
252 0         0 $EVAL_ERROR = $got;
253              
254 0         0 return $ok;
255             }
256              
257             sub doesnt_warn {
258 1     1 1 821 $test_no_warnings = 1;
259 1         2 $test_no_warnings_name = shift;
260              
261 1     0   7 $SIG{__WARN__} = sub { push @warnings, shift };
  0         0  
262             }
263              
264             sub initialise_libssl {
265 305 100   305 1 32617 return 1 if $initialised;
266              
267 42         152 _load_net_ssleay();
268              
269 42         408 Net::SSLeay::randomize();
270              
271             # Error strings aren't loaded by default until OpenSSL 1.1.0, but it's safe
272             # to load them unconditionally because these functions are simply no-ops in
273             # later OpenSSL versions
274 42         75960 Net::SSLeay::load_error_strings();
275 42         1387 Net::SSLeay::ERR_load_crypto_strings();
276              
277 42         4137 Net::SSLeay::library_init();
278              
279             # The test suite makes heavy use of SHA-256, but SHA-256 isn't registered by
280             # default in all OpenSSL versions - register it manually when Net::SSLeay is
281             # built against the following OpenSSL versions:
282              
283             # OpenSSL 0.9.8 series < 0.9.8o
284 42 50       894 Net::SSLeay::OpenSSL_add_all_digests()
285             if Net::SSLeay::constant('OPENSSL_VERSION_NUMBER') < 0x009080ff;
286              
287             # OpenSSL 1.0.0 series < 1.0.0a
288 42 50 33     579 Net::SSLeay::OpenSSL_add_all_digests()
289             if Net::SSLeay::constant('OPENSSL_VERSION_NUMBER') >= 0x10000000
290             && Net::SSLeay::constant('OPENSSL_VERSION_NUMBER') < 0x1000001f;
291              
292 42         124 $initialised = 1;
293              
294 42         165 return 1;
295             }
296              
297             sub is_libressl {
298 5     5 1 2374 _load_net_ssleay();
299              
300             # The most foolproof method of checking whether libssl is provided by
301             # LibreSSL is by checking OPENSSL_VERSION_NUMBER: every version of
302             # LibreSSL identifies itself as OpenSSL 2.0.0, which is a version number
303             # that OpenSSL itself will never use (version 3.0.0 follows 1.1.1)
304 5 50       116 return 0
305             if Net::SSLeay::constant('OPENSSL_VERSION_NUMBER') != 0x20000000;
306              
307 0         0 return 1;
308             }
309              
310             sub is_openssl {
311 25     25 1 328236 _load_net_ssleay();
312              
313             # "OpenSSL 2.0.0" is actually LibreSSL
314 25 50       173 return 0
315             if Net::SSLeay::constant('OPENSSL_VERSION_NUMBER') == 0x20000000;
316              
317 25         47 return 1;
318             }
319              
320             sub is_protocol_usable {
321 263     263 1 700 my ($proto) = @_;
322              
323 263         838 _load_net_ssleay();
324 263         625 initialise_libssl();
325              
326 263         797 my $proto_data = $protos{$proto};
327              
328             # If libssl does not support this protocol version, or if it was disabled at
329             # compile-time, the appropriate method for that version will be missing
330 263 100       738 if (
    100          
331             $proto_data->{constant_type} eq 'version'
332 40         96 ? !eval { &{ $proto_data->{constant} }; 1 }
  40         613  
  0         0  
333 223         720 : !defined &{ $proto_data->{constant} }
334             ) {
335 116         535 return 0;
336             }
337              
338             # If libssl was built with support for this protocol version, the only
339             # reliable way to test whether its use is permitted by the security policy
340             # is to attempt to create a connection that uses it - if it is permitted,
341             # the state machine enters the following states:
342             #
343             # SSL_CB_HANDSHAKE_START (ret=1)
344             # SSL_CB_CONNECT_LOOP (ret=1)
345             # SSL_CB_CONNECT_EXIT (ret=-1)
346             #
347             # If it is not permitted, the state machine instead enters the following
348             # states:
349             #
350             # SSL_CB_HANDSHAKE_START (ret=1)
351             # SSL_CB_CONNECT_EXIT (ret=-1)
352             #
353             # Additionally, ERR_get_error() returns the error code 0x14161044, although
354             # this might not necessarily be guaranteed for all libssl versions, so
355             # testing for it may be unreliable
356              
357 147         1496 my $constant = $proto_data->{constant}->();
358 147         252 my $ctx;
359              
360 147 50       303 if ( $proto_data->{constant_type} eq 'version' ) {
361 0 0       0 $ctx = Net::SSLeay::CTX_new_with_method( Net::SSLeay::TLS_method() )
362             or _libssl_fatal('Failed to create libssl SSL_CTX object');
363              
364 0         0 Net::SSLeay::CTX_set_min_proto_version( $ctx, $constant );
365 0         0 Net::SSLeay::CTX_set_max_proto_version( $ctx, $constant );
366             }
367             else {
368 147 50       14664 $ctx = Net::SSLeay::CTX_new_with_method($constant)
369             or _libssl_fatal('Failed to create SSL_CTX object');
370             }
371              
372 147 50       1567 my $ssl = Net::SSLeay::new($ctx)
373             or _libssl_fatal('Failed to create SSL structure');
374              
375             # For the purposes of this test, it isn't necessary to link the SSL
376             # structure to a file descriptor, since no data actually needs to be sent or
377             # received
378 147 50       746 Net::SSLeay::set_fd( $ssl, -1 )
379             or _libssl_fatal('Failed to set file descriptor for SSL structure');
380              
381 147         258 my @states;
382              
383             Net::SSLeay::CTX_set_info_callback(
384             $ctx,
385             sub {
386 441     441   887 my ( $ssl, $where, $ret, $data ) = @_;
387              
388 441         7568 push @states, $where;
389             }
390 147         2709 );
391              
392 147 50       1682 Net::SSLeay::connect($ssl)
393             or _libssl_fatal('Failed to initiate connection');
394              
395 147         4924 my $disabled = Net::SSLeay::CB_HANDSHAKE_START()
396             + Net::SSLeay::CB_CONNECT_EXIT();
397              
398 147         2587 my $enabled = Net::SSLeay::CB_HANDSHAKE_START()
399             + Net::SSLeay::CB_CONNECT_LOOP()
400             + Net::SSLeay::CB_CONNECT_EXIT();
401              
402 147         2581 Net::SSLeay::free($ssl);
403 147         1602 Net::SSLeay::CTX_free($ctx);
404              
405 147         245 my $observed = 0;
406 147         370 for my $state (@states) {
407 441         545 $observed += $state;
408             }
409              
410 147 50       374 return 0 if $observed == $disabled;
411 147 50       599 return 1 if $observed == $enabled;
412              
413 0         0 croak 'Unexpected TLS state machine sequence: ' . join( ', ', @states );
414             }
415              
416             sub lives_ok {
417 10     10 1 1662 my ( $sub, $name ) = @_;
418              
419 10         791 my ( $got, $ok );
420              
421 10 50       19 if ( !eval { $sub->(); 1 } ) {
  10         48  
  10         585042  
422 0         0 $got = $EVAL_ERROR;
423              
424 0         0 $ok = $tester->ok ( 0, $name );
425              
426 0         0 _diag(
427             got => qq{subroutine died with exception '$got'},
428             expected => 'subroutine lived',
429             );
430             }
431             else {
432 10         59 $got = $EVAL_ERROR;
433              
434 10         60 $ok = $tester->ok( 1, $name );
435             }
436              
437 10         3518 $EVAL_ERROR = $got;
438              
439 10         29 return $ok;
440             }
441              
442             sub new_ctx {
443 53     53 1 13748 my ( $min_proto, $max_proto ) = @_;
444              
445             my @usable_protos =
446             # Exclude protocol versions not supported by this libssl:
447             grep {
448 241         717 is_protocol_usable($_)
449             }
450             # Exclude protocol versions outside the desired range:
451             grep {
452 53         428 (
453             defined $min_proto
454             ? $protos{$_}->{priority} >= $protos{$min_proto}->{priority}
455             : 1
456             )
457             && (
458             defined $max_proto
459             ? $protos{$_}->{priority} <= $protos{$max_proto}->{priority}
460 318 100       1721 : 1
    100          
    100          
461             )
462             }
463             protocols();
464              
465 53 50       249 croak 'Failed to create libssl SSL_CTX object: no usable protocol versions'
466             if !@usable_protos;
467              
468 53         112 my $proto = shift @usable_protos;
469 53         228 my $constant = $protos{$proto}->{constant}->();
470 53         102 my $ctx;
471              
472 53 50       190 if ( $protos{$proto}->{constant_type} eq 'version' ) {
473 0 0       0 $ctx = Net::SSLeay::CTX_new_with_method( Net::SSLeay::TLS_method() )
474             or _libssl_fatal('Failed to create libssl SSL_CTX object');
475              
476 0         0 Net::SSLeay::CTX_set_min_proto_version( $ctx, $constant );
477 0         0 Net::SSLeay::CTX_set_max_proto_version( $ctx, $constant );
478             }
479             else {
480 53 50       3527 $ctx = Net::SSLeay::CTX_new_with_method($constant)
481             or _libssl_fatal('Failed to create SSL_CTX object');
482             }
483              
484 53 100       376 return wantarray ? ( $ctx, $proto )
485             : $ctx;
486             }
487              
488             sub protocols {
489             return
490             sort {
491 53     53 1 1201 $protos{$b}->{priority} <=> $protos{$a}->{priority}
492 531         1238 }
493             keys %protos;
494             }
495              
496             sub tcp_socket {
497 22     22 1 18020 return Test::Net::SSLeay::Socket->new( proto => 'tcp' );
498             }
499              
500             sub warns_like {
501 2     2 1 59 my ( $sub, $expected, $name ) = @_;
502              
503 2 50       7 my @expected = ref $expected eq 'ARRAY'
504             ? @$expected
505             : ($expected);
506              
507 2         3 my @got;
508              
509 2     2   12 local $SIG{__WARN__} = sub { push @got, shift };
  2         15  
510              
511 2         6 $sub->();
512              
513 2         9 $SIG{__WARN__} = 'DEFAULT';
514              
515             my $test = scalar @got == scalar @expected
516 2   33 2   17 && _all( sub { $got[$_] =~ $expected[$_] }, 0 .. $#got );
  2         16  
517              
518             my $ok = $tester->ok( $test, $name )
519 2 50       8 or do {
520 0         0 my $got_str = join q{, }, map { qq{'$_'} } @got;
  0         0  
521 0         0 my $expected_str = join q{, }, map { qq{'$_'} } @expected;
  0         0  
522              
523 0 0       0 my $got_plural = @got == 1 ? '' : 's';
524 0 0       0 my $expected_plural = @expected == 1 ? '' : 's';
525              
526 0         0 _diag(
527             got => "warning$got_plural $got_str",
528             expected => "warning$expected_plural matching $expected_str",
529             );
530             };
531              
532 2         675 return $ok;
533             }
534              
535             1;
536              
537             __END__