]> The Tcpdump Group git mirrors - libpcap/commitdiff
Add SSL option for data socket of rpcap
authorCedric Cellier <[email protected]>
Thu, 24 May 2018 17:55:19 +0000 (19:55 +0200)
committerCedric Cellier <[email protected]>
Thu, 13 Sep 2018 06:25:06 +0000 (08:25 +0200)
When using rpcapd one may want the forwarded traffic to be encrypted.
When running rpcapd via initd it is relatively easy to add stunnel but
the client still have to implement TLS. Or one could also use an ssh
tunnel but it's a lot of setup. Ultimately, it is simpler than rpcap
protocol could run on SSL natively. So this patch adds a -S option to
rpcapd that will wrap the data socket into a TLS tunnel (in both passive
anbd active mode, as long as it's TCP not UDP).

The start capture message has an additional flag: ssl, asking the client
to initiate a TLS handshake once he is connected to the data socket.

This patch is not polished as I'm more interested in early opinions at
this stage. Please let me know what you think of the idea and its
implementation so far.

Proof of concept:

generate a private key, a self signed root cert:

$ openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 36500 -out cert.pem

then run rpcapd with option -S (ssl) and -K and -C:

$ rpcapd -n -S -K key.pem -C cert.pem

Once recompiled, tcpdump can attach to this rpcap:// service and the
traffic will be encrypted.

Makefile.in
configure
configure.ac
pcap-rpcap.c
rpcap-protocol.h
rpcapd/Makefile.in
rpcapd/daemon.c
rpcapd/rpcapd.c
sslutils.c [new file with mode: 0644]
sslutils.h [new file with mode: 0644]

index 5bf218868cfb4b28007e5af272ccdfc13b83397b..9f18c54328ac3dba8e425c5d7707f1b7a43b8c54 100644 (file)
@@ -361,6 +361,8 @@ EXTRA_DIST = \
        rpcapd/win32-svc.h \
        sockutils.c \
        sockutils.h \
+       sslutils.c \
+       sslutils.h \
        scanner.l \
        testprogs/CMakeLists.txt \
        testprogs/Makefile.in \
index 48c2f07836ea6cc6cfec7c441c70ded4329ae314..0928e11579b0e79fc76db5141b79188ad9bad0c2 100755 (executable)
--- a/configure
+++ b/configure
@@ -7883,7 +7883,7 @@ fi
 
 $as_echo "#define ENABLE_REMOTE /**/" >>confdefs.h
 
-       SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c"
+       SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c"
        BUILD_RPCAPD=build-rpcapd
        INSTALL_RPCAPD=install-rpcapd
        ;;
index 1a5310dba62fc0f581d7b4c87113e641424e3c04..aeb7cb8bf0d1faf9185f546329a3438b1d01e609 100644 (file)
@@ -1465,7 +1465,7 @@ yes)      AC_MSG_RESULT(yes)
 
        AC_DEFINE(ENABLE_REMOTE,,
            [Define to 1 if remote packet capture is to be supported])
-       SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c"
+       SSRC="$SSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c"
        BUILD_RPCAPD=build-rpcapd
        INSTALL_RPCAPD=install-rpcapd
        ;;
index a35eaf240669b500d1f263e00a6c160fbd8cdca6..48376e4034ca565bc1c71db03b5b897ef0451297 100644 (file)
 #include "rpcap-protocol.h"
 #include "pcap-rpcap.h"
 
+#ifdef HAVE_OPENSSL
+#include "sslutils.h"
+#endif
+
 /*
  * This file contains the pcap module for capturing from a remote machine's
  * interfaces using the RPCAP protocol.
@@ -111,6 +115,9 @@ struct pcap_rpcap {
 
        SOCKET rmt_sockctrl;            /* socket ID of the socket used for the control connection */
        SOCKET rmt_sockdata;            /* socket ID of the socket used for the data connection */
+#ifdef HAVE_OPENSSL
+       SSL *ssl;                       /* To transport rmt_sockdata via TLS */
+#endif
        int rmt_flags;                  /* we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_startcapture() */
        int rmt_capstarted;             /* 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture() */
        char *currentfilter;            /* Pointer to a buffer (allocated at run-time) that stores the current filter. Needed when flag PCAP_OPENFLAG_NOCAPTURE_RPCAP is turned on. */
