Search code examples
perlssluser-agentlwpocsp

OCSP resolve_blocking() and LWP::UserAgent


This question is regarding Perl usage of IO::Socket::SSL / Net::SSLeay / LWP::UserAgent.

To check certificate revocation status with OCSP, one needs to explicitly call the ocsp_resolver of the socket, e.g. resolve_blocking(). That's the strategy I use when connecting via Net::LDAP.

But in LWP::UserAgent, the connection is a private cached attribute of the object.

Can I get the socket reference from within a verify callback, i.e. the second argument of the callback?

If so then

  • How? I didn't seem to find a fitting Net::SSLeay::X509_Store<somthing>call.

  • Can I conduct blocking OCSP at that point?

If not, then

  • How to invoke ocsp_resolver?

I need this to check the certificate status of non-stapling web servers, as well as that of chains certificate (normally not stapled).


Solution

  • I think there is (currently, as of IO::Socket::SSL 2.056) no clean way to do is.

    But since it is Perl one can do it with some monkey patching. Since the check should best be done immediately after successfully connecting to the server one can use a wrapper around IO::Socket::SSL::connect_SSL to get the SSL socket, do the OCSP checking and let the connect fail if the OCSP check resulted in errors:

    use strict;
    use warnings;
    use IO::Socket::SSL;
    use LWP::UserAgent;
    
    {
        my $old = \&IO::Socket::SSL::connect_SSL;
        no warnings 'redefine';
        *IO::Socket::SSL::connect_SSL = sub {
            my $sock = $old->(@_) or return;
            my $ocsp = $sock->ocsp_resolver;
            if (my $errors = $ocsp->resolve_blocking()) {
                warn $errors;
                close($sock);
                return;
            }
            return $sock;
        }
    }
    
    my $ua = LWP::UserAgent->new();
    $ua->ssl_opts(SSL_ocsp_mode => SSL_OCSP_FULL_CHAIN|SSL_OCSP_FAIL_HARD|SSL_OCSP_NO_STAPLE);
    my $resp = $ua->get('https://revoked.grc.com');
    print $resp->decoded_content;
    

    Note that this monkey patching is global, i.e. affects all IO::Socket::SSL objects and not only the one used within LWP::UserAgent. So it might have some unintended side effects when using in a more complex program than this example. A cleaner design would maybe to have some user defined callback after connect. Maybe I'll add this kind of functionality to IO::Socket::SSL, but for now this hack should work.

    Note also that resolve_blocking is not using the LWP::UserAgent object but relies on HTTP::Tiny. Thus any settings specific to LWP::UserAgent like proxies will have no effect. If this is a problem you can do the requests manually and feed it into the OCSP resolver object using $ocsp->requests to get the requests and $ocsp->add_response to feed the response into the resolver object.