1/* Part of SWI-Prolog 2 3 Author: Jan van der Steen, Matt Lilley and Jan Wielemaker, 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2004-2020, SWI-Prolog Foundation 7 VU University Amsterdam 8 CWI, Amsterdam 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(ssl, 38 [ certificate_field/2, % +Certificate, ?Field 39 load_certificate/2, % +Stream, -Certificate 40 load_private_key/3, % +Stream, +Password, -Key 41 load_public_key/2, % +Stream, -Key 42 load_crl/2, % +Stream, -Crl 43 write_certificate/3, % +Stream, -X509, +Options 44 system_root_certificates/1, % -List 45 cert_accept_any/5, % +SSL, +ProblemCertificate, 46 % +AllCertificates, +FirstCertificate, 47 % +Error 48 same_certificate/2, % +CertificateA, +CertificateB 49 ssl_context/3, % +Role, -Config, :Options 50 ssl_add_certificate_key/4, % +Config, +Cert, +Key, -Config 51 ssl_set_options/3, % +Config0, -Config, +Options 52 ssl_property/2, % +Config, ?Property 53 ssl_negotiate/5, % +Config, +PlainRead, +PlainWrite, 54 % -SSLRead, -SSLWrite 55 ssl_peer_certificate/2, % +Stream, -Certificate 56 ssl_peer_certificate_chain/2, % +Stream, -Certificates 57 ssl_session/2, % +Stream, -Session 58 ssl_secure_ciphers/1, % -Ciphers, 59 verify_certificate/3, % +X509, +AuxiliaryCertificates, +TrustedCertificates 60 verify_certificate_issuer/2, % +Certificate, +IssuerCertificate 61 62 ssl_upgrade_legacy_options/2 % +OptionsIn, -OptionsOut 63 ]). 64:- autoload(library(option),[select_option/4,select_option/3]). 65:- use_module(library(settings),[setting/4,setting/2]). 66 67:- use_module(library(crypto), []). % force initialization of libcrypto 68 69:- use_foreign_library(foreign(ssl4pl)). 70 71:- meta_predicate 72 ssl_context( , , ), 73 ssl_set_options( , , ). 74 75:- predicate_options(ssl_context/3, 3, 76 [ host(atom), 77 port(integer), 78 cacerts(list), 79 certificate_file(atom), 80 key_file(atom), 81 certificate_key_pairs(any), 82 password(any), 83 cipher_list(any), 84 ecdh_curve(any), 85 pem_password_hook(callable), 86 cacert_file(any), 87 crl(any), 88 require_crl(boolean), 89 cert_verify_hook(callable), 90 peer_cert(boolean), 91 close_parent(boolean), 92 close_notify(boolean), 93 sni_hook(callable), 94 alpn_protocols(any), 95 alpn_protocol_hook(callable) 96 ]).
123:- setting(secure_ciphers, atom,
124 'EECDH+AESGCM:EDH+AESGCM:EECDH+AES256:EDH+AES256:EECDH+CHACHA20:EDH+CHACHA20',
125 "Default set of ciphers considered secure").
client
. Otherwise,
certificate verification may fail when negotiating a
secure connection.key_file(+FileName)
option. A server must have at
least one certificate before clients can connect. A client
must have a certificate only if the server demands the client
to identify itself with a client certificate using the
peer_cert(true)
option. If a certificate is provided, it is
necessary to also provide a matching private key via the
key_file/1 option. To configure multiple certificates, use the
option certificate_key_pairs/1 instead. Alternatively, use
ssl_add_certificate_key/4 to add certificates and keys to an
existing context.password(+Text)
or
pem_password_hook(:Goal)
option.call(Goal, +SSL, -Password)
and typically unifies
Password with a string containing the password.require_crl(true)
and
provide neither of these options, verification will necessarily
failrequire_crl(true)
if you want CRLs to actually be checked by OpenSSL.system(root_certificates)
uses a list of
trusted root certificates as provided by the OS. See
system_root_certificates/1 for details.file(Filename)
: A file containing one or more PEM-encoded
certificatescertificate(Blob)
: A certificate blobsystem(root_certificates)
: A special term which refers to
the certificates trusted by the host OS.Additional verification of the peer certificate as well as accepting certificates that are not trusted by the given set can be realised using the hook cert_verify_hook(:Goal).
call(Goal, +SSL, +ProblemCertificate, +AllCertificates, +FirstCertificate, +Error)
In case the certificate was verified by one of the provided
certifications from the cacert_file
option, Error is unified
with the atom verified
. Otherwise it contains the error
string passed from OpenSSL. Access will be granted iff the
predicate succeeds. See load_certificate/2 for a description
of the certificate terms. See cert_accept_any/5 for a dummy
implementation that accepts any certificate.
prime256v1
is used by default.true
, close the raw streams if the SSL streams are closed.
Default is false
.true
(default is false
), the server sends TLS
close_notify
when closing the connection. In addition,
this mitigates truncation attacks for both client and
server role: If EOF is encountered without having received a
TLS shutdown, an exception is raised. Well-designed
protocols are self-terminating, and this attack is therefore
very rarely a concern.sslv3
, tlsv1
, tlsv1_1
, tlsv1_2
and
tlsv1_3
. This option is available with OpenSSL 1.1.0 and
later, and should be used instead of disable_ssl_methods/1.sslv3
, tlsv1
, tlsv1_1
, tlsv1_2
and
tlsv1_3
. This option is available with OpenSSL 1.1.0 and
later, and should be used instead of disable_ssl_methods/1.sslv2
, sslv3
, sslv23
,
tlsv1
, tlsv1_1
and tlsv1_2
. This option is deprecated
starting with OpenSSL 1.1.0. Use min_protocol_version/1 and
max_protocol_version/1 instead.disable_ssl_methods
above.
Using this option is discouraged. When using OpenSSL 1.1.0
or later, this option is ignored, and a version-flexible method
is used to negotiate the connection. Using version-specific
methods is deprecated in recent OpenSSL versions, and this
option will become obsolete and ignored in the future.call(Goal, +SSL0, +HostName, -SSL)
Given the current context SSL0, and the host name of the client request, the predicate computes SSL which is used as the context for negotiating the connection. The first solution is used. If the predicate fails, the default options are used, which are those of the encompassing ssl_context/3 call. In that case, if no default certificate and key are specified, the client connection is rejected.
===
call(Goal, +SSLCtx0, +ListOfClientProtocols, -SSLCtx1, -SelectedProtocol)
===
If this option is unset and the alpn_protocols/1 option is set, then the first common protocol between client & server will be selected.
319ssl_context(Role, SSL, Module:Options) :-
320 select_option(ssl_method(Method), Options, O1, sslv23),
321 ssl_upgrade_legacy_options(O1, O2),
322 ( select_option(cacerts(_), O2, _)
323 -> O3 = O2
324 ; O3 = [cacerts([system(root_certificates)])|O2]
325 ),
326 '_ssl_context'(Role, SSL, Module:O3, Method).
cacert_file(Spec)
option and map it to the new
cacerts(+List)
option.333ssl_upgrade_legacy_options(O1, O4) :- 334 select_option(cacert_file(CACertFile), O1, O2), 335 !, 336 print_message(warning, deprecated(ssl_option(cacert_file(CACertFile)))), 337 ( atom(CACertFile) 338 -> Term = file(CACertFile) 339 ; Term = CACertFile % e.g., system(root_certificates) 340 ), 341 select_option(cacerts(CACerts), O2, O3, []), 342 ssl_upgrade_legacy_options([cacerts([Term|CACerts])|O3], O4). 343ssl_upgrade_legacy_options(Options, Options).
This predicate allows dual-stack RSA and ECDSA servers (for example), and is an alternative for using the certificate_key_pairs/1 option. As of OpenSSL 1.0.2, multiple certificate types with completely independent certificate chains are supported. If a certificate of the same type is added repeatedly to a context, the result is undefined. Currently, up to 12 additional certificates of different types are admissible.
361ssl_add_certificate_key(SSL0, Cert, Key, SSL) :-
362 ssl_copy_context(SSL0, SSL),
363 '_ssl_add_certificate_key'(SSL, Cert, Key).
377ssl_set_options(SSL0, SSL, Options) :-
378 ssl_copy_context(SSL0, SSL),
379 '_ssl_set_options'(SSL, Options).
After a successful handshake and finishing the communication the
user must close SSLRead and SSLWrite, for example using
call_cleanup(close(SSLWrite), close(SSLRead))
. If the SSL
context (created with ssl_context/3 has the option
close_parent(true)
(default false
), closing SSLRead and
SSLWrite also closes the original PlainRead and PlainWrite
streams. Otherwise these must be closed explicitly by the user.
http_open(HTTPS_url, In, []), ssl_peer_certificate(In, Cert), memberchk(subject(Subject), Cert), memberchk('CN' = CommonName), Subject)
Note that the OpenSSL CA.pl
utility creates certificates that
have a human readable textual representation in front of the PEM
representation. You can use the following to skip to the
certificate if you know it is a PEM certificate:
skip_to_pem_cert(In) :- repeat, ( peek_char(In, '-') -> ! ; skip(In, 0'\n), at_end_of_stream(In), ! ).
revoked(+Serial, DateOfRevocation)
system(root_certificates)
. The list is obtained using an OS
specific process. The current implementation is as follows:
"ROOT"
certificates from the OS.system_cacert_filename
. The initial
value of this flag is operating system dependent. For
security reasons, the flag can only be set prior to using
the SSL library. For example:
:- use_module(library(ssl)). :- set_prolog_flag(system_cacert_filename, '/home/jan/ssl/ca-bundle.crt').
private_key(KeyTerm)
where KeyTerm is an rsa/8 term
representing an RSA key, or ec/3 for EC keys.public_key(KeyTerm)
where KeyTerm is an rsa/8 term
representing an RSA key, or ec/3 for EC keys.http_open('https:/...', In, [ cert_verify_hook(cert_accept_any) ])
system(root_certificates)
for TrustedCertificates.
604cert_accept_any(_SSL,
605 _ProblemCertificate, _AllCertificates, _FirstCertificate,
606 _Error).
Secure ciphers must guarantee forward secrecy, and must mitigate all known critical attacks. As of 2018, using these ciphers allows you to obtain grade A on https://www.ssllabs.com. For A+, you must also enable HTTP Strict Transport Security (HSTS) by sending a suitable header field in replies.
Note that obsolete ciphers must be disabled to reliably prevent protocol downgrade attacks.
The Ciphers list is read from the setting ssl:secure_ciphers
and
can be controlled using set_setting/2 and other predicates from
library(settings).
BEWARE: This list must be changed when attacks on these ciphers become known! Keep an eye on this setting and adapt it as necessary in the future.
630ssl_secure_ciphers(Cs) :- 631 setting(secure_ciphers, Cs). 632 633 634 /******************************* 635 * MESSAGES * 636 *******************************/ 637 638:- multifile 639 prolog:error_message//1, 640 prolog:deprecated//1. 641 642prologerror_message(ssl_error(ID, _Library, Function, Reason)) --> 643 [ 'SSL(~w) ~w: ~w'-[ID, Function, Reason] ]. 644prologdeprecated(ssl_option(cacert_file(CACertFile))) --> 645 [ 'SSL: cacert_file(~q) has need deprecated.'-[CACertFile], 646 'Please use the option cacerts(List) instead' 647 ]
Secure Socket Layer (SSL) library
An SSL server and client can be built with the (abstracted) predicate calls from the table below. The
tcp_
predicates are provided by library(socket). The predicate ssl_context/3 defines properties of the SSL connection, while ssl_negotiate/5 establishes the SSL connection based on the wire streams created by the TCP predicates and the context.The library is abstracted to communication over streams, and is not reliant on those streams being directly attached to sockets. The
tcp_
calls here are simply the most common way to use the library. Other two-way communication channels such as (named), pipes can just as easily be used.