| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package LWP::Protocol::https; |
|
2
|
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
395210
|
use strict; |
|
|
4
|
|
|
|
|
26
|
|
|
|
4
|
|
|
|
|
207
|
|
|
4
|
|
|
|
|
|
|
our $VERSION = '6.10'; |
|
5
|
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
24
|
use base qw(LWP::Protocol::http); |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
2215
|
|
|
7
|
|
|
|
|
|
|
require Net::HTTPS; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub socket_type |
|
10
|
|
|
|
|
|
|
{ |
|
11
|
12
|
|
|
12
|
0
|
223649
|
return "https"; |
|
12
|
|
|
|
|
|
|
} |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub _extra_sock_opts |
|
15
|
|
|
|
|
|
|
{ |
|
16
|
10
|
|
|
10
|
|
138569
|
my $self = shift; |
|
17
|
10
|
50
|
|
|
|
62
|
my %ssl_opts = %{$self->{ua}{ssl_opts} || {}}; |
|
|
10
|
|
|
|
|
125
|
|
|
18
|
10
|
100
|
|
|
|
47
|
if (delete $ssl_opts{verify_hostname}) { |
|
19
|
6
|
|
50
|
|
|
53
|
$ssl_opts{SSL_verify_mode} ||= 1; |
|
20
|
6
|
|
|
|
|
16
|
$ssl_opts{SSL_verifycn_scheme} = 'www'; |
|
21
|
|
|
|
|
|
|
} |
|
22
|
|
|
|
|
|
|
else { |
|
23
|
4
|
|
|
|
|
12
|
$ssl_opts{SSL_verify_mode} = 0; |
|
24
|
|
|
|
|
|
|
} |
|
25
|
10
|
100
|
|
|
|
40
|
if ($ssl_opts{SSL_verify_mode}) { |
|
26
|
6
|
0
|
33
|
|
|
21
|
unless (exists $ssl_opts{SSL_ca_file} || exists $ssl_opts{SSL_ca_path}) { |
|
27
|
0
|
|
|
|
|
0
|
eval { |
|
28
|
0
|
|
|
|
|
0
|
require Mozilla::CA; |
|
29
|
|
|
|
|
|
|
}; |
|
30
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
31
|
0
|
0
|
|
|
|
0
|
if ($@ =~ /^Can't locate Mozilla\/CA\.pm/) { |
|
32
|
0
|
|
|
|
|
0
|
$@ = <<'EOT'; |
|
33
|
|
|
|
|
|
|
Can't verify SSL peers without knowing which Certificate Authorities to trust |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
This problem can be fixed by either setting the PERL_LWP_SSL_CA_FILE |
|
36
|
|
|
|
|
|
|
environment variable or by installing the Mozilla::CA module. |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
To disable verification of SSL peers set the PERL_LWP_SSL_VERIFY_HOSTNAME |
|
39
|
|
|
|
|
|
|
environment variable to 0. If you do this you can't be sure that you |
|
40
|
|
|
|
|
|
|
communicate with the expected peer. |
|
41
|
|
|
|
|
|
|
EOT |
|
42
|
|
|
|
|
|
|
} |
|
43
|
0
|
|
|
|
|
0
|
die $@; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
0
|
|
|
|
|
0
|
$ssl_opts{SSL_ca_file} = Mozilla::CA::SSL_ca_file(); |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
} |
|
48
|
10
|
|
|
|
|
30
|
$self->{ssl_opts} = \%ssl_opts; |
|
49
|
10
|
|
|
|
|
118
|
return (%ssl_opts, $self->SUPER::_extra_sock_opts); |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
#------------------------------------------------------------ |
|
53
|
|
|
|
|
|
|
# _cn_match($common_name, $san_name) |
|
54
|
|
|
|
|
|
|
# common_name: an IA5String |
|
55
|
|
|
|
|
|
|
# san_name: subjectAltName |
|
56
|
|
|
|
|
|
|
# initially we were only concerned with the dNSName |
|
57
|
|
|
|
|
|
|
# and the 'left-most' only wildcard as noted in |
|
58
|
|
|
|
|
|
|
# https://tools.ietf.org/html/rfc6125#section-6.4.3 |
|
59
|
|
|
|
|
|
|
# this method does not match any wildcarding in the |
|
60
|
|
|
|
|
|
|
# domain name as listed in section-6.4.3.3 |
|
61
|
|
|
|
|
|
|
# |
|
62
|
|
|
|
|
|
|
sub _cn_match { |
|
63
|
14
|
|
|
14
|
|
6790
|
my( $me, $common_name, $san_name ) = @_; |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# /CN has a '*.' prefix |
|
66
|
|
|
|
|
|
|
# MUST be an FQDN -- fishing? |
|
67
|
14
|
100
|
|
|
|
50
|
return 0 if( $common_name =~ /^\*\./ ); |
|
68
|
|
|
|
|
|
|
|
|
69
|
13
|
|
|
|
|
24
|
my $re = q{}; # empty string |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# turn a leading "*." into a regex |
|
72
|
13
|
100
|
|
|
|
37
|
if( $san_name =~ /^\*\./ ) { |
|
73
|
6
|
|
|
|
|
22
|
$san_name =~ s/\*//; |
|
74
|
6
|
|
|
|
|
12
|
$re = "[^.]+"; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# quotemeta the rest and match anchored |
|
78
|
13
|
100
|
|
|
|
245
|
if( $common_name =~ /^$re\Q$san_name\E$/ ) { |
|
79
|
6
|
|
|
|
|
29
|
return 1; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
7
|
|
|
|
|
28
|
return 0; |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
#------------------------------------------------------- |
|
85
|
|
|
|
|
|
|
# _in_san( cn, cert ) |
|
86
|
|
|
|
|
|
|
# 'cn' of the form /CN=host_to_check ( "Common Name" form ) |
|
87
|
|
|
|
|
|
|
# 'cert' any object that implements a peer_certificate('subjectAltNames') method |
|
88
|
|
|
|
|
|
|
# which will return an array of ( type-id, value ) pairings per |
|
89
|
|
|
|
|
|
|
# http://tools.ietf.org/html/rfc5280#section-4.2.1.6 |
|
90
|
|
|
|
|
|
|
# if there is no subjectAltNames there is nothing more to do. |
|
91
|
|
|
|
|
|
|
# currently we have a _cn_match() that will allow for simple compare. |
|
92
|
|
|
|
|
|
|
sub _in_san |
|
93
|
|
|
|
|
|
|
{ |
|
94
|
3
|
|
|
3
|
|
1795
|
my($me, $cn, $cert) = @_; |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# we can return early if there are no SAN options. |
|
97
|
3
|
|
|
|
|
9
|
my @sans = $cert->peer_certificate('subjectAltNames'); |
|
98
|
3
|
100
|
|
|
|
16
|
return unless scalar @sans; |
|
99
|
|
|
|
|
|
|
|
|
100
|
2
|
|
|
|
|
15
|
(my $common_name = $cn) =~ s/.*=//; # strip off the prefix. |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# get the ( type-id, value ) pairwise |
|
103
|
|
|
|
|
|
|
# currently only the basic CN to san_name check |
|
104
|
2
|
|
|
|
|
10
|
while( my ( $type_id, $value ) = splice( @sans, 0, 2 ) ) { |
|
105
|
2
|
100
|
|
|
|
7
|
return 'ok' if $me->_cn_match($common_name,$value); |
|
106
|
|
|
|
|
|
|
} |
|
107
|
1
|
|
|
|
|
3
|
return; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _check_sock |
|
111
|
|
|
|
|
|
|
{ |
|
112
|
8
|
|
|
8
|
|
346516
|
my($self, $req, $sock) = @_; |
|
113
|
8
|
|
|
|
|
60
|
my $check = $req->header("If-SSL-Cert-Subject"); |
|
114
|
8
|
50
|
|
|
|
811
|
if (defined $check) { |
|
115
|
0
|
|
0
|
|
|
0
|
my $cert = $sock->get_peer_certificate || |
|
116
|
|
|
|
|
|
|
die "Missing SSL certificate"; |
|
117
|
0
|
|
|
|
|
0
|
my $subject = $cert->subject_name; |
|
118
|
0
|
0
|
0
|
|
|
0
|
unless ( defined $subject && ( $subject =~ /$check/ ) ) { |
|
119
|
0
|
|
|
|
|
0
|
my $ok = $self->_in_san( $check, $cert); |
|
120
|
0
|
0
|
|
|
|
0
|
die "Bad SSL certificate subject: '$subject' !~ /$check/" |
|
121
|
|
|
|
|
|
|
unless $ok; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
0
|
|
|
|
|
0
|
$req->remove_header("If-SSL-Cert-Subject"); # don't pass it on |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub _get_sock_info |
|
128
|
|
|
|
|
|
|
{ |
|
129
|
8
|
|
|
8
|
|
148975
|
my $self = shift; |
|
130
|
8
|
|
|
|
|
64
|
$self->SUPER::_get_sock_info(@_); |
|
131
|
8
|
|
|
|
|
1765
|
my($res, $sock) = @_; |
|
132
|
8
|
50
|
33
|
|
|
169
|
if ($sock->can('get_sslversion') and my $sslversion = $sock->get_sslversion) { |
|
133
|
8
|
|
|
|
|
205
|
$res->header("Client-SSL-Version" => $sslversion); |
|
134
|
|
|
|
|
|
|
} |
|
135
|
8
|
|
|
|
|
601
|
$res->header("Client-SSL-Cipher" => $sock->get_cipher); |
|
136
|
8
|
|
|
|
|
696
|
my $cert = $sock->get_peer_certificate; |
|
137
|
8
|
50
|
|
|
|
59
|
if ($cert) { |
|
138
|
8
|
|
|
|
|
52
|
$res->header("Client-SSL-Cert-Subject" => $cert->subject_name); |
|
139
|
8
|
|
|
|
|
1032
|
$res->header("Client-SSL-Cert-Issuer" => $cert->issuer_name); |
|
140
|
|
|
|
|
|
|
} |
|
141
|
8
|
50
|
|
|
|
896
|
if (!$self->{ssl_opts}{SSL_verify_mode}) { |
|
|
|
0
|
|
|
|
|
|
|
142
|
8
|
|
|
|
|
46
|
$res->push_header("Client-SSL-Warning" => "Peer certificate not verified"); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
elsif (!$self->{ssl_opts}{SSL_verifycn_scheme}) { |
|
145
|
0
|
|
|
|
|
0
|
$res->push_header("Client-SSL-Warning" => "Peer hostname match with certificate not verified"); |
|
146
|
|
|
|
|
|
|
} |
|
147
|
8
|
|
|
|
|
382
|
$res->header("Client-SSL-Socket-Class" => $Net::HTTPS::SSL_SOCKET_CLASS); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# upgrade plain socket to SSL, used for CONNECT tunnel when proxying https |
|
151
|
|
|
|
|
|
|
# will only work if the underlying socket class of Net::HTTPS is |
|
152
|
|
|
|
|
|
|
# IO::Socket::SSL, but code will only be called in this case |
|
153
|
|
|
|
|
|
|
if ( $Net::HTTPS::SSL_SOCKET_CLASS->can('start_SSL')) { |
|
154
|
|
|
|
|
|
|
*_upgrade_sock = sub { |
|
155
|
6
|
|
|
6
|
|
89368
|
my ($self,$sock,$url) = @_; |
|
156
|
6
|
|
|
|
|
35
|
$sock = LWP::Protocol::https::Socket->start_SSL( $sock, |
|
157
|
|
|
|
|
|
|
SSL_verifycn_name => $url->host, |
|
158
|
|
|
|
|
|
|
SSL_hostname => $url->host, |
|
159
|
|
|
|
|
|
|
$self->_extra_sock_opts, |
|
160
|
|
|
|
|
|
|
); |
|
161
|
6
|
50
|
|
|
|
83455
|
$@ = LWP::Protocol::https::Socket->errstr if ! $sock; |
|
162
|
6
|
|
|
|
|
37
|
return $sock; |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
#----------------------------------------------------------- |
|
167
|
|
|
|
|
|
|
package LWP::Protocol::https::Socket; |
|
168
|
|
|
|
|
|
|
|
|
169
|
4
|
|
|
4
|
|
177861
|
use base qw(Net::HTTPS LWP::Protocol::http::SocketMethods); |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
2275
|
|
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
1; |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
__END__ |