@@ -163,7 +170,7 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 ver, uint8 request_type,
 static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf);
 static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf);
 static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf);
-static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size);
+static int rpcap_read_packet_msg(struct pcap_rpcap const *, pcap_t *p, size_t size);
 
 /****************************************************
  *                                                  *
@@ -390,27 +397,36 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch
        tv.tv_sec = p->opt.timeout / 1000;
        tv.tv_usec = (suseconds_t)((p->opt.timeout - tv.tv_sec * 1000) * 1000);
 
-       /* Watch out sockdata to see if it has input */
-       FD_ZERO(&rfds);
+#ifdef HAVE_OPENSSL
+       /* Check if we still have bytes available in the last decoded TLS record.
+        * If that's the case, we know SSL_read will not block. */
+       retval = pr->ssl && SSL_pending(pr->ssl) > 0;
+#endif
+       if (! retval)
+       {
+               /* Watch out sockdata to see if it has input */
+               FD_ZERO(&rfds);
 
-       /*
-        * 'fp->rmt_sockdata' has always to be set before calling the select(),
-        * since it is cleared by the select()
-        */
-       FD_SET(pr->rmt_sockdata, &rfds);
+               /*
+                * 'fp->rmt_sockdata' has always to be set before calling the select(),
+                * since it is cleared by the select()
+                */
+               FD_SET(pr->rmt_sockdata, &rfds);
 
-       retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv);
-       if (retval == -1)
-       {
-#ifndef _WIN32
-               if (errno == EINTR)
+               retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv);
+
+               if (retval == -1)
                {
-                       /* Interrupted. */
-                       return 0;
-               }
+#ifndef _WIN32
+                       if (errno == EINTR)
+                       {
+                               /* Interrupted. */
+                               return 0;
+                       }
 #endif
-               sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE);
-               return -1;
+                       sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE);
+                       return -1;
+               }
        }
 
        /* There is no data waiting, so return '0' */
@@ -472,8 +488,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch
                         * The size we should get is the size of the
                         * packet header.
                         */
-                       status = rpcap_read_packet_msg(pr->rmt_sockdata, p,
-                           sizeof(struct rpcap_header));
+                       status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header));
                        if (status == -1)
                        {
                                /* Network error. */
@@ -505,8 +520,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch
                            "Server sent us a message larger than the largest expected packet message");
                        return -1;
                }
-               status = rpcap_read_packet_msg(pr->rmt_sockdata, p,
-                   sizeof(struct rpcap_header) + plen);
+               status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header) + plen);
                if (status == -1)
                {
                        /* Network error. */
@@ -1038,6 +1052,12 @@ static int pcap_startcapture_remote(pcap_t *fp)
        int sockbufsize = 0;
        uint32 server_sockbufsize;
 
+#ifdef HAVE_OPENSSL
+       // Take the opportunity to clear pr->ssl before any goto error,
+       // as it seems pr->priv is not zeroed after its malloced.
+       pr->ssl = NULL;
+#endif
+
        /*
         * Let's check if sampling has been required.
         * If so, let's set it first
@@ -1247,6 +1267,13 @@ static int pcap_startcapture_remote(pcap_t *fp)
        /* Let's save the socket of the data connection */
        pr->rmt_sockdata = sockdata;
 
+#ifdef HAVE_OPENSSL
+       if (startcapreply.ssl) {
+               pr->ssl = ssl_promotion(0, sockdata, fp->errbuf, PCAP_ERRBUF_SIZE);
+               if (! pr->ssl) goto error;
+       }
+#endif
+
        /*
         * Set the size of the socket buffer for the data socket.
         * It has the same size as the local capture buffer used
@@ -1377,6 +1404,13 @@ error:
        (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL);
 
 error_nodiscard:
+#ifdef HAVE_OPENSSL
+       if (pr->ssl) {
+               SSL_free(pr->ssl);  // Have to be done before the socket is closed
+               pr->ssl = NULL;
+       }
+#endif
+
        if ((sockdata) && (sockdata != -1))             /* we can be here because sockdata said 'error' */
                sock_close(sockdata, NULL, 0);
 
@@ -3247,7 +3281,7 @@ static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf)
  * Read bytes into the pcap_t's buffer until we have the specified
  * number of bytes read or we get an error or interrupt indication.
  */
