Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better SSL error messages #102

Closed
JanWielemaker opened this issue Jun 11, 2017 · 10 comments
Closed

Better SSL error messages #102

JanWielemaker opened this issue Jun 11, 2017 · 10 comments
Assignees

Comments

@JanWielemaker
Copy link
Member

Copied from the HTTP package, where this was reported by @wouterbeek.

Port 80 is not the correct port for HTTPS requests of course, but the error message is currently not very descriptive:

?- use_module(library(http/http_open)).
?- use_module(library(http/http_ssl_plugin)).
?- http_open('https://aap.nl:80', In, [cert_verify_hook(cert_accept_any)]).
ERROR: SSL(140770FC) func(119): reason(252)

Is there an existing mapping from SSL error codes to human-readable descriptions that can help the programmer a bit better?

@triska triska self-assigned this Jun 11, 2017
@triska
Copy link
Member

triska commented Jun 13, 2017

Please indicate the OpenSSL version and the system where this occurs.

For example, on Debian, I get:

?- use_module(library(http/http_open)).
true.

?- http_open('https://aap.nl:80', In, [cert_verify_hook(cert_accept_any)]).
ERROR: SSL(1408F10B) ssl3_get_record: wrong version number

With OSX, I get:

?- http_open('https://aap.nl:80', In, [cert_verify_hook(cert_accept_any)]).
ERROR: SSL(140770FC) SSL23_GET_SERVER_HELLO: unknown protocol

You can get the OpenSSL version as follows:

?- current_prolog_flag(ssl_library_version, V).

In my case, it yields on Debian:

V = 'OpenSSL 1.1.0e  16 Feb 2017'.

and on OSX:

V = 'OpenSSL 1.0.2k  26 Jan 2017'.

As an aside, http/http_ssl_plugin is automatically loaded for HTTPS requests, there is no need to load it explicitly. As another aside, speaking about autoloading, I hope SWI-Prolog/packages-http#61 is a good idea?

@wouterbeek
Copy link

@triska Thanks for the info, the originally reported behavior was on my old OS, which was Ubuntu 17.04 (I don't know the OpenSSL version though).

I've just tested on my current OS: "OpenSSL 1.0.2g 1 Mar 2016" and Fedora 25, where I get:

?- http_open('https://aap.nl:80', In, [cert_verify_hook(cert_accept_any)]).
ERROR: SSL(140770FC) SSL23_GET_SERVER_HELLO: unknown protocol

@triska
Copy link
Member

triska commented Jun 13, 2017

As far as I can tell, this is about as good an error message as we can get from OpenSSL.

For comparison, here is Python 3 on Debian:

$ python3
Python 3.4.2 (default, Oct  8 2014, 10:45:20)
....
>>> import urllib.request
>>> urllib.request.urlopen("https://aap.nl:80")

Yielding:

ssl.SSLError: [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:600)

@wouterbeek
Copy link

@triska I agree that the messages you get on Debian and OSX, and the one I get on Fedora, are readable enough. The message I got earlier on Ubuntu is unreadable (ERROR: SSL(140770FC) func(119): reason(252)), but I don't know what caused that message to be so different.

Closing...

@JanWielemaker
Copy link
Member Author

I guess an older version of Ubuntu. I run 16.04 with SSL 1.02 and get reasonable errors.

@wouterbeek
Copy link

wouterbeek commented Jun 25, 2017

I'm reopening this one, because I still regularly run into problems with obscure OpenSSL error messages. Here's a simple example program:

:- use_module(library(error)).
:- use_module(library(http/http_open)).
:- use_module(library(ssl)).

uri_cert(Uri, Cert) :-
  setup_call_cleanup(
    http_open(Uri, In, [status_code(Status)]),
    (
      must_be(between(200,299), Status),
      load_certificate(In, Cert)
    ),
    close(In)
  ).

When I call this predicate I get the following SSL exception, which does not allow me to discover what is actually wrong / what I should fix:

?- uri_cert('https://aap.nl', Cert).
ERROR: SSL(0906D06C) func(109): reason(108)
ERROR: In:
ERROR:   [12] ssl:load_certificate(<stream>(0x16a7ff0),_17224)
ERROR:   [10] setup_call_catcher_cleanup(user:http_open('https://aap.nl',<stream>(0x16a7ff0),...),user:(...,...),_17252,user:close(<stream>(0x16a7ff0))) at /home/wbeek/lib/swipl/boot/init.pl:436
ERROR:    [7] <user>
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

I'm using OpenSSL 1.0.2k on Fedora 25. (Fedora 26 will be released in 2 weeks, it will come with OpenSSL 1.1, which hopefully has better error messages.)

@wouterbeek wouterbeek reopened this Jun 25, 2017
@triska
Copy link
Member

triska commented Jun 25, 2017

I can reproduce this unhelpful error message and will try to raise a better one.

One thing I can tell you right away regarding this error: load_certificate/2 only works if the stream itself is ready to receive a certificate from it.

This is of course not the case here, since you can verify with copy_stream_data(In, current_output) that the stream does not send a certificate at this point but rather, as expected, the decrypted content of the page.

To get the peer certificate of a TLS connection, please use ssl_peer_certificate/2. I have tried this in this case, and this raises a different error, and I will look into it. If you can reproduce the problem, please file this as a different issue so that we can discuss it there. Thank you!

@triska
Copy link
Member

triska commented Jun 25, 2017

For your current issue, the following works to obtain the certificate:

:- use_module(library(ssl)).
:- use_module(library(http/http_open)).

uri_cert(Uri, Cert) :-
  setup_call_cleanup(
    http_open(Uri, In, [status_code(Status),redirect(false)]),
    (
      must_be(between(200,301), Status),
      ssl_peer_certificate(In, Cert)
    ),
    close(In)
  ).

Note that I had to disable redirects, because this HTTPS server redirects the client to port 80, and from there no certificate is of course available.

With the code above, I get the certificate with:

?- uri_cert('https://aap.nl', Cert).

@triska
Copy link
Member

triska commented Jun 25, 2017

OK, since Jan thankfully fixed #107, I recommend the following code to obtain the certificate:

:- use_module(library(http/http_open)).

uri_cert(URI, Cert) :-
        setup_call_cleanup(http_open(URI, In, [status_code(Status)]),
                           (   ssl_peer_certificate(In, Cert),
                               must_be(between(200,299), Status) ),
                           close(In)).

This now works exactly as expected also for:

?- uri_cert('https://aap.nl', Cert).

Regarding the error message: If we mistakenly use load_certificate/2 instead of ssl_peer_certificate/2 in the code above, then I get with OpenSSL 1.1.0f:

?- uri_cert('https://aap.nl', Cert).
ERROR: SSL(0906D06C) PEM_read_bio: no start line

This is a nicer error, expressing quite clearly what is wrong here. Thus, please consider upgrading to a more recent OpenSSL version if you work a lot with TLS connections.

@wouterbeek
Copy link

@triska @JanWielemaker Thank you for looking into this. Now that I'm using OpenSSL 1.1.0f the error messages are clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants