5

Here's a program that does an HTTPS request, with some code at the start that I'm going to explain below:

use 5.012;
use LWP::UserAgent;
use HTTP::Request::Common;
use Net::SSLeay;

BEGIN {
    return unless $^O eq 'MSWin32'; # only needed on Windows
    print STDERR "attempting to set HTTPS_CA_FILE to PEM file path\n";
    require Mozilla::CA; # load module to determine PEM file path
    my $pemfile = do {
        my $path = $INC{ 'Mozilla/CA.pm' };
        $path =~ s#\.pm$#/cacert.pem#;
        $path;
    };
    if ( -f $pemfile ) {
        $ENV{HTTPS_CA_FILE} = $pemfile;
        print STDERR "HTTPS_CA_FILE set to $pemfile\n";
    }
    else {
        warn "PEM file $pemfile missing";
    }
} # ==========================================================================

$Net::SSLeay::trace = 2;

my $ua = LWP::UserAgent->new;
my $req = GET 'https://client.billsafe.de/';
my $rsp = $ua->request( $req );

say $rsp->is_success ? 'success' : 'failure';
say $rsp->status_line;
say '=================';
say substr $rsp->decoded_content, 0, 200;
say '=================';

# possibly relevant module versions
for ( qw/Net::SSLeay Crypt::SSLeay LWP::Protocol::https Mozilla::CA/ ) {
    no strict 'refs';
    say $_, "\t", ${"${_}::VERSION"}
}

The code at the beginning sets the environment variable HTTPS_CA_FILE to the value of the PEM file cacert.pem from Mozilla::CA that gets loaded by default (I checked using procmon.exe, the file is fully read by default).

The reason for doing this apparently nonsensical setting is that we have some Windows machines (Windows Server 2008) where the SSL setup fails with certificate verify failed when the environment variable is not set. It is a mystery to us why this is so. And it works fine on other Windows machines with identical versions for Net::SSLeay, LWP::Protocol::https and Mozilla::CA.

Our module versions are:

  • Net::SSLeay 1.36
  • Crypt::SSLeay -/-
  • LWP::Protocol::https 6.02
  • Mozilla::CA 20110409

Now the question: Are there other places, apart from cacert.pem, that root certificates are loaded from in this constellation (Windows, Perl, Net::SSLeay)? If so, what are they? Where can I read up on it?

Update

The OpenSSL docs do not mention any certificate store other than a plain file and a plain directory:

The Windows C API functions used to open the system certificate store are the following:

I checked out the OpenSSL HEAD from CVS. The CertOpenStore function is indeed used in engines/e_capi.c. I haven't investigated further to find out what is used to access a store in the OpenSSL versions on the servers in question.

If you do a web search you'll see that a couple of people have wondered whether OpenSSL can access the Windows certificate store directly, or have proposed to patch OpenSSL accordingly. There's also this recent issue on the TortoiseSVN list (Windows Certificate Store / OpenSSL CAPI). Some more research needed to find out what's the matter here.

Lumi
  • 14,775
  • 8
  • 59
  • 92

1 Answers1

0

Since LWP 6.00 you can pass an ssl_opts hashref to new specifying the certificate files amongst other options:

my $ua = LWP::UserAgent->new(
    ssl_opts   => {
        verify_hostname => 1,
        SSL_cert_file   => $ssl_cert_file,
        SSL_key_file    => $ssl_key_file,
    },
);
Alexander Hartmaier
  • 2,178
  • 12
  • 21
  • The options for CA certificates are called `SSL_ca_file` and `SSL_ca_path`, you got that wrong in your code sample. But I didn't use those options in my sample script, and it's not answering my question. Thanks anyway. – Lumi Oct 20 '11 at 16:56
  • Sorry for that, those are for certificate authentication. My point was that you can use parameters for setting those instead of environment variables which are global. – Alexander Hartmaier Oct 21 '11 at 07:58
  • Take a look at https://metacpan.org/source/GAAS/LWP-Protocol-https-6.02/lib/LWP/Protocol/https.pm. It uses the configured CA file or path if they are set and falls back to Mozilla::CA. In your case I suggest you check if the failing servers have the correct time set with ntp. – Alexander Hartmaier Oct 21 '11 at 08:05
  • Thanks. Could you explain why you think the correct time matters? Certificate expiry dates for grossly misconfigured dates, like machine date set far in the future so the CA certificate appears to have expired? – Lumi Oct 22 '11 at 11:23
  • Yes, that's one of the things I can think of that could be different between your servers. I'm not sure if the Perl CA modules on Windows use it but check the Windows cert store for differences, maybe because of different patches or service packs. – Alexander Hartmaier Oct 27 '11 at 16:00
  • Thank you, @abraxxa. The servers have the correct time. Even if they didn't that fact wouldn't explain why cert verification succeeds when I set the `HTTPS_CA_FILE` env var as per my description. – Lumi Oct 27 '11 at 20:16