TLS Client Sockets

Cleansocks provides a basic TLS client socket as an extension to the basic socket. Presently, there is no support for TLS servers, but that's on the to-do list. Cleansocks uses the OpenSSL TLS library. Note that the cleansocks interface makes no attempt to expose all the options and flexibility of OpenSSL.

To use the TLS client, #include <cleantlsc.h>. Then the following are available:

client_tls_socket ss(socket base [ , cleantlsc_flags flags ] [ , std::string hostname ] [ , bool secure_now ]);
client_tls_socket ss;    ss(socket base [ , cleantlsc_flags flags ] [ , std::string hostname ] [ , bool secure_now ]);

The square brackets indicate that arguments may be omitted, except that you may not omit all of the bracketed args. The order must be as shown.

This creates a client socket able to provide a secure client connection over a previously-connected socket base. If the secure_now flag true, or omitted, the the created object will immediately commence the TLS handshake as a client over the existing connection base, and the created ss object will offer a secured connetion immediately, or the construction will throw an exception. If secure_now is sent as false, the object is still created, but TLS is not started. The send and recv operations are simply forwarded to base, so the new object is basically just an alias for base. See the start_tls method for more information.

If the hostname parameter is provided, it enables two things:

  • The hostname is checked against the the certificate received from the server, to make sure it was issued for that host. If not, the method throws an exception.
  • The TLS SNI extension is enabled, which means the client will specifically request a certifcate for hostname, rather than just have the server send all which might be relevant. (Some servers, notably Google, tend to pout if clients don't use this extension.)
If hostname is not sent, neither of these is enabled. (The lack of a name check reduces the security provided by TLS.)

The flags parameter, if provided, modifies the behavior of the the object. It is the bitwise or of one or more of the following:

NO_CERT_CHECK Do not require the server to send a certificate, or attempt to verify any sent. If this is set, any following _OK flags are obviated.
SELF_OK A self-signed certificate is acceptable.
EXPIRED_OK An expired certificate is acceptable.
NO_CERT_OK It's okay if there is no certificate at all. Note: This does not make a bad or unverifiable certificate okay. If you want to allow that, use NO_CERT_CHECK.
NO_SNI Disable SNI. If the hostname parameter is not sent, SNI is disabled in any case.
NO_HOST_CHK Disable the certificate host name check. If the hostname parameter is not sent, no hostname check is performed in any case.

Note 1: the ss object retains a reference to base, which should not be closed or destroyed (probably not touched at all) while ss is in use.

Note 2: SNI is not available if the underlying openssl is earlier than version 1.0.0. (Those are scarce now days.)

send(clean_tls_socket &ss, const void *buf, int size);
send(clean_tls_socket &ss, const string str);
recv(clean_tls_socket &ss, void *buf, int size);
close(clean_tls_socket &ss);
The send, recv and close operations function as usual, except that there is no options parameter. The close method calls stop_tls and then closes the base socket.
ss.start_tls();
ss.stop_tls();
ss.tls_active();

A client_tls_socket object (ss in the description) need not be actually providing a secure channel. When it is not, the read, write and close operations are simply forwarded to the underlying base socket (base as sent to the constructor). These methods enable, disable, and report TLS secure operation. The tls_active method returns bool; the others return void. By default, the constructor calls the start_tls method, so new objects are immediately secured. This call is suppressed when the constructor is sent the secure_now flag as false. When implementing protocols that require plain text communication followed by secure on the same channel, this can be more convenient than waiting to create the secure layer until needed.

The start_tls method will throw an exception if TLS is already active, while stop_tls will just return if TLS is not active.

ss.start_tls_share_session(client_tls_socket &other);

This behaves like ordinary start_tls, except that it allows ss to avoid the full TLS handshake and share the negotiated state with that of other. Cleansocks will throw an exception if other is not in TLS mode. The method essentially offers the server the opportunity to reuse other's state for the connection by ss. The server may not agree, in which case a new connection state will be negotiated to secure ss.

Stopping TLS causes the connection state to be released, so stopping TLS on either ss or other will end any sharing, though start_tls_share_session may be used again.

What on earth might this be used for? When securing the old-fangled FTP protocol, it saves time to let the data connections share the state of the control connection.

client_tls_socket::authority_file(string path);

To check a certificate presented by the server, the client must have some root certificate to anchor the chain of trust. Normally, cleantlsc searches a list of standard locations for an appropriate file (see below). The provided installers make sure one exists, so this should work. But if you don't want to use the automatic search, you may use this static method to specify the file. Note that this call disables the search entirely; only the specified file is used.

Normally, you will not need to call authority_file. If you do, call it once, before creating any secure sockets. The setting will apply to all secure connections.

The cleantlsc object may not be assigned or copied.

Operations generally succeed or throw an exception if they cannot. The stop_tls does not, mostly because of its use when shutting down. This may be a bad idea, and may change. The tls_active() method does not throw.

The Authority Root Search Path

If the client_tls_socket::authority_file method is not used, the library will search through a list of locations and use the first one that exists.

On Unix/Linux (actually, anything that isn't Windows), the search is: ~/.cleansocks/ca.pem, /usr/local/etc/cleansocks/ca.pem, /etc/cleansocks/ca.pem, /etc/pki/tls/cert.pem, /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem. That is, first check a home location, local and general configuration locations, and finally finally some places where Red Hat distros put one. On Windows, the search follows a similar pattern, always looking for cleansocks\ca.pem in different places. First, in the user's home area, then in Program Files, which is where the installer will have put one, then on the main drive root. It tries variations of these based on the values of the environment variables HOME, HOMEPATH, HOMEDRIVE, ProgramFiles and SystemDrive, with reasonable defaults when not set. If you want to know the exact search list, consult the source in cleantlsc.cpp.

Note that the search path is partially controlled by environment settings: HOME on Linux, and several on Windows. This is not ideal from a security viewpoint: anyone who can manipulate the environment variables or file system where a cleantlsc program runs can cause it to accept arbitrary certificates. Using the authority_file method can eliminate the enironment dependence, but still leaves the possibility of attacking whatever file you do load.