File Coverage

blib/lib/Net/Scan/SSH/Server/SupportedAuth.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::Scan::SSH::Server::SupportedAuth;
2              
3 1     1   6 use strict;
  1         3  
  1         43  
4 1     1   6 use warnings;
  1         2  
  1         45  
5 1     1   6 use Carp;
  1         8  
  1         225  
6              
7             our $VERSION = '0.02';
8              
9 1     1   429 use Net::SSH::Perl::Kex;
  0            
  0            
10             use Net::SSH::Perl::Auth;
11             our %AUTH_IF;
12             while (my ($a, $b) = each %Net::SSH::Perl::Auth::AUTH) {
13             $AUTH_IF{ lc($a) } = 1<<$b;
14             }
15             $AUTH_IF{publickey} = $AUTH_IF{rsa}; # alias
16             our @EXPORT_OK = qw(%AUTH_IF);
17             our %EXPORT_TAGS = ( flag => [qw(%AUTH_IF)] );
18              
19             BEGIN {
20             my $debug_flag = $ENV{SMART_COMMENTS} || $ENV{SMART_COMMENT} || $ENV{SMART_DEBUG} || $ENV{SC};
21             if ($debug_flag) {
22             my @p = map { '#'x$_ } ($debug_flag =~ /([345])\s*/g);
23             use UNIVERSAL::require;
24             Smart::Comments->use(@p);
25             }
26             }
27              
28             sub new {
29             my($class, %opt) = @_;
30              
31             my $self = bless {
32             server => {
33             host => '127.0.0.1',
34             port => '22',
35             },
36             _version => 0, # 2.0 or 1.99 or 1.5
37             _result => {
38             1 => 0,
39             2 => 0,
40             },
41             _scanned => 0,
42             }, $class;
43              
44             $self->{server}{$_} = $opt{$_} for grep { $opt{$_} } keys %{$self->{server}};
45             ### host, port: $self->{server}{host}, $self->{server}{port}
46              
47             return $self;
48             }
49              
50             sub scan {
51             my $self = shift;
52              
53             $self->{_scanned} = 1;
54              
55             $self->_sshconnect2();
56             $self->_sshconnect1() if $self->{_version} < 2;
57              
58             ### scan: $self->{_result}
59             return $self->{_result};
60             }
61              
62             sub scan_as_hash {
63             my $self = shift;
64             $self->scan unless $self->{_scanned};
65             ### dump: $self->{_result}
66              
67             my $result;
68             for my $v (2,1) {
69             $result->{$v}{password} = ($self->{_result}{$v} & $AUTH_IF{password}) ? 1 : 0;
70             $result->{$v}{publickey} = ($self->{_result}{$v} & $AUTH_IF{rsa}) ? 1 : 0;
71             }
72             ### scan: $result
73             return $result;
74             }
75              
76             sub _sshconnect2 {
77             my $self = shift;
78              
79             ### ssh2 connect
80             my $ssh;
81             eval {
82             $ssh = Net::SSH::Perl->new(
83             $self->{server}{host},
84             port => $self->{server}{port},
85             protocol => 2,
86             compression => 0,
87             debug => 0,
88             ) or return;
89             };
90             if ($@) {
91             ### ssh2 connect error: $@
92             return;
93             }
94              
95             my $v = $self->_protocol_version( $ssh->server_version_string );
96             ### _version: $v
97             $self->{_version} = $v if $v;
98              
99             return if $self->{_version} < 1.5; # server supports 1 only
100              
101             my @authlist;
102             {
103             # override to get auth list.
104             package Net::SSH::Perl::AuthMgr;
105             no warnings 'redefine', 'once';
106              
107             local *auth_failure = sub {
108             my $amgr = shift;
109             my($packet) = @_;
110             my $authlist = $packet->get_str;
111             $packet->{data}->{offset} -= length($authlist)+4;
112              
113             $amgr->{__authlist} = [ split /,/, $authlist ];
114              
115             $amgr->{_done} = 1;
116             };
117             local *auth_list = sub {
118             my $amgr = shift;
119             $amgr->authenticate;
120             return @{ $amgr->{__authlist} };
121             };
122              
123             my $kex = Net::SSH::Perl::Kex->new($ssh);
124             $kex->exchange;
125             my $amgr = Net::SSH::Perl::AuthMgr->new($ssh);
126             @authlist = $amgr->auth_list;
127             }
128              
129             for my $a (@authlist) {
130             ### authlist: $a
131             if ($a eq 'publickey') {
132             $self->{_result}{2} |= $AUTH_IF{rsa};
133             } elsif ($a eq 'password') {
134             $self->{_result}{2} |= $AUTH_IF{password};
135             }
136             }
137             ### ssh2 result: $self->{_result}
138             }
139              
140             sub _sshconnect1 {
141             my $self = shift;
142              
143             ### ssh1 connect
144             my $ssh;
145             eval {
146             $ssh = Net::SSH::Perl->new(
147             $self->{server}{host},
148             port => $self->{server}{port},
149             protocol => 1,
150             compression => 0,
151             debug => 0,
152             ) or return;
153             };
154             if ($@) {
155             ### ssh1 connect error: $@
156             return;
157             }
158              
159             my $v = $self->_protocol_version( $ssh->server_version_string );
160             ### _version: $v
161             $self->{_version} = $v if $v;
162              
163             my($protocol_flags, $supported_ciphers, $supported_auth);
164             {
165             # copy from Net::SSH::Perl::SSH1#_login
166             use Net::SSH::Perl::Constants qw( :protocol :msg :hosts );
167             my $packet = Net::SSH::Perl::Packet->read_expect($ssh, SSH_SMSG_PUBLIC_KEY);
168             my $check_bytes = $packet->bytes(0, 8, "");
169              
170             my %keys;
171             for my $which (qw( public host )) {
172             $keys{$which} = Net::SSH::Perl::Key::RSA1->new;
173             $keys{$which}{rsa}{bits} = $packet->get_int32;
174             $keys{$which}{rsa}{e} = $packet->get_mp_int;
175             $keys{$which}{rsa}{n} = $packet->get_mp_int;
176             }
177              
178             $protocol_flags = $packet->get_int32;
179             $supported_ciphers = $packet->get_int32;
180             $supported_auth = $packet->get_int32;
181             }
182              
183             $self->{_result}{1} = $supported_auth;
184             }
185              
186             sub _protocol_version {
187             my $self = shift;
188             ### _protocol_version: $_[0]
189             return $_[0] =~ /^SSH-([\d.]+)/ ? $1 : 0;
190             }
191              
192             sub dump {
193             my $self = shift;
194             $self->scan unless $self->{_scanned};
195             ### dump: $self->{_result}
196              
197             return sprintf(
198             '{"1":{"password":%d,"publickey":%d},"2":{"password":%d,"publickey":%d}}',
199             $self->{_result}{1} & $AUTH_IF{password} ? 1 : 0,
200             $self->{_result}{1} & $AUTH_IF{rsa} ? 1 : 0,
201             $self->{_result}{2} & $AUTH_IF{password} ? 1 : 0,
202             $self->{_result}{2} & $AUTH_IF{rsa} ? 1 : 0,
203             );
204             }
205              
206             1;
207              
208             __END__