-static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size)
+static int rpcap_read_packet_msg(struct pcap_rpcap const *rp, pcap_t *p, size_t size)
 {
        u_char *bp;
        int cc;
@@ -3266,9 +3300,19 @@ static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size)
                 * We haven't read all of the packet header yet.
                 * Read what remains, which could be all of it.
                 */
-               bytes_read = sock_recv(sock, bp, size - cc,
-                   SOCK_RECEIVEALL_NO|SOCK_EOF_IS_ERROR, p->errbuf,
-                   PCAP_ERRBUF_SIZE);
+#ifdef HAVE_OPENSSL
+               if (rp->ssl)
+               {
+                       bytes_read = ssl_recv(rp->ssl, bp, size - cc, p->errbuf, PCAP_ERRBUF_SIZE);
+               }
+               else
+#endif
+               {
+                       bytes_read = sock_recv(rp->rmt_sockdata, bp, size - cc,
+                                       SOCK_RECEIVEALL_NO|SOCK_EOF_IS_ERROR, p->errbuf,
+                                       PCAP_ERRBUF_SIZE);
+               }
+
                if (bytes_read == -1)
                {
                        /*
index 7598a0aea8c2cb2c2eaeb36a72006cdc2a6ec45e..30c8a218ceba5e6af7322eb7754cac6df7abafbb 100644 (file)
@@ -316,6 +316,7 @@ struct rpcap_startcapreply
        int32 bufsize;          /* Size of the user buffer allocated by WinPcap; it can be different from the one we chose */
        uint16 portdata;        /* Network port on which the server is waiting at (passive mode only) */
        uint16 dummy;           /* Must be zero */
+       uint8 ssl:1;    /* Only known flag so far: client must connect with TLS */
 };
 
 /*
index d3e02c1a9fc3ad397e2946f16a5a7b6baa5e3d26..4ad94b8c1d7be4f36f61ceaa60043ea3006ede5e 100644 (file)
@@ -84,7 +84,7 @@ SRC = daemon.c \
        log-stderr.c \
        rpcapd.c
 
-OBJ =  $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o
+OBJ =  $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o ../sslutils.o
 PUBHDR =
 
 HDR = $(PUBHDR) log.h
index 4502e084a921ca8082728d5504ea6068fe42280f..b4ce20b3cd7b1c7b59b53fd3c8294cd03f13ba3d 100644 (file)
 #include "daemon.h"
 #include "log.h"
 
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include "sslutils.h"
+#endif
+
 #define RPCAP_TIMEOUT_INIT 90          /* Initial timeout for RPCAP connections (default: 90 sec) */
 #define RPCAP_TIMEOUT_RUNTIME 180      /* Run-time timeout for RPCAP connections (default: 3 min) */
 #define RPCAP_SUSPEND_WRONGAUTH 1      /* If the authentication is wrong, stops 1 sec before accepting a new auth message */
@@ -84,6 +89,9 @@ struct daemon_slpars
 struct session {
        SOCKET sockctrl_out;
        SOCKET sockdata;
+#ifdef HAVE_OPENSSL
+       SSL *ssl;               // optional SSL handler for the data transported in sockdata
+#endif
        uint8 protocol_version;
        pcap_t *fp;
        unsigned int TotCapt;
@@ -134,6 +142,8 @@ static void *daemon_thrdatamain(void *ptr);
 static int rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp);
 static int rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf);
 static int rpcapd_discard(SOCKET sock, uint32 len);
+static void session_close(struct session *);
+static int session_send_data(struct session const *, const char *, size_t, char *, size_t);
 
 int
 daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nullAuthAllowed)
@@ -865,12 +875,8 @@ end:
 #endif
                        threaddata.have_thread = 0;
                }
-               if (session->sockdata)
-               {
-                       sock_close(session->sockdata, NULL, 0);
-                       session->sockdata = 0;
-               }
-               pcap_close(session->fp);
+
+               session_close(session);
                free(session);
                session = NULL;
        }
@@ -1593,6 +1599,10 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h
                goto error;
        }
 
