summaryrefslogtreecommitdiff
path: root/rpcapd/rpcapd.c
diff options
context:
space:
mode:
authorCedric Cellier <rixed@happyleptic.org>2018-05-25 19:22:47 +0200
committerCedric Cellier <rixed@happyleptic.org>2018-09-13 08:26:46 +0200
commit9ba5495a8a6c63debb66eee82c153dbf02226c90 (patch)
treec56ebff598dc3d4cad448792212ca145dbde3edb /rpcapd/rpcapd.c
parentb70207e3d95765e2e49af4af1e79b43c4a77d02f (diff)
downloadlibpcap-9ba5495a8a6c63debb66eee82c153dbf02226c90.tar.gz
TLS for rpcap: also encrypt the control socket
This patch also encode the control sockets in adition to the data socket. Clients performs a TLS handshake when the scheme is rpcaps:// rather than rpcap://. Both active and passive modes are supported, but transfert via UDP is not (yet) supported (the lib returns an error in that case). I did some adaptation to the windows code but couldn't tested so for all I know it may not even compile. Also tried to fix the indentation.
Diffstat (limited to 'rpcapd/rpcapd.c')
-rw-r--r--rpcapd/rpcapd.c155
1 files changed, 119 insertions, 36 deletions
diff --git a/rpcapd/rpcapd.c b/rpcapd/rpcapd.c
index 12a71ab4..76e676c5 100644
--- a/rpcapd/rpcapd.c
+++ b/rpcapd/rpcapd.c
@@ -90,6 +90,7 @@ static HANDLE state_change_event; //!< event to signal that a state change shou
#endif
static volatile sig_atomic_t shutdown_server; //!< '1' if the server is to shut down
static volatile sig_atomic_t reread_config; //!< '1' if the server is to re-read its configuration
+static int uses_ssl; //!< '1' to use TLS over the data socket
extern char *optarg; // for getopt()
@@ -148,6 +149,11 @@ static void printusage(void)
#ifndef _WIN32
" -i run in inetd mode (UNIX only)\n\n"
#endif
+#ifdef HAVE_OPENSSL
+ " -S encrypt all communication with SSL (implements rpcaps://)\n"
+ " -K <pem_file> uses the SSL private key in this file (default: key.pem)\n"
+ " -C <pem_file> uses the certificate from this file (default: cert.pem)\n"
+#endif
" -s <config_file> save the current configuration to file\n\n"
" -f <config_file> load the current configuration from file; all switches\n"
" specified from the command line are ignored\n\n"
@@ -354,10 +360,11 @@ int main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN);
#endif
-#ifndef _WIN32
# ifdef HAVE_OPENSSL
if (uses_ssl) init_ssl_or_die(1);
# endif
+
+#ifndef _WIN32
if (isrunbyinetd)
{
//
@@ -407,13 +414,26 @@ int main(int argc, char *argv[])
close(devnull_fd);
}
+ SSL *ssl = NULL;
+#ifdef HAVE_OPENSSL
+ if (uses_ssl)
+ {
+ ssl = ssl_promotion_rw(1, sockctrl_in, sockctrl_out, errbuf, PCAP_ERRBUF_SIZE);
+ if (! ssl)
+ {
+ rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s",
+ errbuf);
+ exit(2);
+ }
+ }
+#endif
//
// Handle this client.
// This is passive mode, so we don't care whether we were
// told by the client to close.
//
- (void)daemon_serviceloop(sockctrl_in, sockctrl_out, 0,
- nullAuthAllowed);
+ (void)daemon_serviceloop(sockctrl_in, sockctrl_out, ssl, 0,
+ nullAuthAllowed, uses_ssl);
//
// Nothing more to do.
@@ -1059,6 +1079,22 @@ accept_connections(void)
sock_cleanup();
}
+#ifdef _WIN32
+//
+// A structure to hold the parameter to the windows thread
+// (on unix there is no need for this explicit copy since the
+// fork "inherits" the parent stack)
+//
+struct sock_copy {
+ SOCKET sockctrl;
+# ifdef HAVE_OPENSSL
+ SSL *ssl;
+# else
+ void *ssl;
+# endif
+};
+#endif
+
//
// Accept a connection and start a worker thread, on Windows, or a
// worker process, on UN*X, to handle the connection.
@@ -1071,10 +1107,12 @@ accept_connection(SOCKET listen_sock)
struct sockaddr_storage from; // generic sockaddr_storage variable
socklen_t fromlen; // keeps the length of the sockaddr_storage variable
+ SSL *ssl = NULL;
+
#ifdef _WIN32
HANDLE threadId; // handle for the subthread
u_long off = 0;
- SOCKET *sockctrl_temp;
+ struct sock_copy *sock_copy = NULL;
#else
pid_t pid;
#endif
@@ -1113,15 +1151,29 @@ accept_connection(SOCKET listen_sock)
return;
}
+#ifdef HAVE_OPENSSL
+ /* We have to upgrade to TLS as soon as possible so that the whole protocol
+ * goes through the encrypted tunnel, including early error messages. */
+ if (uses_ssl)
+ {
+ ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE);
+ if (! ssl)
+ {
+ rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s",
+ errbuf);
+ goto error;
+ }
+ }
+#endif
+
//
// We have a connection.
// Check whether the connecting host is among the ones allowed.
//
if (sock_check_hostlist(hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0)
{
- rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
+ goto error;
}
#ifdef _WIN32
@@ -1137,16 +1189,14 @@ accept_connection(SOCKET listen_sock)
if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR)
{
sock_geterror("ioctlsocket(FIONBIO): ", errbuf, PCAP_ERRBUF_SIZE);
- rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
+ goto error;
}
if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR)
{
sock_geterror("ioctlsocket(FIONBIO): ", errbuf, PCAP_ERRBUF_SIZE);
- rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL);
+ goto error;
}
//
@@ -1156,26 +1206,24 @@ accept_connection(SOCKET listen_sock)
// I guess we *could* just cast sockctrl to a void *, but that's
// a bit ugly.
//
- sockctrl_temp = (SOCKET *)malloc(sizeof (SOCKET));
- if (sockctrl_temp == NULL)
+ sock_copy = malloc(sizeof(*sock_copy));
+ if (sock_copy == NULL)
{
pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE,
errno, "malloc() failed");
- rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL);
+ goto error;
}
- *sockctrl_temp = sockctrl;
+ sock_copy->sockctrl = sockctrl;
+ sock_copy->ssl = NULL;
threadId = (HANDLE)_beginthreadex(NULL, 0,
- main_passive_serviceloop_thread, (void *) sockctrl_temp, 0, NULL);
+ main_passive_serviceloop_thread, (void *) sock_copy, 0, NULL);
if (threadId == 0)
{
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the child thread");
- rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- free(sockctrl_temp);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL);
+ goto error;
}
CloseHandle(threadId);
#else
@@ -1183,9 +1231,8 @@ accept_connection(SOCKET listen_sock)
if (pid == -1)
{
pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the child process");
- rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL);
- sock_close(sockctrl, NULL, 0);
- return;
+ rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL);
+ goto error;
}
if (pid == 0)
{
@@ -1216,8 +1263,8 @@ accept_connection(SOCKET listen_sock)
// This is passive mode, so we don't care whether we were
// told by the client to close.
//
- (void)daemon_serviceloop(sockctrl, sockctrl, 0,
- nullAuthAllowed);
+ (void)daemon_serviceloop(sockctrl, sockctrl, ssl, 0,
+ nullAuthAllowed, uses_ssl);
close(sockctrl);
@@ -1226,8 +1273,25 @@ accept_connection(SOCKET listen_sock)
// I am the parent
// Close the socket for this session (must be open only in the child)
+#ifdef HAVE_OPENSSL
+ if (ssl)
+ {
+ SSL_free(ssl);
+ ssl = NULL;
+ }
+#endif
closesocket(sockctrl);
#endif
+ return;
+
+error:
+#ifdef _WIN32
+ if (sock_copy) free(sock_copy);
+#endif
+#ifdef HAVE_OPENSSL
+ if (ssl) SSL_free(ssl); // Have to be done before closing soskctrl
+#endif
+ sock_close(sockctrl, NULL, 0);
}
/*!
@@ -1251,6 +1315,7 @@ main_active(void *ptr)
struct addrinfo hints; // temporary struct to keep settings needed to open the new socket
struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket
struct active_pars *activepars;
+ SSL *ssl = NULL;
activepars = (struct active_pars *) ptr;
@@ -1295,9 +1360,28 @@ main_active(void *ptr)
continue;
}
- activeclose = daemon_serviceloop(sockctrl, sockctrl, 1,
- nullAuthAllowed);
+#ifdef HAVE_OPENSSL
+ /* Even in active mode the other other end has to initiate the TLS handshake
+ * as we still are the server as far as TLS is concerned: */
+ if (uses_ssl)
+ {
+ ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE);
+ if (! ssl)
+ {
+ rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s",
+ errbuf);
+ sock_close(sockctrl, NULL, 0);
+ continue;
+ }
+ }
+#endif
+
+ activeclose = daemon_serviceloop(sockctrl, sockctrl, ssl, 1,
+ nullAuthAllowed, uses_ssl);
+#ifdef HAVE_OPENSSL
+ if (ssl) SSL_free(ssl);
+#endif
sock_close(sockctrl, NULL, 0);
// If the connection is closed by the user explicitely, don't try to connect to it again
@@ -1316,9 +1400,7 @@ main_active(void *ptr)
//
unsigned __stdcall main_passive_serviceloop_thread(void *ptr)
{
- SOCKET sockctrl;
-
- sockctrl = *((SOCKET *)ptr);
+ struct sock_copy sock = *(struct sock_copy *)ptr;
free(ptr);
//
@@ -1326,9 +1408,10 @@ unsigned __stdcall main_passive_serviceloop_thread(void *ptr)
// This is passive mode, so we don't care whether we were
// told by the client to close.
//
- (void)daemon_serviceloop(sockctrl, sockctrl, 0, nullAuthAllowed);
+ (void)daemon_serviceloop(sock.sockctrl, sock.sockctrl, sock.ssl, 0,
+ nullAuthAllowed, uses_ssl);
- sock_close(sockctrl, NULL, 0);
+ sock_close(sock.sockctrl, NULL, 0);
return 0;
}