line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package LWP::Protocol::https; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
392208
|
use strict; |
|
4
|
|
|
|
|
20
|
|
|
4
|
|
|
|
|
192
|
|
4
|
|
|
|
|
|
|
our $VERSION = '6.09'; |
5
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
26
|
use base qw(LWP::Protocol::http); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
2166
|
|
7
|
|
|
|
|
|
|
require Net::HTTPS; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub socket_type |
10
|
|
|
|
|
|
|
{ |
11
|
12
|
|
|
12
|
0
|
355419
|
return "https"; |
12
|
|
|
|
|
|
|
} |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub _extra_sock_opts |
15
|
|
|
|
|
|
|
{ |
16
|
10
|
|
|
10
|
|
138591
|
my $self = shift; |
17
|
10
|
50
|
|
|
|
25
|
my %ssl_opts = %{$self->{ua}{ssl_opts} || {}}; |
|
10
|
|
|
|
|
89
|
|
18
|
10
|
100
|
|
|
|
52
|
if (delete $ssl_opts{verify_hostname}) { |
19
|
6
|
|
50
|
|
|
86
|
$ssl_opts{SSL_verify_mode} ||= 1; |
20
|
6
|
|
|
|
|
17
|
$ssl_opts{SSL_verifycn_scheme} = 'www'; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
else { |
23
|
4
|
|
|
|
|
13
|
$ssl_opts{SSL_verify_mode} = 0; |
24
|
|
|
|
|
|
|
} |
25
|
10
|
100
|
|
|
|
36
|
if ($ssl_opts{SSL_verify_mode}) { |
26
|
6
|
0
|
33
|
|
|
17
|
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
|
|
|
|
|
26
|
$self->{ssl_opts} = \%ssl_opts; |
49
|
10
|
|
|
|
|
96
|
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
|
|
6964
|
my( $me, $common_name, $san_name ) = @_; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# /CN has a '*.' prefix |
66
|
|
|
|
|
|
|
# MUST be an FQDN -- fishing? |
67
|
14
|
100
|
|
|
|
65
|
return 0 if( $common_name =~ /^\*\./ ); |
68
|
|
|
|
|
|
|
|
69
|
13
|
|
|
|
|
22
|
my $re = q{}; # empty string |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# turn a leading "*." into a regex |
72
|
13
|
100
|
|
|
|
40
|
if( $san_name =~ /^\*\./ ) { |
73
|
6
|
|
|
|
|
22
|
$san_name =~ s/\*//; |
74
|
6
|
|
|
|
|
13
|
$re = "[^.]+"; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# quotemeta the rest and match anchored |
78
|
13
|
100
|
|
|
|
250
|
if( $common_name =~ /^$re\Q$san_name\E$/ ) { |
79
|
6
|
|
|
|
|
28
|
return 1; |
80
|
|
|
|
|
|
|
} |
81
|
7
|
|
|
|
|
24
|
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
|
|
1702
|
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
|
|
|
|
8
|
return 'ok' if $me->_cn_match($common_name,$value); |
106
|
|
|
|
|
|
|
} |
107
|
1
|
|
|
|
|
3
|
return; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _check_sock |
111
|
|
|
|
|
|
|
{ |
112
|
8
|
|
|
8
|
|
498638
|
my($self, $req, $sock) = @_; |
113
|
8
|
|
|
|
|
56
|
my $check = $req->header("If-SSL-Cert-Subject"); |
114
|
8
|
50
|
|
|
|
799
|
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
|
|
193908
|
my $self = shift; |
130
|
8
|
|
|
|
|
72
|
$self->SUPER::_get_sock_info(@_); |
131
|
8
|
|
|
|
|
1607
|
my($res, $sock) = @_; |
132
|
8
|
50
|
33
|
|
|
140
|
if ($sock->can('get_sslversion') and my $sslversion = $sock->get_sslversion) { |
133
|
8
|
|
|
|
|
196
|
$res->header("Client-SSL-Version" => $sslversion); |
134
|
|
|
|
|
|
|
} |
135
|
8
|
|
|
|
|
584
|
$res->header("Client-SSL-Cipher" => $sock->get_cipher); |
136
|
8
|
|
|
|
|
700
|
my $cert = $sock->get_peer_certificate; |
137
|
8
|
50
|
|
|
|
53
|
if ($cert) { |
138
|
8
|
|
|
|
|
44
|
$res->header("Client-SSL-Cert-Subject" => $cert->subject_name); |
139
|
8
|
|
|
|
|
1042
|
$res->header("Client-SSL-Cert-Issuer" => $cert->issuer_name); |
140
|
|
|
|
|
|
|
} |
141
|
8
|
50
|
|
|
|
831
|
if (!$self->{ssl_opts}{SSL_verify_mode}) { |
|
|
0
|
|
|
|
|
|
142
|
8
|
|
|
|
|
44
|
$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
|
|
|
|
|
372
|
$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
|
|
94490
|
my ($self,$sock,$url) = @_; |
156
|
6
|
|
|
|
|
33
|
$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
|
|
|
|
77538
|
$@ = LWP::Protocol::https::Socket->errstr if ! $sock; |
162
|
6
|
|
|
|
|
52
|
return $sock; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
#----------------------------------------------------------- |
167
|
|
|
|
|
|
|
package LWP::Protocol::https::Socket; |
168
|
|
|
|
|
|
|
|
169
|
4
|
|
|
4
|
|
173605
|
use base qw(Net::HTTPS LWP::Protocol::http::SocketMethods); |
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
2080
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
1; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
__END__ |