mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-10 05:00:45 +00:00
HTTPMicro: Inline part of IO::Socket::SSL for cases when the local version of the module isn't high enough to support ->verify_hostname(), like in centos5
This commit is contained in:
183
bin/pt-archiver
183
bin/pt-archiver
@@ -4353,8 +4353,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -4597,6 +4603,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -3468,8 +3468,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -3712,6 +3718,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -2912,8 +2912,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -3156,6 +3162,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
183
bin/pt-diskstats
183
bin/pt-diskstats
@@ -4023,8 +4023,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -4267,6 +4273,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -3869,8 +3869,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -4113,6 +4119,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
183
bin/pt-find
183
bin/pt-find
@@ -2709,8 +2709,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -2953,6 +2959,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -2616,8 +2616,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -2860,6 +2866,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
183
bin/pt-heartbeat
183
bin/pt-heartbeat
@@ -3793,8 +3793,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -4037,6 +4043,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -5375,8 +5375,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -5619,6 +5625,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
183
bin/pt-kill
183
bin/pt-kill
@@ -5384,8 +5384,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -5628,6 +5634,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -6406,8 +6406,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -6650,6 +6656,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -6560,8 +6560,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -6804,6 +6810,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -12409,8 +12409,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -12653,6 +12659,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -3025,8 +3025,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -3269,6 +3275,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -3648,8 +3648,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -3892,6 +3898,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -574,8 +574,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -818,6 +824,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
183
bin/pt-upgrade
183
bin/pt-upgrade
@@ -10850,8 +10850,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -11094,6 +11100,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
@@ -3937,8 +3937,14 @@ sub connect {
|
||||
IO::Socket::SSL->start_SSL($self->{fh});
|
||||
ref($self->{fh}) eq 'IO::Socket::SSL'
|
||||
or die(qq/SSL connection failed for $host\n/);
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args )
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
if ( $self->{fh}->can("verify_hostname") ) {
|
||||
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
|
||||
}
|
||||
else {
|
||||
my $fh = $self->{fh};
|
||||
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
|
||||
or die(qq/SSL certificate not valid for $host\n/);
|
||||
}
|
||||
}
|
||||
|
||||
$self->{host} = $host;
|
||||
@@ -4181,6 +4187,179 @@ sub can_write {
|
||||
return $self->_do_timeout('write', @_)
|
||||
}
|
||||
|
||||
my $prog = <<'EOP';
|
||||
BEGIN {
|
||||
if ( defined &IO::Socket::SSL::CAN_IPV6 ) {
|
||||
*CAN_IPV6 = \*IO::Socket::SSL::CAN_IPV6;
|
||||
}
|
||||
else {
|
||||
constant->import( CAN_IPV6 => '' );
|
||||
}
|
||||
my %const = (
|
||||
NID_CommonName => 13,
|
||||
GEN_DNS => 2,
|
||||
GEN_IPADD => 7,
|
||||
);
|
||||
while ( my ($name,$value) = each %const ) {
|
||||
no strict 'refs';
|
||||
*{$name} = UNIVERSAL::can( 'Net::SSLeay', $name ) || sub { $value };
|
||||
}
|
||||
}
|
||||
{
|
||||
my %dispatcher = (
|
||||
issuer => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name( shift )) },
|
||||
subject => sub { Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name( shift )) },
|
||||
);
|
||||
if ( $Net::SSLeay::VERSION >= 1.30 ) {
|
||||
$dispatcher{commonName} = sub {
|
||||
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID(
|
||||
Net::SSLeay::X509_get_subject_name( shift ), NID_CommonName);
|
||||
$cn =~s{\0$}{}; # work around Bug in Net::SSLeay <1.33
|
||||
$cn;
|
||||
}
|
||||
} else {
|
||||
$dispatcher{commonName} = sub {
|
||||
croak "you need at least Net::SSLeay version 1.30 for getting commonName"
|
||||
}
|
||||
}
|
||||
|
||||
if ( $Net::SSLeay::VERSION >= 1.33 ) {
|
||||
$dispatcher{subjectAltNames} = sub { Net::SSLeay::X509_get_subjectAltNames( shift ) };
|
||||
} else {
|
||||
$dispatcher{subjectAltNames} = sub {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
$dispatcher{authority} = $dispatcher{issuer};
|
||||
$dispatcher{owner} = $dispatcher{subject};
|
||||
$dispatcher{cn} = $dispatcher{commonName};
|
||||
|
||||
sub _peer_certificate {
|
||||
my ($self, $field) = @_;
|
||||
my $ssl = $self->_get_ssl_object or return;
|
||||
|
||||
my $cert = ${*$self}{_SSL_certificate}
|
||||
||= Net::SSLeay::get_peer_certificate($ssl)
|
||||
or return $self->error("Could not retrieve peer certificate");
|
||||
|
||||
if ($field) {
|
||||
my $sub = $dispatcher{$field} or croak
|
||||
"invalid argument for peer_certificate, valid are: ".join( " ",keys %dispatcher ).
|
||||
"\nMaybe you need to upgrade your Net::SSLeay";
|
||||
return $sub->($cert);
|
||||
} else {
|
||||
return $cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
my %scheme = (
|
||||
ldap => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 'leftmost',
|
||||
check_cn => 'always',
|
||||
},
|
||||
http => {
|
||||
wildcards_in_cn => 'anywhere',
|
||||
wildcards_in_alt => 'anywhere',
|
||||
check_cn => 'when_only',
|
||||
},
|
||||
smtp => {
|
||||
wildcards_in_cn => 0,
|
||||
wildcards_in_alt => 0,
|
||||
check_cn => 'always'
|
||||
},
|
||||
none => {}, # do not check
|
||||
);
|
||||
|
||||
$scheme{www} = $scheme{http}; # alias
|
||||
$scheme{xmpp} = $scheme{http}; # rfc 3920
|
||||
$scheme{pop3} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{imap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{acap} = $scheme{ldap}; # rfc 2595
|
||||
$scheme{nntp} = $scheme{ldap}; # rfc 4642
|
||||
$scheme{ftp} = $scheme{http}; # rfc 4217
|
||||
|
||||
|
||||
sub _verify_hostname_of_cert {
|
||||
my $identity = shift;
|
||||
my $cert = shift;
|
||||
my $scheme = shift || 'none';
|
||||
if ( ! ref($scheme) ) {
|
||||
$scheme = $scheme{$scheme} or croak "scheme $scheme not defined";
|
||||
}
|
||||
|
||||
return 1 if ! %$scheme; # 'none'
|
||||
|
||||
my $commonName = $dispatcher{cn}->($cert);
|
||||
my @altNames = $dispatcher{subjectAltNames}->($cert);
|
||||
|
||||
if ( my $sub = $scheme->{callback} ) {
|
||||
return $sub->($identity,$commonName,@altNames);
|
||||
}
|
||||
|
||||
|
||||
my $ipn;
|
||||
if ( CAN_IPV6 and $identity =~m{:} ) {
|
||||
$ipn = IO::Socket::SSL::inet_pton(IO::Socket::SSL::AF_INET6,$identity)
|
||||
or croak "'$identity' is not IPv6, but neither IPv4 nor hostname";
|
||||
} elsif ( $identity =~m{^\d+\.\d+\.\d+\.\d+$} ) {
|
||||
$ipn = IO::Socket::SSL::inet_aton( $identity ) or croak "'$identity' is not IPv4, but neither IPv6 nor hostname";
|
||||
} else {
|
||||
if ( $identity =~m{[^a-zA-Z0-9_.\-]} ) {
|
||||
$identity =~m{\0} and croak("name '$identity' has \\0 byte");
|
||||
$identity = IO::Socket::SSL::idn_to_ascii($identity) or
|
||||
croak "Warning: Given name '$identity' could not be converted to IDNA!";
|
||||
}
|
||||
}
|
||||
|
||||
my $check_name = sub {
|
||||
my ($name,$identity,$wtyp) = @_;
|
||||
$wtyp ||= '';
|
||||
my $pattern;
|
||||
if ( $wtyp eq 'anywhere' and $name =~m{^([a-zA-Z0-9_\-]*)\*(.+)} ) {
|
||||
$pattern = qr{^\Q$1\E[a-zA-Z0-9_\-]*\Q$2\E$}i;
|
||||
} elsif ( $wtyp eq 'leftmost' and $name =~m{^\*(\..+)$} ) {
|
||||
$pattern = qr{^[a-zA-Z0-9_\-]*\Q$1\E$}i;
|
||||
} else {
|
||||
$pattern = qr{^\Q$name\E$}i;
|
||||
}
|
||||
return $identity =~ $pattern;
|
||||
};
|
||||
|
||||
my $alt_dnsNames = 0;
|
||||
while (@altNames) {
|
||||
my ($type, $name) = splice (@altNames, 0, 2);
|
||||
if ( $ipn and $type == GEN_IPADD ) {
|
||||
return 1 if $ipn eq $name;
|
||||
|
||||
} elsif ( ! $ipn and $type == GEN_DNS ) {
|
||||
$name =~s/\s+$//; $name =~s/^\s+//;
|
||||
$alt_dnsNames++;
|
||||
$check_name->($name,$identity,$scheme->{wildcards_in_alt})
|
||||
and return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $ipn and (
|
||||
$scheme->{check_cn} eq 'always' or
|
||||
$scheme->{check_cn} eq 'when_only' and !$alt_dnsNames)) {
|
||||
$check_name->($commonName,$identity,$scheme->{wildcards_in_cn})
|
||||
and return 1;
|
||||
}
|
||||
|
||||
return 0; # no match
|
||||
}
|
||||
}
|
||||
EOP
|
||||
|
||||
eval { require IO::Socket::SSL };
|
||||
if ( $INC{"IO/Socket/SSL.pm"} ) {
|
||||
eval $prog;
|
||||
die $@ if $@;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
Reference in New Issue
Block a user