%%
%%

%\bauthors{Landon Fuller}
%\title{Bacula TLS Additions}

\chapter{TLS}
\label{blb:TheChapter_TLS}
\index{TLS}

Written by Landon Fuller

\section{Introduction to TLS}
\index{TLS Introduction}
\index{Introduction!TLS}

This patch includes all the back-end code necessary to add complete TLS
data encryption support to Bacula.  In addition, support for TLS in
Console/Director communications has been added as a proof of concept.
Adding support for the remaining daemons will be straight-forward.
Supported features of this patchset include:

\begin{bitemize}
\item Client/Server TLS Requirement Negotiation
\item TLSv1 Connections with Server and Client Certificate
Validation
\item Forward Secrecy Support via Diffie-Hellman Ephemeral Keying
\end{bitemize}

This document will refer to both ``server'' and ``client'' contexts.  These
terms refer to the accepting and initiating peer, respectively.

Diffie-Hellman anonymous ciphers are not supported by this patchset.  The
use of DH anonymous ciphers increases the code complexity and places
explicit trust upon the two-way Cram-MD5 implementation.  Cram-MD5 is
subject to known plaintext attacks, and is should be considered
considerably less secure than PKI certificate-based authentication.

Appropriate autoconf macros have been added to detect and use OpenSSL. Two
additional preprocessor defines have been added: \emph{HAVE\_TLS} and
\emph{HAVE\_OPENSSL}.  All changes not specific to OpenSSL rely on
\emph{HAVE\_TLS}.  OpenSSL-specific code is constrained to
\emph{src/lib/tls.c} to facilitate the support of alternative TLS
implementations.

\section{New Configuration Directives}
\index{TLS Configuration Directives}
\index{Directives!TLS Configuration}

Additional configuration directives have been added to both the Console and
Director resources.  These new directives are defined as follows:

\begin{bitemize}
\item \underline{TLS Enable} \emph{(yes/no)}
Enable TLS support.

\item \underline{TLS Require} \emph{(yes/no)}
Require TLS connections.

\item \underline{TLS Certificate} \emph{(path)}
Path to PEM encoded TLS certificate.  Used as either a client or server
certificate.

\item \underline{TLS Key} \emph{(path)}
Path to PEM encoded TLS private key.  Must correspond with the TLS
certificate.

\item \underline{TLS Verify Peer} \emph{(yes/no)}
Verify peer certificate.  Instructs server to request and verify the
client's x509 certificate.  Any client certificate signed by a known-CA
will be accepted unless the TLS Allowed CN configuration directive is used.
Not valid in a client context.

\item \underline{TLS Allowed CN} \emph{(string list)}
Common name attribute of allowed peer certificates.  If directive is
specified, all client certificates will be verified against this list.
This directive may be specified more than once.  Not valid in a client
context.

\item \underline{TLS CA Certificate File} \emph{(path)}
Path to PEM encoded TLS CA certificate(s).  Multiple certificates are
permitted in the file.  One of \emph{TLS CA Certificate File} or \emph{TLS
CA Certificate Dir} are required in a server context if \underline{TLS
Verify Peer} is also specified, and are always required in a client
context.

\item \underline{TLS CA Certificate Dir} \emph{(path)}
Path to TLS CA certificate directory.  In the current implementation,
certificates must be stored PEM encoded with OpenSSL-compatible hashes.
One of \emph{TLS CA Certificate File} or \emph{TLS CA Certificate Dir} are
required in a server context if \emph{TLS Verify Peer} is also specified,
and are always required in a client context.

\item \underline{TLS DH File} \emph{(path)}
Path to PEM encoded Diffie-Hellman parameter file.  If this directive is
specified, DH ephemeral keying will be enabled, allowing for forward
secrecy of communications.  This directive is only valid within a server
context.  To generate the parameter file, you may use openssl:
\footnotesize
\begin{bVerbatim}
openssl dhparam -out dh1024.pem -5 1024
\end{bVerbatim}
\normalsize
\end{bitemize}

\section{TLS API Implementation}
\index{TLS API Implimentation}
\index{API Implimentation!TLS}

To facilitate the use of additional TLS libraries, all OpenSSL-specific
code has been implemented within \emph{src/lib/tls.c}.  In turn, a generic
TLS API is exported.

\subsection{Library Initialization and Cleanup}
\index{Library Initialization and Cleanup}
\index{Initialization and Cleanup!Library}

\footnotesize
\begin{bVerbatim}
int init_tls (void);
\end{bVerbatim}
\normalsize

Performs TLS library initialization, including seeding of the PRNG. PRNG
seeding has not yet been implemented for win32.

