71
71
use warnings;
72
72
use utf8;
73
73
74
- our $SID_main = " @(#) o-saft.pl 3.201 25/03/14 15:52:07 " ; # version of this file
74
+ our $SID_main = " @(#) o-saft.pl 3.203 25/03/19 00:01:38 " ; # version of this file
75
75
my $VERSION = _VERSION(); # # no critic qw(ValuesAndExpressions::RequireConstantVersion)
76
76
# SEE Perl:constant
77
77
# see _VERSION() below for our official version number
@@ -389,7 +389,7 @@ sub _load_file {
389
389
); # %openssl
390
390
391
391
$cfg {' time0' } = $time0 ;
392
- OCfg::set_user_agent(" $cfg {'me'}/3.201 " ); # use version of this file not $VERSION
392
+ OCfg::set_user_agent(" $cfg {'me'}/3.203 " ); # use version of this file not $VERSION
393
393
OCfg::set_user_agent(" $cfg {'me'}/$STR {'MAKEVAL'}" ) if (defined $ENV {' OSAFT_MAKE' });
394
394
# TODO: $STR{'MAKEVAL'} is wrong if not called by internal make targets
395
395
@@ -2816,6 +2816,72 @@ sub _get_target {
2816
2816
return ($prot , $host , $port , $auth , $path );
2817
2817
} # _get_target
2818
2818
2819
+ sub _get_dns {
2820
+ # ? return IP and DNS related data; empty list if target is invalid
2821
+ # gethostbyname() and gethostbyaddr() set $? on error, needs to be reset!
2822
+ my $host = shift ;
2823
+ my $fail = " " ;
2824
+ my $rhost = " " ;
2825
+ my $dns = " " ;
2826
+ my $myIP = " " ;
2827
+ my $myip = " " ;
2828
+ trace(" _get_dns($host ){" );
2829
+ if (" " ne $cfg {' proxyhost' }) {
2830
+ # if a proxy is used, DNS might not work at all, or be done by the
2831
+ # proxy (which even may return other results than the local client)
2832
+ # so we set corresponding values to a warning
2833
+ $fail = _get_text(' disabled' , " --proxyhost=$cfg {'proxyhost'}" );
2834
+ $rhost = $fail ;
2835
+ $dns = $fail ;
2836
+ $myIP = $fail ;
2837
+ $myip = $fail ;
2838
+ } else {
2839
+ $fail = ' <<gethostbyaddr() failed>>' ;
2840
+ $myip = gethostbyname ($host ); # primary IP as identified by given hostname
2841
+ if (not defined $myip ) {
2842
+ OCfg::warn (" 201: Can't get IP for host '$host '; host ignored" );
2843
+ trace(" host}" );
2844
+ return undef ; # failed, must be handled by caller
2845
+ }
2846
+ # gethostbyaddr() is strange: returns $?==0 but an error message in $!
2847
+ # hence just checking $? is not reliable, we do it additionally.
2848
+ # If gethostbyaddr() fails we use Perl's `or' to assign our default
2849
+ # text. This may happen when there are problems with the local name
2850
+ # resolution.
2851
+ # When gethostbyaddr() fails, the connection to the target most likely
2852
+ # fails also, which produces more Perl warnings later.
2853
+ # Using "C4" (8-bit unsigned char) which should be ok for IPs an works
2854
+ # in ancient Perl too, which does not support "W4".
2855
+ _vprint(" test IP" );
2856
+ $myIP = join (" ." , unpack (" C4" , $myip ));
2857
+ if (_is_cfg_use(' dns' )) { # following settings only with --dns
2858
+ trace(" test DNS (disable with --no-dns)" );
2859
+ _trace_time(" test DNS{" );
2860
+ local $? = 0; local $! = undef ;
2861
+ ($rhost = gethostbyaddr ($myip , AF_INET)) or $rhost = $fail ;
2862
+ $rhost = $fail if ($? != 0);
2863
+ my ($fqdn , $aliases , $addrtype , $length , @ips ) = gethostbyname ($host );
2864
+ my $i = 0;
2865
+ # dbx printf "@ips = %s\n", join(" - ", @ips);
2866
+ foreach my $ip (@ips ) {
2867
+ local $? = 0; local $! = undef ;
2868
+ # TODO: $rhost = gethostbyaddr($ipv6, AF_INET6));
2869
+ ($rhost = gethostbyaddr ($ip , AF_INET)) or $rhost = $fail ;
2870
+ $rhost = $fail if ($? != 0);
2871
+ $dns .= join (" ." , unpack (" C4" , $myip )) . " " . $rhost . " ; " ;
2872
+ # dbx printf "[%s] = %s\t%s\n", $i, join(".",unpack("C4",$ip)), $rhost;
2873
+ }
2874
+ if ($rhost =~ m / gethostbyaddr/ ) {
2875
+ OCfg::warn (" 202: Can't do DNS reverse lookup: for '$host ': $fail ; ignored" );
2876
+ OCfg::hint(" 202: use '--no-dns' to disable this check" );
2877
+ }
2878
+ _trace_time(" test DNS}" );
2879
+ }
2880
+ }
2881
+ trace(" _get_dns()\t = [rhost=$rhost , dns=$dns , IP=$myIP , ..] }" );
2882
+ return ($rhost , $dns , $myIP , $myip );
2883
+ } # _get_dns
2884
+
2819
2885
sub _get_data0 {
2820
2886
# ? get %data for connection without SNI
2821
2887
# this function currently only returns data for: cn_nosni, session_ticket
@@ -3127,7 +3193,7 @@ sub ciphers_scan_openssl {
3127
3193
my $results = {}; # hash of cipher list to be returned
3128
3194
foreach my $ssl (@{$cfg {' version' }}) {
3129
3195
my $usesni = $cfg {' use' }-> {' sni' };
3130
- _vprint(" test $cnt ciphers for $ssl ... ($cfg {'ciphermode'}) " );
3196
+ _vprint(" test $cnt ciphers for $ssl ... ($cfg {'ciphermode'}) " );
3131
3197
trace( " test $cnt ciphers for $ssl ... ($cfg {'ciphermode'}) " );
3132
3198
trace( " using cipherpattern=[ @{$cfg {'cipher'}} ], cipherrange=$cfg {'cipherrange'}" );
3133
3199
if ($ssl =~ m / ^SSLv[23] / ) {
@@ -3210,7 +3276,7 @@ sub ciphers_scan_intern {
3210
3276
my $accepted_cnt = 0;
3211
3277
my @all = _get_cipherslist(' keys' , $ssl );
3212
3278
$total += scalar (@all );
3213
- _vprint(" test " . scalar (@all ) . " ciphers for $ssl ... (SSLhello)" );
3279
+ _vprint(" test " . scalar (@all ) . " ciphers for $ssl ... (SSLhello)" );
3214
3280
trace( " test " . scalar (@all ) . " ciphers for $ssl ... (SSLhello)" );
3215
3281
trace( " using cipherpattern=[ @{$cfg {'cipher'}} ], cipherrange=$cfg {'cipherrange'}" );
3216
3282
if (" @all " =~ / ^\s *$ / ) {
@@ -3268,6 +3334,50 @@ sub ciphers_scan_intern {
3268
3334
return $results ;
3269
3335
} # ciphers_scan_intern
3270
3336
3337
+ sub ciphers_scan {
3338
+ # ? scan target for ciphers for all protocols; modifies %checks, %prot
3339
+ # wrapper for ciphers_scan_intern() and ciphers_scan_openssl()
3340
+ # returns array with accepted ciphers
3341
+ my ($host , $port ) = @_ ;
3342
+ trace(" ciphers_scan($host , $port ) {" );
3343
+ my $results = {}; # array of arrays
3344
+ OCfg::warn (" 209: No SSL versions for '+cipher' available" ) if ($# {$cfg {' version' }} < 0);
3345
+ # above warning is most likely a programming error herein
3346
+ if (' openssl' eq $cfg {' cipherrange' }) {
3347
+ # get ciphers from openssl for any --ciphermode=
3348
+ # TODO: see CIPHER_RANGE also
3349
+ require SSLinfo; # FIXME: dirty hack until we have lib/SSLtool.pm
3350
+ $SSLinfo::openssl = $openssl {' exe' };
3351
+ @{$cfg {' cipher' }} = map ({Ciphers::get_key($_ )||" " ;} SSLinfo::cipher_openssl(" @{$cfg {'cipher'}}" ));
3352
+ trace(" openssl ciphers: " . scalar @{$cfg {' cipher' }});
3353
+ }
3354
+ _vprint(" test protocols @{$cfg {'version'}} ..." );
3355
+ if (_is_cfg_ciphermode(' intern|dump' )) {
3356
+ trace(" use SSLhello ..." );
3357
+ SSLhello::printParameters() if ($cfg {' trace' } > 1);
3358
+ $results = ciphers_scan_intern($host , $port );
3359
+ }
3360
+ if (_is_cfg_ciphermode(' openssl|socket' )) {
3361
+ trace(" use $cfg {'ciphermode'} ..." );
3362
+ # FIXME: on tiny systems following may cause "Out of memory!"
3363
+ $results = ciphers_scan_openssl($host , $port ); # uses @{$cfg{'ciphers'}}
3364
+ # TODO: $prot{$ssl}->{'default'} = $cipher;
3365
+ # SEE Note:+cipher-selected
3366
+ trace(" get default ..." );
3367
+ _trace_time(" need_default{" );
3368
+ ciphers_default_openssl($host , $port );
3369
+ _trace_time(" need_default}" );
3370
+ }
3371
+ foreach my $ssl (@{$cfg {' version' }}) { # all requested protocol versions
3372
+ $checks {' cnt_ciphers' }-> {val } += $results -> {' _admin' }{$ssl }{' cnt_offered' };
3373
+ $checks {' cnt_totals' } -> {val } += $results -> {' _admin' }{$ssl }{' cnt_accepted' };
3374
+ }
3375
+ # dbx# print Dumper(\$results);
3376
+ checkciphers($host , $port , $results );
3377
+ trace(" ciphers_scan() }" ); # no trace($results) as already done by ciphers_scan_*()
3378
+ return $results ;
3379
+ } # ciphers_scan
3380
+
3271
3381
# _____________________________________________________________________________
3272
3382
# __________________________________________________________ check functions __|
3273
3383
@@ -5410,7 +5520,7 @@ sub print_footer {
5410
5520
} # print_footer
5411
5521
5412
5522
sub print_title {
5413
- # ? print title according given legacy format
5523
+ # ? print title according given legacy format; uses $cfg{'IP'}
5414
5524
my ($legacy , $ssl , $host , $port , $header ) = @_ ;
5415
5525
if ($legacy eq ' sslyze' ) {
5416
5526
my $txt = " SCAN RESULTS FOR " . $host . " - " . $cfg {' IP' };
@@ -6129,6 +6239,24 @@ sub printchecks {
6129
6239
return ;
6130
6240
} # printchecks
6131
6241
6242
+ sub printdns {
6243
+ # ? print DNS-related information for target
6244
+ my ($legacy , $host , $port ) = @_ ;
6245
+ _vprint(" print DNS stuff" );
6246
+ trace(" +info || +check || +sni*" );
6247
+ if ($legacy =~ / (compact|full|owasp|simple)/ ) {
6248
+ print_ruler();
6249
+ print_line($legacy , $host , $port , ' host_name' , $text {' host_name' }, $host );
6250
+ print_line($legacy , $host , $port , ' host_IP' , $text {' host_IP' }, $cfg {' IP' });
6251
+ if (_is_cfg_use(' dns' )) {
6252
+ print_line($legacy , $host , $port , ' host_rhost' , $text {' host_rhost' }, $cfg {' rhost' });
6253
+ print_line($legacy , $host , $port , ' host_DNS' , $text {' host_DNS' }, $cfg {' DNS' });
6254
+ }
6255
+ print_ruler();
6256
+ }
6257
+ return ;
6258
+ } # printdns
6259
+
6132
6260
# | definitions: print functions for help and information
6133
6261
# | -------------------------------------
6134
6262
@@ -6205,7 +6333,7 @@ sub printversion {
6205
6333
my $me = $cfg {' me' };
6206
6334
print ( " = $0 " . _VERSION() . " =" );
6207
6335
if (not _is_cfg_verbose()) {
6208
- printf (" %-21s%s \n " , $me , " 3.201 " );# just version to keep make targets happy
6336
+ printf (" %-21s%s \n " , $me , " 3.203 " );# just version to keep make targets happy
6209
6337
} else {
6210
6338
printf (" %-21s%s \n " , $me , $SID_main ); # own unique SID
6211
6339
# print internal SID of our own modules
@@ -7519,7 +7647,7 @@ sub printusage_exit {
7519
7647
OMan::man_printhelp($help );
7520
7648
exit 0;
7521
7649
}
7522
- if (0 == scalar (@{$cfg {' do' }}) and $cfg {' opt-V' }) { print " 3.201 " ; exit 0; }
7650
+ if (0 == scalar (@{$cfg {' do' }}) and $cfg {' opt-V' }) { print " 3.203 " ; exit 0; }
7523
7651
# NOTE: printciphers_list() is a wrapper for Ciphers::show() regarding more options
7524
7652
if (_is_cfg_do(' list' )) { _vprint(" list " ); printciphers_list(' list' ); exit 0; }
7525
7653
if (_is_cfg_do(' ciphers' )) { _vprint(" ciphers " ); printciphers_list(' ciphers' ); exit 0; }
@@ -7992,82 +8120,16 @@ sub printusage_exit {
7992
8120
$SSLinfo::target_url =~ s : ^\s *$: /: ; # set to / if empty
7993
8121
_resetchecks();
7994
8122
print_header(_get_text(' out_target' , " $host :$port " ), " " , " " , $cfg {' out' }-> {' header' });
7995
- next if _trace_next(" DNS0 - start" );
7996
8123
7997
- # gethostbyname() and gethostbyaddr() set $? on error, needs to be reset!
7998
- my $rhost = " " ;
7999
- $fail = " " ; # reusing variable
8000
- if (" " ne $cfg {' proxyhost' }) {
8001
- # if a proxy is used, DNS might not work at all, or be done by the
8002
- # proxy (which even may return other results than the local client)
8003
- # so we set corresponding values to a warning
8004
- $fail = _get_text(' disabled' , " --proxyhost=$cfg {'proxyhost'}" );
8005
- $cfg {' rhost' } = $fail ;
8006
- $cfg {' DNS' } = $fail ;
8007
- $cfg {' IP' } = $fail ;
8008
- $cfg {' ip' } = $fail ;
8009
- } else {
8010
- $fail = ' <<gethostbyaddr() failed>>' ;
8011
- $cfg {' ip' } = gethostbyname ($host ); # primary IP as identified by given hostname
8012
- if (not defined $cfg {' ip' }) {
8013
- OCfg::warn (" 201: Can't get IP for host '$host '; host ignored" );
8014
- trace(" host}" );
8015
- next ; # otherwise all following fails
8016
- }
8017
- # gethostbyaddr() is strange: returns $?==0 but an error message in $!
8018
- # hence just checking $? is not reliable, we do it additionally.
8019
- # If gethostbyaddr() fails we use Perl's `or' to assign our default
8020
- # text. This may happen when there are problems with the local name
8021
- # resolution.
8022
- # When gethostbyaddr() fails, the connection to the target most likely
8023
- # fails also, which produces more Perl warnings later.
8024
- # Using "C4" (8-bit unsigned char) which should be ok for IPs an works
8025
- # in ancient Perl too, which does not support "W4".
8026
- _vprint(" test IP" );
8027
- $cfg {' IP' } = join (" ." , unpack (" C4" , $cfg {' ip' }));
8028
- if (_is_cfg_use(' dns' )) { # following settings only with --dns
8029
- trace(" test DNS (disable with --no-dns)" );
8030
- _trace_time(" test DNS{" );
8031
- local $? = 0; local $! = undef ;
8032
- ($cfg {' rhost' } = gethostbyaddr ($cfg {' ip' }, AF_INET)) or $cfg {' rhost' } = $fail ;
8033
- $cfg {' rhost' } = $fail if ($? != 0);
8034
- my ($fqdn , $aliases , $addrtype , $length , @ips ) = gethostbyname ($host );
8035
- my $i = 0;
8036
- # dbx printf "@ips = %s\n", join(" - ", @ips);
8037
- foreach my $ip (@ips ) {
8038
- local $? = 0; local $! = undef ;
8039
- # TODO: $rhost = gethostbyaddr($ipv6, AF_INET6));
8040
- ($rhost = gethostbyaddr ($ip , AF_INET)) or $rhost = $fail ;
8041
- $rhost = $fail if ($? != 0);
8042
- $cfg {' DNS' } .= join (" ." , unpack (" C4" , $cfg {' ip' })) . " " . $rhost . " ; " ;
8043
- # dbx printf "[%s] = %s\t%s\n", $i, join(".",unpack("C4",$ip)), $rhost;
8044
- }
8045
- if ($cfg {' rhost' } =~ m / gethostbyaddr/ ) {
8046
- OCfg::warn (" 202: Can't do DNS reverse lookup: for '$host ': $fail ; ignored" );
8047
- OCfg::hint(" 202: use '--no-dns' to disable this check" );
8048
- }
8049
- _trace_time(" test DNS}" );
8050
- }
8051
- }
8124
+ next if _trace_next(" DNS0 - start" );
8125
+ ($cfg {' rhost' }, $cfg {' DNS' }, $cfg {' IP' }, $cfg {' ip' }) = _get_dns($host );
8126
+ next if not defined $cfg {' rhost' }; # otherwise all following fails
8052
8127
if (_is_cfg_do(' host' ) or (($info + $check + $cmdsni ) > 0)) {
8053
- _vprint(" print DNS stuff" );
8054
- trace(" +info || +check || +sni*" );
8055
- if ($legacy =~ / (compact|full|owasp|simple)/ ) {
8056
- print_ruler();
8057
- print_line($legacy , $host , $port , ' host_name' , $text {' host_name' }, $host );
8058
- print_line($legacy , $host , $port , ' host_IP' , $text {' host_IP' }, $cfg {' IP' });
8059
- if (_is_cfg_use(' dns' )) {
8060
- print_line($legacy , $host , $port , ' host_rhost' , $text {' host_rhost' }, $cfg {' rhost' });
8061
- print_line($legacy , $host , $port , ' host_DNS' , $text {' host_DNS' }, $cfg {' DNS' });
8062
- }
8063
- print_ruler();
8064
- }
8128
+ printdns($legacy , $host , $port );
8065
8129
}
8066
-
8067
8130
next if _trace_next(" DNS9 - end" );
8068
8131
8069
8132
if (_is_cfg_do(' cipher_dh' )) {
8070
- # abort here is ok because +cipher-dh cannot be combined with other commands
8071
8133
if (not _is_cfg_ciphermode(' intern' )) {
8072
8134
OCfg::warn (" 405: option '--ciphermode=', not supported for '+cipher-dh'; reset to --ciphermode=intern" );
8073
8135
$cfg {' ciphermode' } = " intern" ;
@@ -8080,42 +8142,9 @@ sub printusage_exit {
8080
8142
8081
8143
next if _trace_next(" CIPHER0 - start (ciphermode=$cfg {'ciphermode'})" );
8082
8144
if (_need_cipher()) {
8083
- OCfg::warn (" 209: No SSL versions for '+cipher' available" ) if ($# {$cfg {' version' }} < 0);
8084
- # above warning is most likely a programming error herein
8085
- if (' openssl' eq $cfg {' cipherrange' }) {
8086
- # get ciphers from openssl for any --ciphermode=
8087
- # TODO: see CIPHER_RANGE also
8088
- require SSLinfo; # FIXME: dirty hack until we have lib/SSLtool.pm
8089
- $SSLinfo::openssl = $openssl {' exe' };
8090
- @{$cfg {' cipher' }} = map ({Ciphers::get_key($_ )||" " ;} SSLinfo::cipher_openssl(" @{$cfg {'cipher'}}" ));
8091
- trace(" openssl ciphers: " . scalar @{$cfg {' cipher' }});
8092
- }
8093
- $cipher_results = {}; # new list for every host (array of arrays)
8094
- _vprint(" test protocols @{$cfg {'version'}} ..." );
8095
- if (_is_cfg_ciphermode(' intern|dump' )) {
8096
- trace(" use SSLhello ..." );
8097
- SSLhello::printParameters() if ($cfg {' trace' } > 1);
8098
- $cipher_results = ciphers_scan_intern($host , $port );
8099
- }
8100
- if (_is_cfg_ciphermode(' openssl|socket' )) {
8101
- trace(" use $cfg {'ciphermode'} ..." );
8102
- # FIXME: on tiny systems following may cause "Out of memory!"
8103
- $cipher_results = ciphers_scan_openssl($host , $port ); # uses @{$cfg{'ciphers'}}
8104
- # TODO: $prot{$ssl}->{'default'} = $cipher;
8105
- # SEE Note:+cipher-selected
8106
- trace(" get default ..." );
8107
- _trace_time(" need_default{" );
8108
- ciphers_default_openssl($host , $port );
8109
- _trace_time(" need_default}" );
8110
- }
8111
- foreach my $ssl (@{$cfg {' version' }}) { # all requested protocol versions
8112
- $checks {' cnt_ciphers' }-> {val } += $cipher_results -> {' _admin' }{$ssl }{' cnt_offered' };
8113
- $checks {' cnt_totals' } -> {val } += $cipher_results -> {' _admin' }{$ssl }{' cnt_accepted' };
8114
- }
8115
- # dbx# print Dumper(\$cipher_results);
8116
- checkciphers($host , $port , $cipher_results );
8117
- } # need_cipher
8118
- next if _trace_next(" SCAN - done" );
8145
+ $cipher_results = ciphers_scan($host , $port );
8146
+ }
8147
+ next if _trace_next(" SCAN - done" ); # dirty logic outside if-condition to ensure it's always checked
8119
8148
8120
8149
if (_is_cfg_do(' cipher_dh' )) {
8121
8150
_vprint(" +cipher-dh" );
@@ -8140,11 +8169,13 @@ sub printusage_exit {
8140
8169
# in printprotocols() anyway
8141
8170
printcipherpreferred($legacy , $host , $port );
8142
8171
}
8172
+ next if _trace_next(" CIPHER9 - end" );
8143
8173
goto CLOSE_SSL if (_is_cfg_do(' cipher' ) and (0 == $quick )); # next HOSTS
8144
8174
} # need_cipher
8145
- next if _trace_next(" CIPHER9 - end" );
8175
+ next if _trace_next(" CIPHER9 - end" ); # dirty logic outside if-condition to ensure it's always checked
8146
8176
8147
- # Quick check if the target is available; any command except +cipher*
8177
+ # from here on all other commands than +cipher*
8178
+ # Quick check if the target is available
8148
8179
next if _trace_next(" CONN0 - start" ); # SEE Note:Connection Test
8149
8180
my $connect_ssl = 1;
8150
8181
trace(" sni_name= " . ($cfg {' sni_name' } || $STR {UNDEF }));
0 commit comments