The SSL interface allows you to use Secure Socket Layer (SSL) with Lisp objects of type socket-stream and async-io-state.
The interface is based on the OpenSSL code, and most of it is simply an FLI interface to OpenSSL functions. The main LispWorks specific code is the way OpenSSL is integrated with socket-stream and async-io-state.
The SSL interface is part of the "comm"
module, so to load it you evaluate
(require "comm")
Note:
In this section we assume that the current package uses the comm
package. That is, comm
package symbols may not be qualified explicitly.
At the time of writing, OpenSSL is available as shown in OpenSSL availability:
Installed by default on most 32-bit and 64-bit distributions |
|
32-bit and 64-bit libraries are available at |
|
Installed by default on Solaris 10. For other versions, see the freeware from Sun at |
After installing (with pkgadd) you need to put the shared libraries libcrypto.so
and libssl.o
on the loader path. By default these are installed in /usr/local/ssl/lib
.
Since OpenSSL is not a standard on all machines yet, the location of the library or libraries varies. By default, ensure-ssl loads libraries as shown in How LispWorks locates the OpenSSL libraries.
On machines where the path is unknown or is incorrect, you must set the path by calling
set-ssl-library-path
, or by passing the path as the library-path argument to ensure-ssl. In particular, if you want to use the Windows OpenSSL 1.1 libraries from the page that is mentioned in the table OpenSSL availability, then the library names have changed and you will have to set the path. With the download from that page from Septemter 2017, the names need to be:
There are three ways to make a socket-stream with SSL processing:
(make-instance 'socket-stream :ssl-ctx ...)
:ssl-ctx
keyword.These calls implicitly load the OpenSSL library and seed the Pseudo Random Number Generator (PRNG).
(open-tcp-stream some-url 443 :ssl-ctx t)
There are three ways to make an async-io-state with SSL processing:
:ssl-ctx
keyword.:ssl-ctx
keyword.These calls implicitly load the OpenSSL library and seed the Pseudo Random Number Generator (PRNG).
The keyword arguments :ssl-ctx
, :ssl-side
, :ctx-configure-callback
, :ssl-configure-callback
and :handshake-timeout
can be be passed to create and configure socket streams and async-io-states with SSL processing. The various interface calls for creating and configuring SSL streams and async-io-states accept these keyword arguments as shown in SSL configuration keywords.
|
|||||
(make-instance 'socket-stream ...)
and open-tcp-stream, when ssl-ctx is non-nil, call attach-ssl and pass it all the arguments. accept-tcp-connections-creating-async-io-states and create-async-io-state-and-connected-tcp-socket when ssl-ctx is non-nil attach the ssl similar to the way async-io-state-attach-ssl does.
:ssl-ctx
specifies that SSL should be used, and also specifies the SSL_CTX object to use. See the OpenSSL manual entry for SSL_CTX_new for details of making a SSL_CTX. The value of ssl-ctx can be:
Together with ssl-side, this symbol specifies which protocol to use. ssl-ctx can be one of:
1) t
or :default
, meaning use the default. Currently this is the same as :v23
.
2) One of :v2
, :v3
, :v23
or :tls-v1
. These are mapped to the SSLv2_*, SSLv3_*, SSLv23_*, TLSv1_* methods.
LispWorks makes a new SSL_CTX object and uses it and frees it when the stream or state is closed. The interface calls also make an SSL object, uses it and frees it when the stream or state is closed.
A foreign pointer of type
ssl-ctx-pointer
This corresponds to the C type SSL_CTX*. This is used and is not freed when the stream is closed. The interface calls also make an SSL object, use it and free it when the stream is closed. The foreign pointer maybe a result of a call to make-ssl-ctx, but it can also be a result of your code, provided that it points to a valid SSL_CTX and has the type
ssl-ctx-pointer
.
A foreign pointer of type
ssl-pointer
This corresponds to the C type SSL*. This specifies the SSL to use in the interface calls. This may be a result of a call to
ssl-new
. but can also be a result of your code, provided that it points to a valid SSL object and has the type
ssl-ctx-pointer
. The SSL is used and is not freed when the stream is closed.
When you pass a
ssl-ctx-pointer
or a
ssl-pointer
foreign pointer, these must have already been set up correctly and you are responsible for freeing them when they are no longer required.
:ssl-side
specifies which side the stream is. The value ssl-side can be one of :client
, :server
or :both
. open-tcp-stream and create-async-io-state-and-connected-tcp-socket do not take this keyword and always use :client
. For the other calls this argument defaults to :server
. The value of ssl-side is used in three cases:
ssl-ctx-pointer
, it checks that the side of ssl-ctx and ssl-side are not conflicting. If one is :client
and the other is :server
, they conflict and an error is signaled.:client
or :server
, LispWorks calls ssl-set-connect-state
or ssl-set-accept-state
respectively.
When a new SSL object is created, ssl-side is :client
and handshake-timeout is greater than 0, a handshake is performed immediately.
If ssl-ctx is of type
ssl-pointer
then ssl-side is ignored.
:ctx-configure-callback
specifies a callback, a function which takes a foreign pointer of type
ssl-ctx-pointer
. This is called immediately after a new SSL_CTX is created. If the value of ssl-ctx is not a symbol, ctx-configure-callback is ignored.
:ssl-configure-callback
specifies a callback, a function which takes a foreign pointer of type
ssl-pointer
. This is called immediately after a new SSL is created. If the value of ssl-ctx is a
ssl-pointer
, ssl-configure-callback is ignored.
When a handshake is performed immediately (ssl-side is :client
and ssl-ctx is not a
ssl-ctx-pointer
), handshake-timeout specifies the time in seconds to wait for the handshake to complete. If handshake-timeout is nil
(the default) then it waits forever. If the handshake fails or times out, it is an error situation: for the synchronous interface (stream) an error is signaled, and for the asynchronous interface the callback is called with an error indicator (see the specific functions for details). Note that handshake-timeout 0 (or negative) prevents the handshake.
In typical usage, you will create few
ssl-ctx-pointer
objects (maybe only one), configure them as appropriate for your application and the machine that it runs on, and then use one of these as ssl-ctx in all of your calls. If some connections need special configuration, you will use ssl-configure-callback to configure the
ssl-pointer
of this connection. Sometimes when you open a connection as a client it may be sufficient to pass a symbol for ssl-ctx. Passing an
ssl-pointer
as ssl-ctx is for special cases.
You can attach SSL to an existing socket-stream by calling attach-ssl on the stream. The socket-stream SSL keyword arguments are processed by attach-ssl as described in Keyword arguments for use with SSL.
Detach SSL from a socket-stream and shut down the SSL with detach-ssl.
For full descriptions see attach-ssl and detach-ssl.
You can attach SSL to an existing async-io-state by calling async-io-state-attach-ssl on the state, and detach it using
async-io-state-detach-ssl.
After an object (stream or state) has been detached, you can attach SSL to it again.
Detaching frees any automatically generated SSL objects in the same way that closing a stream or state does.
The SSL objects are attached to the socket-stream or async-io-state, rather that to the socket. Therefore if you want to move a socket to another object then you need to attach it again.
For example, if you have attached SSL to an async-io-state and then want to change to synchronous communication, you need to close the async-io-state by close-async-io-state with keep-alive true (effectively detach the SSL), and then call make-instance
with socket-stream with the socket plus SSL-CTX and any other necessary arguments.
To move the other way, from synchronous to asynchronous, use replace-socket-stream-socket with socket nil
to disconnect the socket from the stream (which effectively calls detach-ssl), call create-async-io-state with the socket, and then call async-io-state-attach-ssl on the new async-io-state.
The configuration interface contains mostly FLI function definitions that map directly to OpenSSL calls. See below for a list of those provided.
There are also some functions to make common cases simpler. These are read-dhparams,
pem-read,
set-ssl-ctx-options,
set-ssl-ctx-password-callback, and
set-ssl-ctx-dh.
The Lisp constants SSL_FILETYPE_ASN1
and SSL_FILETYPE_PEM
representing file types are provided.
This section describes the mapping between OpenSSL function names and the corresponding Lisp names.
For functions that map directly to OpenSSL calls, the convention is to create the LISP name from the C name by replacing underscores by hyphens.
The following functions map directly to the OpenSSL functions. Check the OpenSSL documentation for details.
Where an OpenSSL function takes an SSL* or SSL_CTX*, the Lisp function's argument must be a foreign pointer of type ssl-pointer, ssl-ctx-pointer or ssl-cipher-pointer. Where an OpenSSL function takes a char*
or int
, the Lisp function's argument must be a string or integer. Where an OpenSSL function takes other kinds of pointers, the Lisp function's argument must be a foreign pointer. The return values are integers or foreign pointers unless stated otherwise.
If an error occurs in one of these functions, an error code is returned. They do not signal any Common Lisp conditions and so you should check the return value carefully.
First value is number of bits the cipher actually uses. |
|
If you need OpenSSL functionality that is not provided here, you can define your own foreign functions via the LispWorks Foreign Language Interface.
If you do this, an important point to note is that on Microsoft Windows, the :calling-convention
must be :cdecl
(it defaults to :stdcall
). If using OpenSSL suddenly causes mysterious crashes, the calling-convention in your foreign function definitions is the first thing to check.
The C objects SSL and SSL_CTX are represented in LispWorks by foreign pointers with type ssl-pointer and ssl-ctx-pointer, which correspond to the C types SSL* and SSL_CTX*. These foreign types should be used for any foreign function that takes or returns these C types, and must be used when passing a foreign pointer as the value of the :ssl-ctx
argument.
Making SSL objects is a way of getting access to them to perform configuration, but, especially in the case of the SSL_CTX, it is a useful way to avoid repeated calls to the configuration routines which may be time consuming. For example, if we have defined a function configure-a-ctx
, and we want to read once every 60 seconds from some URL, we can write:
(loop (with-open-stream
(str (comm:open-tcp-stream some-url 443 :ssl-ctx t
:ctx-configure-callback 'configure-a-ctx))
(read-something str))
(sleep 60))
This will cause configure-a-ctx
to be called each time. If it is expensive, we can call it only once by changing the code to:
(let ((ctx (comm:make-ssl-ctx :ssl-side :client)))
(configure-a-ctx ctx)
(loop (with-open-stream
(str (comm:open-tcp-stream some-url 443 :ssl-ctx ctx))
(read-something str))
(sleep 60))
(comm:destroy-ssl-ctx ctx))
The SSL objects could be made either by make-ssl-ctx or ssl-new or by user code that calls the C functions SSL_CTX_new and SSL_new.
destroy-ssl-ctx frees the SSL_CTX object. To free an SSL object you would call
destroy-ssl. See the manual entries for full descriptions of these functions.
Alternatively, the SSL objects can be obtained from a socket-stream by calling socket-stream-ssl or
socket-stream-ctx and from an async-io-state by calling
async-io-state-ssl or
async-io-state-ssl. You can also find the ssl-side value that was passed to the interface call that created the SSL objects by calling
socket-stream-ssl-side or
async-io-state-ssl-side.
All the functions that make a SSL_CTX first call ensure-ssl, so normally you do not need to initialize the library. If your code makes a SSL_CTX itself (that is, not by calling any of the LispWorks interface functions), it needs to initialize the library first. Normally that should be done by an explicit call to ensure-ssl, which loads the SSL library and calls SSL_library_init and SSL_load_error_strings, and also does some LispWorks specific initializations. If your code must do the initialization, ensure-ssl should still be called with the argument
:already-done t
, which tells it that the library is already loaded and initialized.
If there are errors inside SSL, LispWorks will signal an error of type
ssl-condition
, which is a subclass of
socket-error
.
The condition can be one of the types
ssl-x509-lookup
,
ssl-closed
,
ssl-error
and
ssl-failure
. See the manual pages for details of these condition classes.
LispWorks User Guide and Reference Manual - 20 Sep 2017