line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
1077
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
31
|
|
2
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
39
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Net::IMP::Example::LogServerCertificate; |
5
|
1
|
|
|
1
|
|
6
|
use base 'Net::IMP::Base'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
118
|
|
6
|
1
|
|
|
1
|
|
6
|
use Net::SSLeay; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
42
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use fields ( |
9
|
1
|
|
|
|
|
5
|
'done', # done or no SSL |
10
|
|
|
|
|
|
|
'sbuf', # buffer on server side |
11
|
1
|
|
|
1
|
|
5
|
); |
|
1
|
|
|
|
|
1
|
|
12
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
84
|
use Net::IMP qw(:log :DEFAULT); # import IMP_ constants |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
183
|
|
14
|
1
|
|
|
1
|
|
7
|
use Net::IMP::Debug; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
15
|
1
|
|
|
1
|
|
7
|
use Carp 'croak'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
966
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub INTERFACE { |
18
|
|
|
|
|
|
|
return ([ |
19
|
|
|
|
|
|
|
undef, |
20
|
0
|
|
|
0
|
0
|
|
[ IMP_PASS, IMP_PREPASS, IMP_LOG ] |
21
|
|
|
|
|
|
|
]) |
22
|
|
|
|
|
|
|
} |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# create new analyzer object |
26
|
|
|
|
|
|
|
sub new_analyzer { |
27
|
0
|
|
|
0
|
1
|
|
my ($factory,%args) = @_; |
28
|
0
|
|
|
|
|
|
my $self = $factory->SUPER::new_analyzer(%args); |
29
|
|
|
|
|
|
|
|
30
|
0
|
|
|
|
|
|
$self->run_callback( |
31
|
|
|
|
|
|
|
# we are not interested in data from client |
32
|
|
|
|
|
|
|
[ IMP_PASS, 0, IMP_MAXOFFSET ], |
33
|
|
|
|
|
|
|
# and we will not change data from server, only inspect |
34
|
|
|
|
|
|
|
[ IMP_PREPASS, 1, IMP_MAXOFFSET ], |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
0
|
|
|
|
|
|
$self->{sbuf} = ''; |
38
|
0
|
|
|
|
|
|
return $self; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub data { |
42
|
0
|
|
|
0
|
1
|
|
my ($self,$dir,$data) = @_; |
43
|
0
|
0
|
|
|
|
|
return if $dir == 0; # should not happen |
44
|
0
|
0
|
|
|
|
|
return if $self->{done}; # done or no SSL |
45
|
0
|
0
|
|
|
|
|
return if $data eq ''; # eof from server |
46
|
|
|
|
|
|
|
|
47
|
0
|
|
|
|
|
|
my $buf = $self->{sbuf} .= $data; |
48
|
|
|
|
|
|
|
|
49
|
0
|
0
|
0
|
|
|
|
if ( _read_ssl_handshake($self,\$buf,2) # Server Hello |
50
|
|
|
|
|
|
|
and my $certs = _read_ssl_handshake($self,\$buf,11) # Certificates |
51
|
|
|
|
|
|
|
) { |
52
|
0
|
|
|
|
|
|
$self->{done} = 1; |
53
|
|
|
|
|
|
|
|
54
|
0
|
|
|
|
|
|
my ($len) = unpack("xa3",substr($certs,0,4,'')); |
55
|
0
|
|
|
|
|
|
$len = unpack("N","\0$len"); |
56
|
0
|
|
|
|
|
|
substr($certs,$len) = ''; |
57
|
0
|
|
|
|
|
|
$len = unpack("N","\0".substr($certs,0,3,'')); |
58
|
0
|
|
|
|
|
|
substr($certs,$len) = ''; |
59
|
0
|
|
|
|
|
|
my $i = 0; |
60
|
0
|
|
|
|
|
|
while ($certs ne '') { |
61
|
0
|
|
|
|
|
|
my $clen = unpack("N","\0".substr($certs,0,3,'')); |
62
|
0
|
|
|
|
|
|
my $cert = substr($certs,0,$clen,''); |
63
|
0
|
0
|
|
|
|
|
length($cert) == $clen or |
64
|
|
|
|
|
|
|
die "invalid certificate length ($clen vs. ".length($cert).")"; |
65
|
0
|
0
|
|
|
|
|
if ( my $line = eval { _cert2line($cert) } ) { |
|
0
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
$self->run_callback([ IMP_LOG,1,0,0,IMP_LOG_INFO, |
67
|
|
|
|
|
|
|
sprintf("chain[%d]: %s",$i,$line)]); |
68
|
|
|
|
|
|
|
} else { |
69
|
0
|
|
|
|
|
|
warn "failed to convert cert to string: $@"; |
70
|
|
|
|
|
|
|
} |
71
|
0
|
|
|
|
|
|
$i++; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$self->run_callback([ IMP_PASS,1,IMP_MAXOFFSET ]) |
76
|
0
|
0
|
|
|
|
|
if $self->{done}; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub _cert2line { |
80
|
0
|
|
|
0
|
|
|
my $der = shift; |
81
|
0
|
|
|
|
|
|
my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem()); |
82
|
0
|
|
|
|
|
|
Net::SSLeay::BIO_write($bio,$der); |
83
|
0
|
|
|
|
|
|
my $cert = Net::SSLeay::d2i_X509_bio($bio); |
84
|
0
|
|
|
|
|
|
Net::SSLeay::BIO_free($bio); |
85
|
0
|
0
|
|
|
|
|
$cert or die "cannot parse certificate: ". |
86
|
|
|
|
|
|
|
Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()); |
87
|
0
|
|
|
|
|
|
my $not_before = Net::SSLeay::X509_get_notBefore($cert); |
88
|
0
|
|
|
|
|
|
my $not_after = Net::SSLeay::X509_get_notAfter($cert); |
89
|
0
|
|
|
|
|
|
$_ = Net::SSLeay::P_ASN1_TIME_put2string($_) for($not_before,$not_after); |
90
|
0
|
|
|
|
|
|
my $subject = Net::SSLeay::X509_NAME_oneline( |
91
|
|
|
|
|
|
|
Net::SSLeay::X509_get_subject_name($cert)); |
92
|
0
|
|
|
|
|
|
return "$subject | $not_before - $not_after"; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub _read_ssl_handshake { |
97
|
0
|
|
|
0
|
|
|
my ($self,$buf,$expect_htype) = @_; |
98
|
0
|
0
|
|
|
|
|
return if length($$buf) < 22; # need way more data |
99
|
|
|
|
|
|
|
|
100
|
0
|
|
|
|
|
|
my ($ctype,$version,$len,$htype) = unpack('CnnC',$$buf); |
101
|
0
|
0
|
|
|
|
|
if ($ctype != 22) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
102
|
0
|
|
|
|
|
|
debug("no SSL >=3.0 handshake record"); |
103
|
0
|
|
|
|
|
|
goto bad; |
104
|
|
|
|
|
|
|
} elsif ( $len > 2**14 ) { |
105
|
0
|
|
|
|
|
|
debug("length looks way too big - assuming no ssl"); |
106
|
0
|
|
|
|
|
|
goto bad; |
107
|
|
|
|
|
|
|
} elsif ( $htype != $expect_htype ) { |
108
|
0
|
|
|
|
|
|
debug("unexpected handshake type $htype - assuming no ssl"); |
109
|
0
|
|
|
|
|
|
goto bad; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
0
|
0
|
|
|
|
|
length($$buf)-5 >= $len or return; # need more data |
113
|
0
|
|
|
|
|
|
substr($$buf,0,5,''); |
114
|
0
|
|
|
|
|
|
debug("got handshake type $htype length $len"); |
115
|
0
|
|
|
|
|
|
return substr($$buf,0,$len,''); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
bad: |
118
|
0
|
|
|
|
|
|
$self->{done} = 1; |
119
|
0
|
|
|
|
|
|
return; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# debugging stuff |
124
|
|
|
|
|
|
|
sub _hexdump { |
125
|
0
|
|
|
0
|
|
|
my ($buf,$len) = @_; |
126
|
0
|
0
|
|
|
|
|
$buf = substr($buf,0,$len) if $len; |
127
|
0
|
|
|
|
|
|
my @hx = map { sprintf("%02x",$_) } unpack('C*',$buf); |
|
0
|
|
|
|
|
|
|
128
|
0
|
|
|
|
|
|
my $t = ''; |
129
|
0
|
|
|
|
|
|
while (@hx) { |
130
|
0
|
|
|
|
|
|
$t .= join(' ',splice(@hx,0,16))."\n"; |
131
|
|
|
|
|
|
|
} |
132
|
0
|
|
|
|
|
|
return $t; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
1; |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
__END__ |