\footnotesize
\begin{bVerbatim}
int cleanup_tls (void);
\end{bVerbatim}
\normalsize

Performs TLS library cleanup.

\subsection{Manipulating TLS Contexts}
\index{TLS Context Manipulation}
\index{Contexts!Manipulating TLS}

\footnotesize
\begin{bVerbatim}
TLS_CONTEXT  *new_tls_context (const char *ca_certfile,
        const char *ca_certdir, const char *certfile,
        const char *keyfile, const char *dhfile, bool verify_peer);
\end{bVerbatim}
\normalsize

Allocates and initalizes a new opaque \emph{TLS\_CONTEXT} structure.  The
\emph{TLS\_CONTEXT} structure maintains default TLS settings from which
\emph{TLS\_CONNECTION} structures are instantiated.  In the future the
\emph{TLS\_CONTEXT} structure may be used to maintain the TLS session
cache.  \emph{ca\_certfile} and \emph{ca\_certdir} arguments are used to
initialize the CA verification stores.  The \emph{certfile} and
\emph{keyfile} arguments are used to initialize the local certificate and
private key.  If \emph{dhfile} is non-NULL, it is used to initialize
Diffie-Hellman ephemeral keying.  If \emph{verify\_peer} is \emph{true} ,
client certificate validation is enabled.

\footnotesize
\begin{bVerbatim}
void free_tls_context (TLS_CONTEXT *ctx);
\end{bVerbatim}
\normalsize

Deallocated a previously allocated \emph{TLS\_CONTEXT} structure.

\subsection{Performing Post-Connection Verification}
\index{TLS Post-Connection Verification}
\index{Verification!TLS Post-Connection}

\footnotesize
\begin{bVerbatim}
bool tls_postconnect_verify_host (TLS_CONNECTION *tls, const char *host);
\end{bVerbatim}
\normalsize

Performs post-connection verification of the peer-supplied x509
certificate.  Checks whether the \emph{subjectAltName} and
\emph{commonName} attributes match the supplied \emph{host} string.
Returns \emph{true} if there is a match, \emph{false} otherwise.

\footnotesize
\begin{bVerbatim}
bool tls_postconnect_verify_cn (TLS_CONNECTION *tls, alist *verify_list);
\end{bVerbatim}
\normalsize

Performs post-connection verification of the peer-supplied x509
certificate.  Checks whether the \emph{commonName} attribute matches any
strings supplied via the \emph{verify\_list} parameter.  Returns
\emph{true} if there is a match, \emph{false} otherwise.

\subsection{Manipulating TLS Connections}
\index{TLS Connection Manipulation}
\index{Connections!Manipulating TLS}

\footnotesize
\begin{bVerbatim}
TLS_CONNECTION *new_tls_connection (TLS_CONTEXT *ctx, int fd);
\end{bVerbatim}
\normalsize

Allocates and initializes a new \emph{TLS\_CONNECTION} structure with
context \emph{ctx} and file descriptor \emph{fd}.

\footnotesize
\begin{bVerbatim}
void free_tls_connection (TLS_CONNECTION *tls);
\end{bVerbatim}
\normalsize

Deallocates memory associated with the \emph{tls} structure.

\footnotesize
\begin{bVerbatim}
bool tls_bsock_connect (BSOCK *bsock);
\end{bVerbatim}
\normalsize

Negotiates a a TLS client connection via \emph{bsock}.  Returns \emph{true}
if successful, \emph{false} otherwise.  Will fail if there is a TLS
protocol error or an invalid certificate is presented

\footnotesize
\begin{bVerbatim}
bool tls_bsock_accept (BSOCK *bsock);
\end{bVerbatim}
\normalsize

Accepts a TLS client connection via \emph{bsock}.  Returns \emph{true} if
successful, \emph{false} otherwise.  Will fail if there is a TLS protocol
error or an invalid certificate is presented.

\footnotesize
\begin{bVerbatim}
bool tls_bsock_shutdown (BSOCK *bsock);
\end{bVerbatim}
\normalsize

Issues a blocking TLS shutdown request to the peer via \emph{bsock}. This function may not wait for the peer's reply.

\footnotesize
\begin{bVerbatim}
int tls_bsock_writen (BSOCK *bsock, char *ptr, int32_t nbytes);
\end{bVerbatim}
\normalsize

Writes \emph{nbytes} from \emph{ptr} via the \emph{TLS\_CONNECTION}
associated with \emph{bsock}.  Due to OpenSSL's handling of \emph{EINTR},
\emph{bsock} is set non-blocking at the start of the function, and restored
to its original blocking state before the function returns.  Less than
\emph{nbytes} may be written if an error occurs.  The actual number of
bytes written will be returned.

