From: Cedric Cellier Date: Fri, 25 May 2018 17:22:47 +0000 (+0200) Subject: TLS for rpcap: also encrypt the control socket X-Git-Tag: libpcap-1.10-bp~677^2^2~3 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/9ba5495a8a6c63debb66eee82c153dbf02226c90 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. --- diff --git a/pcap-int.h b/pcap-int.h index 99e7af78..243e7831 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -526,6 +526,13 @@ int install_bpf_program(pcap_t *, struct bpf_program *); int pcap_strcasecmp(const char *, const char *); +/* + * Internal interface for pcap_parsesrcstr with the additional bit of + * information regarding SSL support (rpcap:// vs rpcaps://) + */ +int pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf); + #ifdef YYDEBUG extern int pcap_debug; #endif diff --git a/pcap-rpcap.c b/pcap-rpcap.c index 48376e40..83af582b 100644 --- a/pcap-rpcap.c +++ b/pcap-rpcap.c @@ -87,6 +87,7 @@ struct activehosts { struct sockaddr_storage host; SOCKET sockctrl; + SSL *ssl; uint8 protocol_version; struct activehosts *next; }; @@ -115,14 +116,13 @@ 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 + SSL *ctrl_ssl, *data_ssl; /* optional transport of rmt_sockctrl and rmt_sockdata via TLS */ 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. */ uint8 protocol_version; /* negotiated protocol version */ + uint8 uses_ssl; /* User asked for rpcaps scheme */ unsigned int TotNetDrops; /* keeps the number of packets that have been dropped by the network */ @@ -162,14 +162,14 @@ static void pcap_save_current_filter_rpcap(pcap_t *fp, const char *filter); static int pcap_setfilter_rpcap(pcap_t *fp, struct bpf_program *prog); static int pcap_setsampling_remote(pcap_t *fp); static int pcap_startcapture_remote(pcap_t *fp); -static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf); -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); -static int rpcap_process_msg_header(SOCKET sock, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); -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_sendauth(SOCKET sock, SSL *, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf); +static int rpcap_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_ver(SOCKET sock, SSL *, uint8 expected_ver, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_type(SOCKET sock, SSL *, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); +static int rpcap_process_msg_header(SOCKET sock, SSL *, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); +static int rpcap_recv(SOCKET sock, SSL *, void *buffer, size_t toread, uint32 *plen, char *errbuf); +static void rpcap_msg_err(SOCKET sockctrl, SSL *, uint32 plen, char *remote_errbuf); +static int rpcap_discard(SOCKET sock, SSL *, uint32 len, char *errbuf); static int rpcap_read_packet_msg(struct pcap_rpcap const *, pcap_t *p, size_t size); /**************************************************** @@ -383,7 +383,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch struct rpcap_pkthdr *net_pkt_header; /* header of the packet, from the message */ u_char *net_pkt_data; /* packet data from the message */ uint32 plen; - int retval; /* generic return value */ + int retval = 0; /* generic return value */ int msglen; /* Structures needed for the select() call */ @@ -400,7 +400,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch #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; + retval = pr->data_ssl && SSL_pending(pr->data_ssl) > 0; #endif if (! retval) { @@ -444,7 +444,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch if (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) { /* Read the entire message from the network */ - msglen = sock_recv_dgram(pr->rmt_sockdata, p->buffer, + msglen = sock_recv_dgram(pr->rmt_sockdata, pr->data_ssl, p->buffer, p->bufsize, p->errbuf, PCAP_ERRBUF_SIZE); if (msglen == -1) { @@ -549,7 +549,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->protocol_version, + if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->data_ssl, pr->protocol_version, header, p->errbuf) == -1) { return 0; /* Return 'no packets received' */ @@ -729,7 +729,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * we're closing this pcap_t, and have no place to report * the error. No reply is sent to this message. */ - (void)sock_send(pr->rmt_sockctrl, (char *)&header, + (void)sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0); } else @@ -742,7 +742,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no place to * report the error. */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0) == 0) { /* @@ -750,11 +750,11 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no * place to report the error. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_ENDCAP_REQ, &header, NULL) == 0) { - (void)rpcap_discard(pr->rmt_sockctrl, + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, NULL); } } @@ -762,14 +762,31 @@ static void pcap_cleanup_rpcap(pcap_t *fp) if (pr->rmt_sockdata) { +#ifdef HAVE_OPENSSL + if (pr->data_ssl) + { + SSL_free(pr->data_ssl); // Has to be done before the socket is closed + pr->data_ssl = NULL; + } +#endif sock_close(pr->rmt_sockdata, NULL, 0); pr->rmt_sockdata = 0; } if ((!active) && (pr->rmt_sockctrl)) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + SSL_free(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } pr->rmt_sockctrl = 0; + pr->ctrl_ssl = NULL; if (pr->currentfilter) { @@ -892,19 +909,19 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int RPCAP_MSG_STATS_REQ, 0, 0); /* Send the PCAP_STATS command */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), p->errbuf, PCAP_ERRBUF_SIZE) < 0) return NULL; /* Unrecoverable network error */ /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STATS_REQ, &header, p->errbuf) == -1) return NULL; /* Error */ plen = header.plen; /* Read the reply body */ - if (rpcap_recv(pr->rmt_sockctrl, (char *)&netstats, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&netstats, sizeof(struct rpcap_stats), &plen, p->errbuf) == -1) goto error; @@ -921,7 +938,7 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int #endif /* _WIN32 */ /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, p->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, p->errbuf) == -1) goto error_nodiscard; return ps; @@ -932,7 +949,7 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: return NULL; @@ -1052,11 +1069,9 @@ 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, + // Take the opportunity to clear pr->data_ssl before any goto error, // as it seems pr->priv is not zeroed after its malloced. - pr->ssl = NULL; -#endif + pr->data_ssl = NULL; /* * Let's check if sampling has been required. @@ -1197,18 +1212,18 @@ static int pcap_startcapture_remote(pcap_t *fp) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, &fp->fcode)) goto error_nodiscard; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STARTCAP_REQ, &header, fp->errbuf) == -1) goto error_nodiscard; plen = header.plen; - if (rpcap_recv(pr->rmt_sockctrl, (char *)&startcapreply, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&startcapreply, sizeof(struct rpcap_startcapreply), &plen, fp->errbuf) == -1) goto error; @@ -1268,9 +1283,10 @@ static int pcap_startcapture_remote(pcap_t *fp) 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; + if (pr->uses_ssl) + { + pr->data_ssl = ssl_promotion(0, sockdata, fp->errbuf, PCAP_ERRBUF_SIZE); + if (! pr->data_ssl) goto error; } #endif @@ -1361,7 +1377,7 @@ static int pcap_startcapture_remote(pcap_t *fp) fp->cc = 0; /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, fp->errbuf) == -1) goto error_nodiscard; /* @@ -1401,13 +1417,14 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, 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; + if (pr->data_ssl) + { + SSL_free(pr->data_ssl); // Have to be done before the socket is closed + pr->data_ssl = NULL; } #endif @@ -1415,7 +1432,16 @@ error_nodiscard: sock_close(sockdata, NULL, 0); if (!active) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + SSL_free(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } if (addrinfo != NULL) freeaddrinfo(addrinfo); @@ -1548,19 +1574,19 @@ static int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, prog)) return -1; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_UPDATEFILTER_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1793,19 +1819,19 @@ static int pcap_setsampling_remote(pcap_t *fp) sampling_pars->method = (uint8)fp->rmt_samp.method; sampling_pars->value = (uint16)htonl(fp->rmt_samp.value); - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_SETSAMPLING_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1840,7 +1866,7 @@ static int pcap_setsampling_remote(pcap_t *fp) * \return '0' if everything is fine, '-1' for an error. For errors, * an error message string is returned in the 'errbuf' variable. */ -static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) +static int rpcap_doauth(SOCKET sockctrl, SSL *ssl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) { int status; @@ -1850,7 +1876,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * First try with the maximum version number we support. */ *ver = RPCAP_MAX_VERSION; - status = rpcap_sendauth(sockctrl, ver, auth, errbuf); + status = rpcap_sendauth(sockctrl, ssl, ver, auth, errbuf); if (status == 0) { // @@ -1871,7 +1897,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * support that version. *ver has been set to that version; try * authenticating again with that version. */ - status = rpcap_sendauth(sockctrl, ver, auth, errbuf); + status = rpcap_sendauth(sockctrl, ssl, ver, auth, errbuf); if (status == 0) { // @@ -1921,7 +1947,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * support, or '-1' for other errors. For errors, an error message string * is returned in the 'errbuf' variable. */ -static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) +static int rpcap_sendauth(SOCKET sock, SSL *ssl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data that has to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -2019,14 +2045,14 @@ static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, ch rpauth->slen2 = htons(rpauth->slen2); } - if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) + if (sock_send(sock, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive the reply */ - if (rpcap_recv_msg_header(sock, &header, errbuf) == -1) + if (rpcap_recv_msg_header(sock, ssl, &header, errbuf) == -1) return -1; - if (rpcap_check_msg_type(sock, RPCAP_MSG_AUTH_REQ, &header, + if (rpcap_check_msg_type(sock, ssl, RPCAP_MSG_AUTH_REQ, &header, &errcode, errbuf) == -1) { /* Error message - or something else, which is a protocol error. */ @@ -2071,7 +2097,7 @@ static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, ch * * Discard the rest of it. */ - if (rpcap_discard(sock, header.plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header.plen, errbuf) == -1) return -1; return 0; @@ -2142,6 +2168,7 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim struct activehosts *activeconn; /* active connection, if there is one */ int error; /* '1' if rpcap_remoteact_getsock returned an error */ SOCKET sockctrl; + SSL *ssl = NULL; uint8 protocol_version; /* negotiated protocol version */ int active; uint32 plen; @@ -2190,7 +2217,7 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim * You must have a valid source string even if we're in active mode, because otherwise * the call to the following function will fail. */ - if (pcap_parsesrcstr(fp->opt.device, &retval, host, ctrlport, iface, errbuf) == -1) + if (pcap_parsesrcstr_ex(fp->opt.device, &retval, host, ctrlport, iface, &pr->uses_ssl, errbuf) == -1) { pcap_close(fp); return NULL; @@ -2217,12 +2244,14 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim activeconn = rpcap_remoteact_getsock(host, &error, errbuf); if (activeconn != NULL) { + active = 1; sockctrl = activeconn->sockctrl; + ssl = activeconn->ssl; protocol_version = activeconn->protocol_version; - active = 1; } else { + active = 0; // Must be set before jumping to error struct addrinfo hints; /* temp, needed to open a socket connection */ struct addrinfo *addrinfo; /* temp, needed to open a socket connection */ @@ -2271,13 +2300,24 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim /* addrinfo is no longer used */ freeaddrinfo(addrinfo); - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (pr->uses_ssl) { - sock_close(sockctrl, NULL, 0); +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + pcap_close(fp); + return NULL; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); pcap_close(fp); return NULL; +#endif } - active = 0; + + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) + goto error_nodiscard; } /* @@ -2295,28 +2335,29 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) goto error_nodiscard; - if (sock_send(sockctrl, sendbuf, sendbufidx, errbuf, + if (sock_send(sockctrl, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_OPEN_REQ, &header, errbuf) == -1) goto error_nodiscard; plen = header.plen; /* Read the reply body */ - if (rpcap_recv(sockctrl, (char *)&openreply, + if (rpcap_recv(sockctrl, ssl, (char *)&openreply, sizeof(struct rpcap_openreply), &plen, errbuf) == -1) goto error; /* Discard the rest of the message, if there is any. */ - if (rpcap_discard(sockctrl, plen, errbuf) == -1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == -1) goto error_nodiscard; /* Set proper fields into the pcap_t struct */ fp->linktype = ntohl(openreply.linktype); pr->rmt_sockctrl = sockctrl; + pr->ctrl_ssl = ssl; pr->protocol_version = protocol_version; pr->rmt_clientside = 1; @@ -2348,11 +2389,16 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(sockctrl, plen, NULL); + (void)rpcap_discard(sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Have to be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); + } pcap_close(fp); return NULL; @@ -2380,6 +2426,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** int error; /* '1' if rpcap_remoteact_getsock returned an error */ uint8 protocol_version; /* protocol version */ SOCKET sockctrl; /* socket descriptor of the control connection */ + SSL *ssl = NULL; /* optional SSL handler for sockctrl */ uint32 plen; struct rpcap_header header; /* structure that keeps the general header of the rpcap protocol */ int i, j; /* temp variables */ @@ -2387,6 +2434,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** int active; /* 'true' if we the other end-party is in active mode */ int type; char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE]; + uint8 uses_ssl; char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ pcap_if_t *lastdev; /* Last device in the pcap_if_t list */ pcap_if_t *dev; /* Device we're adding to the pcap_if_t list */ @@ -2396,7 +2444,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** lastdev = NULL; /* Retrieve the needed data for getting adapter list */ - if (pcap_parsesrcstr(source, &type, host, port, NULL, errbuf) == -1) + if (pcap_parsesrcstr_ex(source, &type, host, port, NULL, &uses_ssl, errbuf) == -1) return -1; /* Warning: this call can be the first one called by the user. */ @@ -2408,12 +2456,14 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** activeconn = rpcap_remoteact_getsock(host, &error, errbuf); if (activeconn != NULL) { + active = 1; sockctrl = activeconn->sockctrl; + ssl = activeconn->ssl; protocol_version = activeconn->protocol_version; - active = 1; } else { + active = 0; struct addrinfo hints; /* temp variable needed to resolve hostnames into to socket representation */ struct addrinfo *addrinfo; /* temp variable needed to resolve hostnames into to socket representation */ @@ -2455,24 +2505,42 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** freeaddrinfo(addrinfo); addrinfo = NULL; - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (uses_ssl) { +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + sock_close(sockctrl, NULL, 0); + return -1; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); + sock_close(sockctrl, NULL, 0);; + return -1; +#endif + } + + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) + { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Must be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); return -1; } - active = 0; } /* RPCAP findalldevs command */ rpcap_createhdr(&header, protocol_version, RPCAP_MSG_FINDALLIF_REQ, 0, 0); - if (sock_send(sockctrl, (char *)&header, sizeof(struct rpcap_header), + if (sock_send(sockctrl, ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_FINDALLIF_REQ, &header, errbuf) == -1) goto error_nodiscard; @@ -2492,7 +2560,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** tmpstring2[PCAP_BUF_SIZE] = 0; /* receive the findalldevs structure from remote host */ - if (rpcap_recv(sockctrl, (char *)&findalldevs_if, + if (rpcap_recv(sockctrl, ssl, (char *)&findalldevs_if, sizeof(struct rpcap_findalldevs_if), &plen, errbuf) == -1) goto error; @@ -2541,7 +2609,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** } /* Retrieve adapter name */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.namelen, &plen, errbuf) == -1) goto error; @@ -2574,7 +2642,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** } /* Retrieve adapter description */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.desclen, &plen, errbuf) == -1) goto error; @@ -2607,7 +2675,7 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** struct rpcap_findalldevs_ifaddr ifaddr; /* Retrieve the interface addresses */ - if (rpcap_recv(sockctrl, (char *)&ifaddr, + if (rpcap_recv(sockctrl, ssl, (char *)&ifaddr, sizeof(struct rpcap_findalldevs_ifaddr), &plen, errbuf) == -1) goto error; @@ -2681,13 +2749,16 @@ pcap_findalldevs_ex_remote(char *source, struct pcap_rmtauth *auth, pcap_if_t ** } /* Discard the rest of the message. */ - if (rpcap_discard(sockctrl, plen, errbuf) == 1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == 1) goto error_nodiscard; /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) { /* DO not send RPCAP_CLOSE, since we did not open a pcap_t; no need to free resources */ +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Has to be done before the socket is closed +#endif if (sock_close(sockctrl, errbuf, PCAP_ERRBUF_SIZE)) return -1; } @@ -2711,12 +2782,17 @@ error: * * Checks if all the data has been read; if not, discard the data in excess */ - (void) rpcap_discard(sockctrl, plen, NULL); + (void) rpcap_discard(sockctrl, ssl, plen, NULL); error_nodiscard: /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Has to be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); + } /* To avoid inconsistencies in the number of sock_init() */ sock_cleanup(); @@ -2742,10 +2818,12 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * struct sockaddr_storage from; /* generic sockaddr_storage variable */ socklen_t fromlen; /* keeps the length of the sockaddr_storage variable */ SOCKET sockctrl; /* keeps the main socket identifier */ + SSL *ssl = NULL; /* Optional SSL handler for sockctrl */ uint8 protocol_version; /* negotiated protocol version */ struct activehosts *temp, *prev; /* temp var needed to scan he host list chain */ *connectinghost = 0; /* just in case */ + uint8 uses_ssl = 0; // TODO: how to get this info and how to return the SSL*? /* Prepare to open a new server socket */ memset(&hints, 0, sizeof(struct addrinfo)); @@ -2802,11 +2880,31 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * return (SOCKET)-2; } + /* Promote to SSL early before any error message may be sent */ + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; +#endif + } + /* Get the numeric for of the name of the connecting host */ if (getnameinfo((struct sockaddr *) &from, fromlen, connectinghost, RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST)) { sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE); - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2814,7 +2912,10 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* checks if the connecting host is among the ones allowed */ if (sock_check_hostlist((char *)hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0) { - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2822,10 +2923,13 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* * Send authentication to the remote machine. */ - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) { /* Unrecoverable error. */ - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-3; } @@ -2862,13 +2966,17 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); - rpcap_senderror(sockctrl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } memcpy(&temp->host, &from, fromlen); temp->sockctrl = sockctrl; + temp->ssl = ssl; temp->protocol_version = protocol_version; temp->next = NULL; @@ -2915,7 +3023,7 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Don't check for errors, since we're * just cleaning up. */ - if (sock_send(temp->sockctrl, + if (sock_send(temp->sockctrl, temp->ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) @@ -2924,12 +3032,18 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Let that error be the one we * report. */ +#ifdef HAVE_OPENSSL + if (temp->ssl) SSL_free(temp->ssl); +#endif (void)sock_close(temp->sockctrl, NULL, 0); status = -1; } else { +#ifdef HAVE_OPENSSL + if (temp->ssl) SSL_free(temp->ssl); +#endif if (sock_close(temp->sockctrl, errbuf, PCAP_ERRBUF_SIZE) == -1) status = -1; @@ -3030,11 +3144,11 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) /* * Receive the header of a message. */ -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf) +static int rpcap_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *header, char *errbuf) { int nrecv; - nrecv = sock_recv(sock, (char *) header, sizeof(struct rpcap_header), + nrecv = sock_recv(sock, ssl, (char *) header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nrecv == -1) @@ -3050,7 +3164,7 @@ static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char * Make sure the protocol version of a received message is what we were * expecting. */ -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf) +static int rpcap_check_msg_ver(SOCKET sock, SSL *ssl, uint8 expected_ver, struct rpcap_header *header, char *errbuf) { /* * Did the server specify the version we negotiated? @@ -3060,7 +3174,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -3081,7 +3195,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea * Check the message type of a received message, which should either be * the expected message type or RPCAP_MSG_ERROR. */ -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) +static int rpcap_check_msg_type(SOCKET sock, SSL *ssl, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) { const char *request_type_string; const char *msg_type_string; @@ -3096,7 +3210,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he * Hand that error back to our caller. */ *errcode = ntohs(header->value); - rpcap_msg_err(sock, header->plen, errbuf); + rpcap_msg_err(sock, ssl, header->plen, errbuf); return -1; } @@ -3115,7 +3229,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -3151,11 +3265,11 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Receive and process the header of a message. */ -static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) +static int rpcap_process_msg_header(SOCKET sock, SSL *ssl, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) { uint16 errcode; - if (rpcap_recv_msg_header(sock, header, errbuf) == -1) + if (rpcap_recv_msg_header(sock, ssl, header, errbuf) == -1) { /* Network error. */ return -1; @@ -3164,13 +3278,13 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(sock, expected_ver, header, errbuf) == -1) + if (rpcap_check_msg_ver(sock, ssl, expected_ver, header, errbuf) == -1) return -1; /* * Check the message type. */ - return rpcap_check_msg_type(sock, request_type, header, + return rpcap_check_msg_type(sock, ssl, request_type, header, &errcode, errbuf); } @@ -3183,7 +3297,7 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf) +static int rpcap_recv(SOCKET sock, SSL *ssl, void *buffer, size_t toread, uint32 *plen, char *errbuf) { int nread; @@ -3193,7 +3307,7 @@ static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, ch pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -1; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -3206,7 +3320,7 @@ static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, ch /* * This handles the RPCAP_MSG_ERROR message. */ -static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) +static void rpcap_msg_err(SOCKET sockctrl, SSL *ssl, uint32 plen, char *remote_errbuf) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -3216,7 +3330,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -3233,7 +3347,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) /* * Throw away the rest. */ - (void)rpcap_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); + (void)rpcap_discard(sockctrl, ssl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); } else if (plen == 0) { @@ -3242,7 +3356,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) } else { - if (sock_recv(sockctrl, remote_errbuf, plen, + if (sock_recv(sockctrl, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -3264,11 +3378,11 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf) +static int rpcap_discard(SOCKET sock, SSL *ssl, uint32 len, char *errbuf) { if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. return -1; @@ -3300,18 +3414,9 @@ static int rpcap_read_packet_msg(struct pcap_rpcap const *rp, pcap_t *p, size_t * We haven't read all of the packet header yet. * Read what remains, which could be all of it. */ -#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); - } + bytes_read = sock_recv(rp->rmt_sockdata, rp->data_ssl, bp, size - cc, + SOCK_RECEIVEALL_NO|SOCK_EOF_IS_ERROR, p->errbuf, + PCAP_ERRBUF_SIZE); if (bytes_read == -1) { diff --git a/pcap.c b/pcap.c index 0bc1b1ea..c4aba74b 100644 --- a/pcap.c +++ b/pcap.c @@ -1589,7 +1589,8 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, * * XXX - %-escaping? */ - if (pcap_strcasecmp(scheme, "rpcap") == 0 && + if ((pcap_strcasecmp(scheme, "rpcap") == 0 || + pcap_strcasecmp(scheme, "rpcaps") == 0) && strchr(colonp + 3, '/') == NULL) { /* * Local device. @@ -1859,8 +1860,8 @@ pcap_createsrcstr(char *source, int type, const char *host, const char *port, } int -pcap_parsesrcstr(const char *source, int *type, char *host, char *port, - char *name, char *errbuf) +pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf) { char *scheme, *tmpuserinfo, *tmphost, *tmpport, *tmppath; @@ -1871,6 +1872,8 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, *port = '\0'; if (name) *name = '\0'; + if (uses_ssl) + *uses_ssl = 0; /* Parse the source string */ if (pcap_parse_source(source, &scheme, &tmpuserinfo, &tmphost, @@ -1896,12 +1899,20 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, return (0); } - if (strcmp(scheme, "rpcap") == 0) { + int is_rpcap = 0; + if (strcmp(scheme, "rpcaps") == 0) { + is_rpcap = 1; + if (uses_ssl) *uses_ssl = 1; + } else if (strcmp(scheme, "rpcap") == 0) { + is_rpcap = 1; + } + + if (is_rpcap) { /* - * rpcap:// + * rpcap[s]:// * * pcap_parse_source() has already handled the case of - * rpcap://device + * rpcap[s]://device */ if (host && tmphost) { if (tmpuserinfo) @@ -1955,6 +1966,13 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, free(scheme); return (0); } + +int +pcap_parsesrcstr(const char *source, int *type, char *host, char *port, + char *name, char *errbuf) +{ + return pcap_parsesrcstr_ex(source, type, host, port, name, NULL, errbuf); +} #endif pcap_t * diff --git a/pcap/pcap.h b/pcap/pcap.h index e13e7ee2..8ad79b61 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -631,6 +631,9 @@ PCAP_API const char *pcap_lib_version(void); * - rpcap:// [lists all local adapters] * - rpcap://host:port/ [lists the devices available on a remote host] * + * In all the above, "rpcaps://" can be substituted for "rpcap://" to enable + * SSL (if it has been compiled in). + * * Referring to the 'host' and 'port' parameters, they can be either numeric or literal. Since * IPv6 is fully supported, these are the allowed formats: * diff --git a/rpcap-protocol.c b/rpcap-protocol.c index 692f7c5c..0cdc0ba3 100644 --- a/rpcap-protocol.c +++ b/rpcap-protocol.c @@ -61,6 +61,8 @@ * * \param sock: the socket we are currently using. * + * \param ssl: if compiled with openssl, the optional ssl handler to use with the above socket. + * * \param ver: the protocol version we want to put in the reply. * * \param errcode: a integer which tells the other party the type of error @@ -78,7 +80,7 @@ * error message is returned in the 'errbuf' variable. */ int -rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *error, char *errbuf) +rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, unsigned short errcode, const char *error, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -99,7 +101,7 @@ rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *erro RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) return -1; - if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) + if (sock_send(sock, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; return 0; diff --git a/rpcap-protocol.h b/rpcap-protocol.h index 30c8a218..ad66b755 100644 --- a/rpcap-protocol.h +++ b/rpcap-protocol.h @@ -316,7 +316,6 @@ 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 */ }; /* @@ -446,9 +445,10 @@ struct rpcap_sampling *********************************************************/ #include "sockutils.h" +#include "sslutils.h" extern void rpcap_createhdr(struct rpcap_header *header, uint8 ver, uint8 type, uint16 value, uint32 length); extern const char *rpcap_msg_type_string(uint8 type); -extern int rpcap_senderror(SOCKET sock, uint8 ver, uint16 errcode, const char *error, char *errbuf); +extern int rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, uint16 errcode, const char *error, char *errbuf); #endif diff --git a/rpcapd/daemon.c b/rpcapd/daemon.c index b4ce20b3..3cf82926 100644 --- a/rpcapd/daemon.c +++ b/rpcapd/daemon.c @@ -78,6 +78,7 @@ struct daemon_slpars { SOCKET sockctrl_in; //!< SOCKET ID of the input side of the control connection SOCKET sockctrl_out; //!< SOCKET ID of the output side of the control connection + SSL *ssl; //!< Optional SSL handler for the controlling sockets uint8 protocol_version; //!< negotiated protocol version int isactive; //!< Not null if the daemon has to run in active mode int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise @@ -89,9 +90,7 @@ 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 + SSL *ctrl_ssl, *data_ssl; // optional SSL handlers for sockctrl_out and sockdata. uint8 protocol_version; pcap_t *fp; unsigned int TotCapt; @@ -115,18 +114,18 @@ struct thread_handle { }; // Locally defined functions -static int daemon_msg_err(SOCKET sockctrl_in, uint32 plen); +static int daemon_msg_err(SOCKET sockctrl_in, SSL *, uint32 plen); static int daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen); static int daemon_AuthUserPwd(char *username, char *password, char *errbuf); static int daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen); static int daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_t sourcelen); -static int daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param); +static int daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param, int uses_ssl); static int daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struct thread_handle *threaddata); static int daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, uint32 plen); -static int daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errbuf); +static int daemon_unpackapplyfilter(SOCKET sockctrl_in, SSL *, struct session *session, uint32 *plenp, char *errbuf); static int daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 plen, struct pcap_stat *stats, unsigned int svrcapt); @@ -139,14 +138,13 @@ static unsigned __stdcall daemon_thrdatamain(void *ptr); static void *daemon_thrdatamain(void *ptr); #endif -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 int rpcapd_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *headerp); +static int rpcapd_recv(SOCKET sock, SSL *, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); +static int rpcapd_discard(SOCKET sock, SSL *, 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) +daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, SSL *ssl, int isactive, int nullAuthAllowed, int uses_ssl) { struct daemon_slpars pars; // service loop parameters char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed @@ -177,6 +175,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Set parameters structure pars.sockctrl_in = sockctrl_in; pars.sockctrl_out = sockctrl_out; + pars.ssl = ssl; pars.protocol_version = 0; // not yet known pars.isactive = isactive; // active mode pars.nullAuthAllowed = nullAuthAllowed; @@ -227,7 +226,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu if (retval == -1) { sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -236,7 +235,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -245,7 +244,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -301,7 +300,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // reply_version = RPCAP_MAX_VERSION; } - if (rpcap_senderror(pars.sockctrl_out, reply_version, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, reply_version, PCAP_ERR_WRONGVER, "RPCAP version number mismatch", errbuf) == -1) { @@ -311,7 +310,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Network error. goto end; @@ -355,7 +354,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Discard the rest of the message, if // there is anything more. // - (void)rpcapd_discard(pars.sockctrl_in, plen); + (void)rpcapd_discard(pars.sockctrl_in, pars.ssl, plen); // We're done with this client. goto end; @@ -367,7 +366,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // an error message rather than a "let // me log in" message, indicating that // we're not allowed to connect to them? - (void)daemon_msg_err(pars.sockctrl_in, plen); + (void)daemon_msg_err(pars.sockctrl_in, pars.ssl, plen); goto end; case RPCAP_MSG_FINDALLIF_REQ: @@ -390,7 +389,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -398,7 +397,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Network error. goto end; @@ -426,7 +425,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -434,7 +433,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -446,7 +445,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Unknown message type. // pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -454,7 +453,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -506,7 +505,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu if (retval == -1) { sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -517,7 +516,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", @@ -530,7 +529,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -557,7 +556,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // so they don't reject it as having the wrong // version. // - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, header.ver, PCAP_ERR_WRONGVER, "RPCAP version in message isn't the negotiated version", errbuf) == -1) @@ -568,7 +567,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } // Discard the rest of the message. - (void)rpcapd_discard(pars.sockctrl_in, plen); + (void)rpcapd_discard(pars.sockctrl_in, pars.ssl, plen); // Give up on them. goto end; } @@ -577,7 +576,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { case RPCAP_MSG_ERROR: // The other endpoint reported an error { - (void)daemon_msg_err(pars.sockctrl_in, plen); + (void)daemon_msg_err(pars.sockctrl_in, pars.ssl, plen); // Do nothing; just exit; the error code is already into the errbuf // XXX - actually exit.... break; @@ -622,7 +621,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { // They never told us what device // to capture on! - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_STARTCAPTURE, "No capture device was specified", @@ -633,14 +632,14 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { goto end; } break; } - if (daemon_msg_startcap_req(&pars, plen, &threaddata, source, &session, &samp_param) == -1) + if (daemon_msg_startcap_req(&pars, plen, &threaddata, source, &session, &samp_param, uses_ssl) == -1) { // Fatal error; a message has // been logged, so just give up. @@ -662,7 +661,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } else { - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_UPDATEFILTER, "Device not opened. Cannot update filter", @@ -729,7 +728,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } else { - rpcap_senderror(pars.sockctrl_out, + rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_ENDCAPTURE, "Device not opened. Cannot close the capture", @@ -756,7 +755,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // get to reauthenticate. // rpcapd_log(LOGPRIO_INFO, "The client sent an RPCAP_MSG_AUTH_REQ message after authentication was completed"); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, "RPCAP_MSG_AUTH_REQ request sent after authentication was completed", @@ -766,7 +765,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -796,7 +795,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu rpcapd_log(LOGPRIO_INFO, "The client sent a server-to-client message of type %u", header.type); pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -804,7 +803,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -817,7 +816,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // rpcapd_log(LOGPRIO_INFO, "The client sent a message of type %u", header.type); pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errbuf, errmsgbuf) == -1) { @@ -825,7 +824,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -892,7 +891,7 @@ end: * This handles the RPCAP_MSG_ERR message. */ static int -daemon_msg_err(SOCKET sockctrl_in, uint32 plen) +daemon_msg_err(SOCKET sockctrl_in, SSL *ssl, uint32 plen) { char errbuf[PCAP_ERRBUF_SIZE]; char remote_errbuf[PCAP_ERRBUF_SIZE]; @@ -903,7 +902,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl_in, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl_in, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -911,7 +910,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); return -1; } - if (rpcapd_discard(sockctrl_in, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) + if (rpcapd_discard(sockctrl_in, ssl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) { // Network error. return -1; @@ -929,7 +928,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) } else { - if (sock_recv(sockctrl_in, remote_errbuf, plen, + if (sock_recv(sockctrl_in, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -979,7 +978,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) int status; struct rpcap_auth auth; // RPCAP authentication header - status = rpcapd_recv(pars->sockctrl_in, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); if (status == -1) { return -1; @@ -1015,7 +1014,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) PCAP_ERRBUF_SIZE, errno, "malloc() failed"); goto error; } - status = rpcapd_recv(pars->sockctrl_in, username, usernamelen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, username, usernamelen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1037,7 +1036,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) free(username); goto error; } - status = rpcapd_recv(pars->sockctrl_in, passwd, passwdlen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, passwd, passwdlen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1060,7 +1059,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) // free(username); free(passwd); - if (rpcap_senderror(pars->sockctrl_out, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { @@ -1097,7 +1096,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_AUTH_REPLY, 0, 0); // Send the ok message back - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1105,7 +1104,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1113,7 +1112,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1123,7 +1122,7 @@ error: error_noreply: // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1268,7 +1267,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) uint16 nif = 0; // counts the number of interface listed // Discard the rest of the message; there shouldn't be any payload. - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1280,7 +1279,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) if (alldevs == NULL) { - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_NOREMOTEIF, "No interfaces found! Make sure libpcap/WinPcap is properly installed" " and you have the right to access to the remote device.", @@ -1434,7 +1433,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) pcap_freealldevs(alldevs); // Send a final command that says "now send it!" - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1446,7 +1445,7 @@ error: if (alldevs) pcap_freealldevs(alldevs); - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_FINDALLIF, errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1476,7 +1475,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ goto error; } - nread = sock_recv(pars->sockctrl_in, source, plen, + nread = sock_recv(pars->sockctrl_in, pars->ssl, source, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -1519,7 +1518,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ pcap_close(fp); // Send the reply. - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1527,7 +1526,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_OPEN, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1536,7 +1535,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1548,7 +1547,7 @@ error: to discard excess data in the message, if present) */ static int -daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param _U_) +daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param _U_, int uses_ssl) { char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client @@ -1578,7 +1577,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h addrinfo = NULL; - status = rpcapd_recv(pars->sockctrl_in, (char *) &startcapreq, + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &startcapreq, sizeof(struct rpcap_startcapreq), &plen, errmsgbuf); if (status == -1) { @@ -1599,9 +1598,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h goto error; } -#ifdef HAVE_OPENSSL - session->ssl = NULL; -#endif + session->ctrl_ssl = session->data_ssl = NULL; // Open the selected device if ((session->fp = pcap_open_live(source, @@ -1697,10 +1694,11 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h // Needed to send an error on the ctrl connection session->sockctrl_out = pars->sockctrl_out; + session->ctrl_ssl = pars->ssl; session->protocol_version = pars->protocol_version; // Now I can set the filter - ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl_in, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -1735,11 +1733,7 @@ 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) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1768,22 +1762,20 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h sockdata = socktemp; } + SSL *ssl = NULL; + if (uses_ssl) { #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) - { + 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->data_ssl = ssl; session->sockdata = sockdata; // Now we have to create a new thread to receive packets @@ -1814,7 +1806,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h threaddata->have_thread = 1; // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) goto fatal_error; *sessionp = session; @@ -1853,13 +1845,15 @@ error: if (session->fp) pcap_close(session->fp); #ifdef HAVE_OPENSSL - if (session->ssl) - SSL_free(session->ssl); + if (session->ctrl_ssl) + SSL_free(session->ctrl_ssl); + if (session->data_ssl) + SSL_free(session->data_ssl); #endif free(session); } - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_STARTCAPTURE, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1868,7 +1862,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1929,8 +1923,10 @@ fatal_error: if (session->fp) pcap_close(session->fp); #ifdef HAVE_OPENSSL - if (session->ssl) - SSL_free(session->ssl); + if (session->ctrl_ssl) + SSL_free(session->ctrl_ssl); + if (session->data_ssl) + SSL_free(session->data_ssl); #endif free(session); } @@ -1979,12 +1975,12 @@ daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struc threaddata->have_thread = 0; } - session_close(session); + session_close(session); rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_ENDCAP_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1995,7 +1991,7 @@ daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struc } static int -daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errmsgbuf) +daemon_unpackapplyfilter(SOCKET sockctrl_in, SSL *ctrl_ssl, struct session *session, uint32 *plenp, char *errmsgbuf) { int status; struct rpcap_filter filter; @@ -2004,7 +2000,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *pl struct bpf_program bf_prog; unsigned int i; - status = rpcapd_recv(sockctrl_in, (char *) &filter, + status = rpcapd_recv(sockctrl_in, ctrl_ssl, (char *) &filter, sizeof(struct rpcap_filter), plenp, errmsgbuf); if (status == -1) { @@ -2035,7 +2031,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *pl for (i = 0; i < bf_prog.bf_len; i++) { - status = rpcapd_recv(sockctrl_in, (char *) &insn, + status = rpcapd_recv(sockctrl_in, ctrl_ssl, (char *) &insn, sizeof(struct rpcap_filterbpf_insn), plenp, errmsgbuf); if (status == -1) { @@ -2080,7 +2076,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, int ret; // status of daemon_unpackapplyfilter() struct rpcap_header header; // keeps the answer to the updatefilter command - ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl_in, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -2093,7 +2089,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2103,7 +2099,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -2113,11 +2109,11 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, return 0; error: - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } - rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_UPDATEFILTER, errmsgbuf, NULL); return 0; @@ -2135,7 +2131,7 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap struct rpcap_sampling rpcap_samp; int status; - status = rpcapd_recv(pars->sockctrl_in, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); if (status == -1) { return -1; @@ -2153,14 +2149,14 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_SETSAMPLING_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -2168,7 +2164,7 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -2177,7 +2173,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -2195,7 +2191,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 struct rpcap_stats *netstats; // statistics sent on the network // Checks that the header does not contain other data; if so, discard it - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2239,7 +2235,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 } // Send the packet - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -2248,7 +2244,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 return 0; error: - rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_GETSTATS, errmsgbuf, NULL); return 0; } @@ -2409,7 +2405,7 @@ daemon_thrdatamain(void *ptr) // Send the packet // If the client dropped the connection, don't report an // error, just quit. - status = session_send_data(session, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); + status = sock_send(session->sockdata, session->data_ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); if (status < 0) { if (status == -1) @@ -2434,13 +2430,13 @@ daemon_thrdatamain(void *ptr) if (retval == -1) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp)); - rpcap_senderror(session->sockctrl_out, session->protocol_version, + rpcap_senderror(session->sockctrl_out, session->ctrl_ssl, session->protocol_version, PCAP_ERR_READEX, errbuf, NULL); goto error; } error: - session_close(session); + session_close(session); free(sendbuf); @@ -2529,12 +2525,12 @@ void sleep_secs(int secs) * Read the header of a message. */ static int -rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) +rpcapd_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *headerp) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors - nread = sock_recv(sock, (char *) headerp, sizeof(struct rpcap_header), + nread = sock_recv(sock, ssl, (char *) headerp, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2561,7 +2557,7 @@ rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) * error. */ static int -rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) +rpcapd_recv(SOCKET sock, SSL *ssl, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors @@ -2572,7 +2568,7 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -2; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2590,13 +2586,13 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg * error. */ static int -rpcapd_discard(SOCKET sock, uint32 len) +rpcapd_discard(SOCKET sock, SSL *ssl, uint32 len) { char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); @@ -2608,15 +2604,16 @@ rpcapd_discard(SOCKET sock, uint32 len) /* * Close the socket associated with the session, the optional SSL handle, - * and the underlying packet capture handle. + * and the underlying packet capture handle. We of course do not touch + * the controlling socket that's also copied into the session. */ static void session_close(struct session *session) { #ifdef HAVE_OPENSSL - if (session->ssl) + if (session->data_ssl) { - SSL_free(session->ssl); // Must happen *before* the socket is closed - session->ssl = NULL; + SSL_free(session->data_ssl); // Must happen *before* the socket is closed + session->data_ssl = NULL; } #endif @@ -2628,17 +2625,3 @@ static void session_close(struct session *session) 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); - } -} diff --git a/rpcapd/daemon.h b/rpcapd/daemon.h index bd240b80..83901e77 100644 --- a/rpcapd/daemon.h +++ b/rpcapd/daemon.h @@ -33,12 +33,18 @@ #ifndef __DAEMON_H__ #define __DAEMON_H__ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "sslutils.h" + // // Returns 1 if the client closed the control connection explicitly, 0 // otherwise; used in active mode only. // -int daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, - int nullAuthAllowed); +int daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, SSL *ssl, + int isactive, int nullAuthAllowed, int uses_ssl); void sleep_secs(int secs); 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() @@ -147,6 +148,11 @@ static void printusage(void) " the service is started from the control panel\n\n" #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 uses the SSL private key in this file (default: key.pem)\n" + " -C uses the certificate from this file (default: cert.pem)\n" #endif " -s save the current configuration to file\n\n" " -f load the current configuration from file; all switches\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; } diff --git a/sockutils.c b/sockutils.c index e01bd967..84745527 100644 --- a/sockutils.c +++ b/sockutils.c @@ -640,7 +640,7 @@ int sock_initaddress(const char *host, const char *port, * '-2' if we got one of those errors. * For errors, an error message is returned in the 'errbuf' variable. */ -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *ssl, const char *buffer, size_t size, char *errbuf, int errbuflen) { int remaining; @@ -659,6 +659,12 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, remaining = (int)size; do { +#ifdef HAVE_OPENSSL + if (ssl) return ssl_send(ssl, buffer, remaining, errbuf, errbuflen); +#else + (void)ssl; +#endif + #ifdef MSG_NOSIGNAL /* * Send with MSG_NOSIGNAL, so that we don't get SIGPIPE @@ -835,7 +841,7 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int * The error message is returned in the 'errbuf' variable. */ -int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, +int sock_recv(SOCKET sock, SSL *ssl, void *buffer, size_t size, int flags, char *errbuf, int errbuflen) { char *bufp = buffer; @@ -866,6 +872,16 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * Win32. */ for (;;) { +#ifdef HAVE_OPENSSL + if (ssl) + { + nread = ssl_recv(ssl, bufp, remaining, errbuf, errbuflen); + if (nread == -2) return -1; + } + else +#else + (void)ssl; +#endif nread = recv(sock, bufp, remaining, 0); if (nread == -1) @@ -924,7 +940,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * * Returns the size of the datagram on success or -1 on error. */ -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *ssl, void *buffer, size_t size, char *errbuf, int errbuflen) { ssize_t nread; @@ -949,6 +965,13 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, return -1; } + // TODO: DTLS + if (ssl) + { + pcap_snprintf(errbuf, errbuflen, "DTLS not implemented yet"); + return -1; + } + /* * This should be a datagram socket, so we should get the * entire datagram in one recv() or recvmsg() call, and @@ -1059,7 +1082,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, * \return '0' if everything is fine, '-1' if some errors occurred. * The error message is returned in the 'errbuf' variable. */ -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) +int sock_discard(SOCKET sock, SSL *ssl, int size, char *errbuf, int errbuflen) { #define TEMP_BUF_SIZE 32768 @@ -1075,7 +1098,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ while (size > TEMP_BUF_SIZE) { - if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; size -= TEMP_BUF_SIZE; @@ -1087,7 +1110,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ if (size) { - if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; } diff --git a/sockutils.h b/sockutils.h index d3299eaf..95bd8ddb 100644 --- a/sockutils.h +++ b/sockutils.h @@ -101,6 +101,8 @@ #define closesocket(a) close(a) #endif +#include "sslutils.h" // for SSL type, whatever that turns out to be + /* * MingW headers include this definition, but only for Windows XP and above. * MSDN states that this function is available for most versions on Windows. @@ -206,17 +208,17 @@ void sock_geterror(const char *caller, char *errbuf, int errbufsize); int sock_initaddress(const char *address, const char *port, struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen); -int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, +int sock_recv(SOCKET sock, SSL *, void *buffer, size_t size, int receiveall, char *errbuf, int errbuflen); -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *, void *buffer, size_t size, char *errbuf, int errbuflen); SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen); int sock_close(SOCKET sock, char *errbuf, int errbuflen); -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *, const char *buffer, size_t size, char *errbuf, int errbuflen); int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen); -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen); +int sock_discard(SOCKET sock, SSL *, int size, char *errbuf, int errbuflen); int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen); int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second); diff --git a/sslutils.c b/sslutils.c index 346466e2..637940a1 100644 --- a/sslutils.c +++ b/sslutils.c @@ -41,7 +41,6 @@ #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 @@ -59,26 +58,40 @@ static int ssl_init_once(int is_server, char *errbuf, size_t errbuflen) SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); +<<<<<<< HEAD SSL_METHOD const *meth = SSLv23_method(); ctx = SSL_CTX_new(meth); if (! ctx) { +======= + SSL_METHOD const *meth = + is_server ? SSLv23_server_method() : SSLv23_client_method(); + ctx = SSL_CTX_new(meth); + if (! ctx) { +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket 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); +<<<<<<< HEAD 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)) { +======= + 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)) { +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket 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"; +<<<<<<< HEAD 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)); @@ -97,19 +110,40 @@ static int ssl_init_once(int is_server, char *errbuf, size_t errbuflen) } else { +======= + 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 { +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); } } #if 0 +<<<<<<< HEAD if (! RAND_load_file(RANDOM, 1024*1024)) { +======= + if (! RAND_load_file(RANDOM, 1024*1024)) { +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket pcap_snprintf(errbuf, errbuflen, "Cannot init random"); goto die; } +<<<<<<< HEAD if (is_server) { +======= + if (is_server) { +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket SSL_CTX_set_session_id_context(ctx, (void *)&s_server_session_id_context, sizeof(s_server_session_id_context)); } #endif @@ -125,36 +159,37 @@ void init_ssl_or_die(int is_server) { char errbuf[PCAP_ERRBUF_SIZE]; +<<<<<<< HEAD if (ssl_init_once(is_server, errbuf, sizeof errbuf) < 0) { fprintf(stderr, "%s\n", errbuf); exit(3); } +======= + 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) +SSL *ssl_promotion_rw(int is_server, SOCKET in, SOCKET out, char *errbuf, size_t errbuflen) { - if (ssl_init_once(is_server, errbuf, errbuflen) < 0) - { + if (ssl_init_once(is_server, errbuf, errbuflen) < 0) { return NULL; } - SSL *ssl = SSL_new(ctx); - SSL_set_fd(ssl, s); + SSL *ssl = SSL_new(ctx); // TODO: also a DTLS context + SSL_set_rfd(ssl, in); + SSL_set_wfd(ssl, out); - if (is_server) - { - if (SSL_accept(ssl) <= 0) - { + 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) - { + } else { + if (SSL_connect(ssl) <= 0) { pcap_snprintf(errbuf, errbuflen, "SSL_connect(): %s", ERR_error_string(ERR_get_error(), NULL)); return NULL; @@ -162,6 +197,12 @@ SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen) } return ssl; +>>>>>>> b5063379... TLS for rpcap: also encrypt the control socket +} + +SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen) +{ + return ssl_promotion_rw(is_server, s, s, errbuf, errbuflen); } // Same return value as sock_send: @@ -193,9 +234,8 @@ int ssl_send(SSL *ssl, char const *buffer, size_t size, char *errbuf, size_t err } } -// 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) +// Returns the number of bytes read, or -1 on syserror, or -2 on SSL error. +int ssl_recv(SSL *ssl, char *buffer, size_t size, char *errbuf, size_t errbuflen) { int status = SSL_read(ssl, buffer, size); if (status <= 0) @@ -203,26 +243,24 @@ int ssl_recv(SSL *ssl, unsigned char *buffer, size_t size, char *errbuf, size_t 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; + return 0; } 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 -1; } + else + { + // Should not happen + pcap_snprintf(errbuf, errbuflen, "SSL_read(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return -2; + } + } + else + { + return status; } - return status; } #endif // HAVE_OPENSSL diff --git a/sslutils.h b/sslutils.h index b2ae3194..457c9214 100644 --- a/sslutils.h +++ b/sslutils.h @@ -34,19 +34,18 @@ #define __SSLUTILS_H__ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif #ifdef HAVE_OPENSSL #include #include -#include "sockutils.h" +#include "pcap/pcap.h" // for SOCKET /* * Configuration parameters */ -extern int uses_ssl; extern char ssl_keyfile[PATH_MAX]; extern char ssl_certfile[PATH_MAX]; extern char ssl_rootfile[PATH_MAX]; @@ -57,8 +56,14 @@ extern char ssl_rootfile[PATH_MAX]; void init_ssl_or_die(int is_server); SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen); +SSL *ssl_promotion_rw(int is_server, SOCKET in, SOCKET out, 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); +int ssl_recv(SSL *, char *buffer, size_t size, char *errbuf, size_t errbuflen); + +#else // HAVE_OPENSSL + +// This saves us from a lot of ifdefs: +#define SSL void const #endif // HAVE_OPENSSL