+#ifdef HAVE_OPENSSL
+       session->ssl = NULL;
+#endif
+
        // Open the selected device
        if ((session->fp = pcap_open_live(source,
                        ntohl(startcapreq.snaplen),
@@ -1725,6 +1735,10 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h
                startcapreply->portdata = htons(port);
        }
 
+#ifdef HAVE_OPENSSL
+       startcapreply->ssl = !!uses_ssl;
+#endif
+
        if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1)
        {
                // That failed; log a message and give up.
@@ -1754,6 +1768,22 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h
                sockdata = socktemp;
        }
 
+#ifdef HAVE_OPENSSL
+       if (uses_ssl)
+       {
+               /* In both active or passive cases, wait for the client to initiate the
+                * TLS handshake. Yes during that time the control socket will not be
+                * served, but the same was true from the above call to accept(). */
+               SSL *ssl = ssl_promotion(1, sockdata, errbuf, PCAP_ERRBUF_SIZE);
+               if (! ssl)
+               {
+                       rpcapd_log(LOGPRIO_ERROR, "TLS handshake failed: %s", errbuf);
+                       goto error;
+               }
+               session->ssl = ssl;
+       }
+#endif
+
        session->sockdata = sockdata;
 
        // Now we have to create a new thread to receive packets
@@ -1822,6 +1852,10 @@ error:
        {
                if (session->fp)
                        pcap_close(session->fp);
+#ifdef HAVE_OPENSSL
+               if (session->ssl)
+                       SSL_free(session->ssl);
+#endif
                free(session);
        }
 
@@ -1894,6 +1928,10 @@ fatal_error:
        {
                if (session->fp)
                        pcap_close(session->fp);
+#ifdef HAVE_OPENSSL
+               if (session->ssl)
+                       SSL_free(session->ssl);
+#endif
                free(session);
        }
 
@@ -1940,13 +1978,8 @@ daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struc
 #endif
                threaddata->have_thread = 0;
        }
-       if (session->sockdata)
-       {
-               sock_close(session->sockdata, NULL, 0);
-               session->sockdata = 0;
-       }
 
-       pcap_close(session->fp);
+  session_close(session);
 
        rpcap_createhdr(&header, pars->protocol_version,
            RPCAP_MSG_ENDCAP_REPLY, 0, 0);
@@ -2376,7 +2409,7 @@ daemon_thrdatamain(void *ptr)
                // Send the packet
                // If the client dropped the connection, don't report an
                // error, just quit.
-               status = sock_send(session->sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE);
+               status = session_send_data(session, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE);
                if (status < 0)
                {
                        if (status == -1)
@@ -2407,8 +2440,7 @@ daemon_thrdatamain(void *ptr)
        }
 
 error:
-       closesocket(session->sockdata);
-       session->sockdata = 0;
+  session_close(session);
 
        free(sendbuf);
 
@@ -2573,3 +2605,40 @@ rpcapd_discard(SOCKET sock, uint32 len)
        }
        return 0;
 }
+
+/*
+ * Close the socket associated with the session, the optional SSL handle,
+ * and the underlying packet capture handle.
+ */
+static void session_close(struct session *session)
+{
+#ifdef HAVE_OPENSSL
+       if (session->ssl)
+       {
+               SSL_free(session->ssl); // Must happen *before* the socket is closed
+               session->ssl = NULL;
+       }
+#endif
+
+       if (session->sockdata)
+       {
+               sock_close(session->sockdata, NULL, 0);
+               session->sockdata = 0;
+       }
+
+       pcap_close(session->fp);
+}
+
+static int session_send_data(struct session const *session, const char *buffer, size_t size, char *errbuf, size_t errbuflen)
+{
+#ifdef HAVE_OPENSSL
+       if (session->ssl)
+       {
+               return ssl_send(session->ssl, buffer, size, errbuf, errbuflen);
+       }
+       else
+#endif
+       {
+               return sock_send(session->sockdata, buffer, size, errbuf, errbuflen);
+       }
+}
index e8b3b1d6c98991c2594c8b8280cc0837dba7048c..12a71ab4a7a8d2cc7b40643bf206ccbbf132447b 100644 (file)
 #include "daemon.h"            // the true main() method of this daemon
 #include "log.h"
 
+#ifdef HAVE_OPENSSL
+#include "sslutils.h"
+#endif
+
 #ifdef _WIN32
   #include <process.h>         // for thread stuff
   #include "win32-svc.h"       // for Win32 service stuff
@@ -194,7 +198,15 @@ int main(int argc, char *argv[])
        mainhints.ai_socktype = SOCK_STREAM;
 
        // Getting the proper command line options
