Package: libio-socket-ssl-perl
Version: 1.992-1

The attached test program is supposed to try to connect to www.debian.org while trusting only a random wrong CA:

   my $host = 'www.debian.org';
   my $ca = 'China_Internet_Network_Information_Center_EV_Certificates_Root';
   # definitely NOT the www.debian.org's CA ---^
   my $cafile = "/usr/share/ca-certificates/mozilla/$ca.crt";
   IO::Socket::SSL::set_defaults(
       SSL_verify_mode => SSL_VERIFY_PEER,
       SSL_verifycn_scheme => 'http',
       SSL_ca_file => $cafile,
   );

The program calls set_args_filter_hack('use_defaults'). This call shouldn't affect anything interesting in this case, but it actually break things:

   $ perl test-filter-hack.pl
   Eeek! Connected to www.debian.org, even though only 
China_Internet_Network_Information_Center_EV_Certificates_Root was supposed to 
be trusted.

This is my understanding why it happens:

IO::Socket::SSL has defaults for both SSL_ca_file and SSL_ca_path. These defaults are normally only taken into account if user set none of these two themselves:

   # if any of SSL_ca* is set don't set the other SSL_ca*
   # from defaults
   if ( $arg_hash->{SSL_ca} ) {
       $arg_hash->{SSL_ca_file} ||= undef
       $arg_hash->{SSL_ca_path} ||= undef
   } elsif ( $arg_hash->{SSL_ca_path} ) {
       $arg_hash->{SSL_ca_file} ||= undef
   } elsif ( $arg_hash->{SSL_ca_file} ) {
       $arg_hash->{SSL_ca_path} ||= undef;
   }

But if you use set_args_filter_hack('use_defaults'), the code I quoted above is no-op, because all the SSL_ca* are already initialized with %DEFAULT_SSL_CLIENT_ARGS values:

   sub set_args_filter_hack {
       # ...
       } elsif ( $sub eq 'use_defaults' ) {
           # override args with defaults
           $FILTER_SSL_ARGS = sub {
               my ($is_server,$args) = @_;
               %$args = ( %$args, $is_server
                   ? ( %DEFAULT_SSL_SERVER_ARGS, %$GLOBAL_SSL_SERVER_ARGS )
                   : ( %DEFAULT_SSL_CLIENT_ARGS, %$GLOBAL_SSL_CLIENT_ARGS )
               );
           }
       }
   }

A possible work-around is to always set both SSL_ca_file and SSL_ca_path, setting the one you don't need explicitly to undef:

 IO::Socket::SSL::set_defaults(
     SSL_verify_mode => SSL_VERIFY_PEER,
     SSL_verifycn_scheme => 'http',
     SSL_ca_file => $cafile,
     SSL_ca_path => undef,
 );


-- System Information:
Debian Release: jessie/sid
 APT prefers unstable
 APT policy: (990, 'unstable'), (500, 'experimental')
Architecture: i386 (x86_64)
Foreign Architectures: amd64

Kernel: Linux 3.14-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=C, LC_CTYPE=pl_PL.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages libio-socket-ssl-perl depends on:
ii  libnet-ssleay-perl  1.63-1
ii  netbase             5.2
ii  perl                5.18.2-4

Versions of packages libio-socket-ssl-perl recommends:
ii  libio-socket-inet6-perl     2.72-1
ii  libio-socket-ip-perl        0.29-1
ii  libnet-idn-encode-perl      2.100-2
ii  libsocket6-perl             0.25-1
ii  liburi-perl                 1.60-1
ii  perl                        5.18.2-4
ii  perl-base [libsocket-perl]  5.18.2-4

Versions of packages libio-socket-ssl-perl suggests:
ii  ca-certificates  20140325

--
Jakub Wilk
#!/usr/bin/perl

use strict;
use warnings;

use IO::Socket::SSL;

my $host = 'www.debian.org';
my $ca = 'China_Internet_Network_Information_Center_EV_Certificates_Root';
# definitely NOT the www.debian.org's CA ---^
my $cafile = "/usr/share/ca-certificates/mozilla/$ca.crt";
-r $cafile or die "$cafile: $!";

IO::Socket::SSL::set_defaults(
    SSL_verify_mode => SSL_VERIFY_PEER,
    SSL_verifycn_scheme => 'http',
    SSL_ca_file => $cafile,
    # SSL_ca_path => undef,
    # ^-- uncommenting this magically fixes the problem
);

IO::Socket::SSL::set_args_filter_hack('use_defaults');

my $sock = IO::Socket::SSL->new(
    PeerAddr => $host,
    PeerPort => 'https',
) or die IO::Socket::SSL::errstr;

print "Eeek! Connected to $host, even though only $ca was supposed to be trusted.\n";

$sock->close();

Reply via email to