\footnotesize
\begin{bVerbatim}
int tls_bsock_readn (BSOCK *bsock, char *ptr, int32_t nbytes);
\end{bVerbatim}
\normalsize

Reads \emph{nbytes} from the \emph{TLS\_CONNECTION} associated with
\emph{bsock} and stores the result in \emph{ptr}.  Due to OpenSSL's
handling of \emph{EINTR}, \emph{bsock} is set non-blocking at the start of
the function, and restored to its original blocking state before the
function returns.  Less than \emph{nbytes} may be read if an error occurs.
The actual number of bytes read will be returned.

\section{Bnet API Changes}
\index{Bnet API Changes}
\index{API Changes!Bnet}

A minimal number of changes were required in the Bnet socket API. The BSOCK
structure was expanded to include an associated TLS\_CONNECTION structure,
as well as a flag to designate the current blocking state of the socket.
The blocking state flag is required for win32, where it does not appear
possible to discern the current blocking state of a socket.

\subsection{Negotiating a TLS Connection}
\index{Negotiating a TLS Connection}
\index{TLS Connection!Negotiating}

\emph{bnet\_tls\_server()} and \emph{bnet\_tls\_client()} were both
implemented using the new TLS API as follows:

\footnotesize
\begin{bVerbatim}
int bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock);
\end{bVerbatim}
\normalsize

Negotiates a TLS session via \emph{bsock} using the settings from
\emph{ctx}.  Returns 1 if successful, 0 otherwise.

\footnotesize
\begin{bVerbatim}
int bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list);
\end{bVerbatim}
\normalsize

Accepts a TLS client session via \emph{bsock} using the settings from
\emph{ctx}.  If \emph{verify\_list} is non-NULL, it is passed to
\emph{tls\_postconnect\_verify\_cn()} for client certificate verification.

\subsection{Manipulating Socket Blocking State}
\index{Manipulating Socket Blocking State}
\index{Socket Blocking State!Manipulating}
\index{Blocking State!Socket!Manipulating}

Three functions were added for manipulating the blocking state of a socket
on both Win32 and Unix-like systems.  The Win32 code was written according
to the MSDN documentation, but has not been tested.

These functions are prototyped as follows:

\footnotesize
\begin{bVerbatim}
int bnet_set_nonblocking (BSOCK *bsock);
\end{bVerbatim}
\normalsize

Enables non-blocking I/O on the socket associated with \emph{bsock}.
Returns a copy of the socket flags prior to modification.

\footnotesize
\begin{bVerbatim}
int bnet_set_blocking (BSOCK *bsock);
\end{bVerbatim}
\normalsize

Enables blocking I/O on the socket associated with \emph{bsock}.  Returns a
copy of the socket flags prior to modification.

\footnotesize
\begin{bVerbatim}
void bnet_restore_blocking (BSOCK *bsock, int flags);
\end{bVerbatim}
\normalsize

Restores blocking or non-blocking IO setting on the socket associated with
\emph{bsock}.  The \emph{flags} argument must be the return value of either
\emph{bnet\_set\_blocking()} or \emph{bnet\_restore\_blocking()}.

\pagebreak

\section{Authentication Negotiation}
\index{Authentication Negotiation}
\index{Negotiation!TLS Authentication}

Backwards compatibility with the existing SSL negotiation hooks implemented
in src/lib/cram-md5.c have been maintained.  The
\emph{cram\_md5\_get\_auth()} function has been modified to accept an
integer pointer argument, tls\_remote\_need.  The TLS requirement
advertised by the remote host is returned via this pointer.

After exchanging cram-md5 authentication and TLS requirements, both the
client and server independently decide whether to continue:

\footnotesize
\begin{bVerbatim}
if (!cram_md5_get_auth(dir, password, &tls_remote_need) ||
        !cram_md5_auth(dir, password, tls_local_need)) {
[snip]
/* Verify that the remote host is willing to meet our TLS requirements */
if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK &&
        tls_remote_need != BNET_TLS_OK) {
   sendit(_("Authorization problem:"
            " Remote server did not advertise required TLS support.\n"));
   auth_success = false;
   goto auth_done;
}

/* Verify that we are willing to meet the remote host's requirements */
if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK &&
        tls_remote_need != BNET_TLS_OK) {
   sendit(_("Authorization problem:"
            " Remote server requires TLS.\n"));
   auth_success = false;
   goto auth_done;
}
\end{bVerbatim}
\normalsize