-       while ((retval = getopt(argc, argv, "b:dhip:4l:na:s:f:v")) != -1)
+#      ifdef HAVE_OPENSSL
+#              define SSL_CLOPTS  "SK:C:"
+#      else
+#              define SSL_CLOPTS ""
+#      endif
+
+#      define CLOPTS "b:dhip:4l:na:s:f:v" SSL_CLOPTS
+
+       while ((retval = getopt(argc, argv, CLOPTS)) != -1)
        {
                switch (retval)
                {
@@ -266,6 +278,17 @@ int main(int argc, char *argv[])
                        case 's':
                                strlcpy(savefile, optarg, MAX_LINE);
                                break;
+#ifdef HAVE_OPENSSL
+                       case 'S':
+                               uses_ssl = 1;
+                               break;
+                       case 'K':
+                               snprintf(ssl_keyfile, sizeof ssl_keyfile, "%s", optarg);
+                               break;
+                       case 'C':
+                               snprintf(ssl_certfile, sizeof ssl_certfile, "%s", optarg);
+                               break;
+#endif
                        case 'h':
                                printusage();
                                exit(0);
@@ -332,6 +355,9 @@ int main(int argc, char *argv[])
 #endif
 
 #ifndef _WIN32
+# ifdef HAVE_OPENSSL
+       if (uses_ssl) init_ssl_or_die(1);
+# endif
        if (isrunbyinetd)
        {
                //
diff --git a/sslutils.c b/sslutils.c
new file mode 100644 (file)
index 0000000..346466e
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2002 - 2003
+ * NetGroup, Politecnico di Torino (Italy)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Politecnico di Torino nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_OPENSSL
+#include <stdlib.h>
+
+#include "portability.h"
+#include "sslutils.h"
+#include "pcap/pcap.h"
+
+int uses_ssl; //!< '1' to use TLS over the data socket
+char ssl_keyfile[PATH_MAX]; //!< file containing the private key in PEM format
+char ssl_certfile[PATH_MAX];  //!< file containing the server's certificate in PEM format
+char ssl_rootfile[PATH_MAX];  //!< file containing the list of CAs trusted by the client
+// TODO: a way to set ssl_rootfile from the command line, or an envvar?
+
+// TODO: lock?
+static SSL_CTX *ctx;
+
+static int ssl_init_once(int is_server, char *errbuf, size_t errbuflen)
+{
+       static int inited = 0;
+       if (inited) return 0;
+
+       SSL_library_init();
+       SSL_load_error_strings();
+       OpenSSL_add_ssl_algorithms();
+
+       SSL_METHOD const *meth = SSLv23_method();
+       ctx = SSL_CTX_new(meth);
+       if (! ctx)
+       {
+               pcap_snprintf(errbuf, errbuflen, "Cannot get a new SSL context: %s", ERR_error_string(ERR_get_error(), NULL));
+               goto die;
+       }
+
+       SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+
+       if (is_server)
+       {
+               char const *certfile = ssl_certfile[0] ? ssl_certfile : "cert.pem";
+               if (1 != SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
+               {
+                       pcap_snprintf(errbuf, errbuflen, "Cannot read certificate file %s: %s", certfile, ERR_error_string(ERR_get_error(), NULL));
+                       goto die;
+               }
+
+               char const *keyfile = ssl_keyfile[0] ? ssl_keyfile : "key.pem";
+               if (1 != SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
+               {
+                       pcap_snprintf(errbuf, errbuflen, "Cannot read private key file %s: %s", keyfile, ERR_error_string(ERR_get_error(), NULL));
+                       goto die;
+               }
+       }
+       else
+       {
+               if (ssl_rootfile[0])
+               {
+                       if (! SSL_CTX_load_verify_locations(ctx, ssl_rootfile, 0))
+                       {
+                               pcap_snprintf(errbuf, errbuflen, "Cannot read CA list from %s", ssl_rootfile);
+                               goto die;
+                       }
+               }
+               else
+               {
+                       SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
+               }
+       }
+
+#if 0
+       if (! RAND_load_file(RANDOM, 1024*1024))
+       {
+               pcap_snprintf(errbuf, errbuflen, "Cannot init random");
+               goto die;
+       }
+
+       if (is_server)
+       {
+               SSL_CTX_set_session_id_context(ctx, (void *)&s_server_session_id_context, sizeof(s_server_session_id_context));
+       }
+#endif
+
+       inited = 1;
+       return 0;
+
+die:
+       return -1;
+}
+
+void init_ssl_or_die(int is_server)
+{
+       char errbuf[PCAP_ERRBUF_SIZE];
+
+       if (ssl_init_once(is_server, errbuf, sizeof errbuf) < 0)
+       {
+               fprintf(stderr, "%s\n", errbuf);
+               exit(3);
+       }
+}
+
+SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen)
+{
+       if (ssl_init_once(is_server, errbuf, errbuflen) < 0)
+       {
+               return NULL;
+       }
+
+       SSL *ssl = SSL_new(ctx);
+       SSL_set_fd(ssl, s);
+
+       if (is_server)
+       {
+               if (SSL_accept(ssl) <= 0)
+               {
+                       pcap_snprintf(errbuf, errbuflen, "SSL_accept(): %s",
+                                       ERR_error_string(ERR_get_error(), NULL));
+                       return NULL;
+               }
+       }
+       else
+       {
+               if (SSL_connect(ssl) <= 0)
+               {
+                       pcap_snprintf(errbuf, errbuflen, "SSL_connect(): %s",
+                                       ERR_error_string(ERR_get_error(), NULL));
+                       return NULL;
+               }
+       }
+
+       return ssl;
+}
+
+// Same return value as sock_send:
+// 0 on OK, -1 on error but closed connection (-2).
+int ssl_send(SSL *ssl, char const *buffer, size_t size, char *errbuf, size_t errbuflen)
+{
+       int status = SSL_write(ssl, buffer, size);
+       if (status > 0)
+       {
+               // "SSL_write() will only return with success, when the complete contents (...) has been written."
+               return 0;
+       }
+       else
+       {
+               int ssl_err = SSL_get_error(ssl, status); // TODO: does it pop the error?
+               if (ssl_err == SSL_ERROR_ZERO_RETURN)
+               {
+                       return -2;
+               }
+               else if (ssl_err == SSL_ERROR_SYSCALL)
+               {
+#ifndef _WIN32
+                       if (errno == ECONNRESET || errno == EPIPE) return -2;
+#endif
+               }
+               pcap_snprintf(errbuf, errbuflen, "SSL_write(): %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+}
+
+// Same return status as sock_recv(SOCK_EOF_IS_ERROR):
+// -3 for EINTR, -1 on error and EOF, or number of bytes read
+int ssl_recv(SSL *ssl, unsigned char *buffer, size_t size, char *errbuf, size_t errbuflen)
+{
+       int status = SSL_read(ssl, buffer, size);
+       if (status <= 0)
+       {
+               int ssl_err = SSL_get_error(ssl, status);
+               if (ssl_err == SSL_ERROR_ZERO_RETURN)
+               {
+                       pcap_snprintf(errbuf, errbuflen, "The other host terminated the connection.");
+                       return -1;
+               }
+               else if (ssl_err == SSL_ERROR_SYSCALL)
+               {
+#ifndef _WIN32
+                       if (errno == EINTR)
+                       {
+                               return -3;
+                       }
+                       else
+                       {
+                               pcap_snprintf(errbuf, errbuflen, "SSL_read(): %s",
+                                   ERR_error_string(ERR_get_error(), NULL));
+                               return -1;
+                       }
+#endif
+               }
+       }
+       return status;
+}
+
+#endif // HAVE_OPENSSL
diff --git a/sslutils.h b/sslutils.h
new file mode 100644 (file)
index 0000000..b2ae319
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2002 - 2003
+ * NetGroup, Politecnico di Torino (Italy)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Politecnico di Torino nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __SSLUTILS_H__
+#define __SSLUTILS_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include "sockutils.h"
+
+/*
+ * Configuration parameters
+ */
+
+extern int uses_ssl;
+extern char ssl_keyfile[PATH_MAX];
+extern char ssl_certfile[PATH_MAX];
+extern char ssl_rootfile[PATH_MAX];
+
+/*
+ * Utility functions
+ */
+
+void init_ssl_or_die(int is_server);
+SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen);
+int ssl_send(SSL *, char const *buffer, size_t size, char *errbuf, size_t errbuflen);
+int ssl_recv(SSL *, unsigned char *buffer, size_t size, char *errbuf, size_t errbuflen);
+
+#endif  // HAVE_OPENSSL
+
+#endif  // __SSLUTILS_H__