X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/8bed61def0750b5d7cc4532e5d35206674865d6c..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/rpcapd/daemon.c diff --git a/rpcapd/daemon.c b/rpcapd/daemon.c index f21d775d..9b0f8285 100644 --- a/rpcapd/daemon.c +++ b/rpcapd/daemon.c @@ -199,6 +199,17 @@ struct tls_alert { static int is_url(const char *source); +/* + * Maximum sizes for fixed-bit-width values. + */ +#ifndef UINT16_MAX +#define UINT16_MAX 65535U +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295U +#endif + int daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, int nullAuthAllowed, int uses_ssl) @@ -364,7 +375,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // Immediate EOF goto end; } - plen = (tls_header.length_hi << 8) | tls_header.length_lo; + plen = (tls_header.length_hi << 8U) | tls_header.length_lo; // Discard the rest of the message. if (rpcapd_discard(sockctrl, NULL, plen) == -1) @@ -384,7 +395,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, if (sock_send(sockctrl, NULL, (char *) &tls_header, TLS_RECORD_HEADER_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -394,7 +405,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, if (sock_send(sockctrl, NULL, (char *) &tls_alert, TLS_ALERT_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -438,7 +449,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, if (getpeername(pars.sockctrl, (struct sockaddr *)&from, &fromlen) == -1) { - sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getpeername() failed"); if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; @@ -512,7 +524,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, retval = select((int)pars.sockctrl + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { - sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "select() failed"); if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; @@ -736,7 +749,7 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, // // Be carefully: the capture can have been started, but an error occurred (so session != NULL, but // sockdata is 0 - if ((!pars.isactive) && ((session == NULL) || ((session != NULL) && (session->sockdata == 0)))) + if ((!pars.isactive) && (session == NULL || session->sockdata == 0)) { // Check for the initial timeout FD_ZERO(&rfds); @@ -745,11 +758,15 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients, tv.tv_usec = 0; FD_SET(pars.sockctrl, &rfds); - +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + retval = 1; +#else retval = select((int)pars.sockctrl + 1, &rfds, NULL, NULL, &tv); +#endif if (retval == -1) { - sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "select() failed"); if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) @@ -1094,6 +1111,9 @@ end: session = NULL; } + if (passiveClients) { + free(passiveClients); + } // // Finish using the SSL handle for the control socket, if we // have an SSL connection, and close the control socket. @@ -1351,16 +1371,21 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) goto error; // - // Indicate to our peer what versions we support. + // Indicate to our peer what versions we support and what our + // version of the byte-order magic is (which will tell the + // client whether our byte order differs from theirs, in which + // case they will need to byte-swap some fields in some + // link-layer types' headers). // memset(authreply, 0, sizeof(struct rpcap_authreply)); authreply->minvers = RPCAP_MIN_VERSION; authreply->maxvers = RPCAP_MAX_VERSION; + authreply->byte_order_magic = RPCAP_BYTE_ORDER_MAGIC; // Send the reply. if (sock_send(pars->sockctrl, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } @@ -1429,7 +1454,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) pcap_fmt_errmsg_for_win32_err(errmsgbuf, PCAP_ERRBUF_SIZE, error, "LogonUser() failed"); rpcapd_log(LOGPRIO_ERROR, "%s", errmsgbuf); - } + } return -1; } @@ -1452,7 +1477,7 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) /* * See * - * http://www.unixpapa.com/incnote/passwd.html + * https://www.unixpapa.com/incnote/passwd.html * * We use the Solaris/Linux shadow password authentication if * we have getspnam(), otherwise we just do traditional @@ -1560,6 +1585,21 @@ daemon_AuthUserPwd(char *username, char *password, char *errbuf) } +/* + * Make sure that the reply length won't overflow 32 bits if we add the + * specified amount to it. If it won't, add that amount to it. + * + * We check whether replylen + itemlen > UINT32_MAX, but subtract itemlen + * from both sides, to prevent overflow. + */ +#define CHECK_AND_INCREASE_REPLY_LEN(itemlen) \ + if (replylen > UINT32_MAX - (itemlen)) { \ + pcap_strlcpy(errmsgbuf, "Reply length doesn't fit in 32 bits", \ + sizeof (errmsgbuf)); \ + goto error; \ + } \ + replylen += (uint32)(itemlen) + static int daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { @@ -1570,7 +1610,6 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) pcap_if_t *alldevs = NULL; // pointer to the header of the interface chain pcap_if_t *d; // temp pointer needed to scan the interface chain struct pcap_addr *address; // pcap structure that keeps a network address of an interface - struct rpcap_findalldevs_if *findalldevs_if;// rpcap structure that packet all the data of an interface together uint32 replylen; // length of reply payload uint16 nif = 0; // counts the number of interface listed @@ -1606,13 +1645,30 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { nif++; - if (d->description) - replylen += strlen(d->description); - if (d->name) - replylen += strlen(d->name); + if (d->description) { + size_t stringlen = strlen(d->description); + if (stringlen > UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Description length doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + CHECK_AND_INCREASE_REPLY_LEN(stringlen); + } + if (d->name) { + size_t stringlen = strlen(d->name); + if (stringlen > UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Name length doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + CHECK_AND_INCREASE_REPLY_LEN(stringlen); + } - replylen += sizeof(struct rpcap_findalldevs_if); + CHECK_AND_INCREASE_REPLY_LEN(sizeof(struct rpcap_findalldevs_if)); + uint16_t naddrs = 0; for (address = d->addresses; address != NULL; address = address->next) { /* @@ -1624,7 +1680,14 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) #ifdef AF_INET6 case AF_INET6: #endif - replylen += (sizeof(struct rpcap_sockaddr) * 4); + CHECK_AND_INCREASE_REPLY_LEN(sizeof(struct rpcap_sockaddr) * 4); + if (naddrs == UINT16_MAX) { + pcap_strlcpy(errmsgbuf, + "Number of interfaces doesn't fit in 16 bits", + sizeof (errmsgbuf)); + goto error; + } + naddrs++; break; default: @@ -1633,7 +1696,7 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) } } - // RPCAP findalldevs command + // RPCAP findalldevs reply if (sock_bufferize(NULL, sizeof(struct rpcap_header), NULL, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) @@ -1647,23 +1710,36 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) { uint16 lname, ldescr; - findalldevs_if = (struct rpcap_findalldevs_if *) &sendbuf[sendbufidx]; - - if (sock_bufferize(NULL, sizeof(struct rpcap_findalldevs_if), NULL, - &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_CHECKONLY, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) - goto error; - - memset(findalldevs_if, 0, sizeof(struct rpcap_findalldevs_if)); + // Note: the findalldevs_if entries are *not* neatly + // aligned on 4-byte boundaries, because they're + // preceded by strings that aren't padded to 4-byte + // boundaries, so we cannot just cast output buffer + // boundaries to struct rpcap_findalldevs_if pointers + // and store into them - we must fill in a structure and + // then copy the structure to the buffer, as not all + // systems support unaligned access (some, such as + // SPARC, crash; others, such as Arm, may just ignore + // the lower-order bits). + struct rpcap_findalldevs_if findalldevs_if; - if (d->description) ldescr = (short) strlen(d->description); - else ldescr = 0; - if (d->name) lname = (short) strlen(d->name); - else lname = 0; + /* + * We've already established that the string lengths + * fit in 16 bits. + */ + if (d->description) + ldescr = (uint16) strlen(d->description); + else + ldescr = 0; + if (d->name) + lname = (uint16) strlen(d->name); + else + lname = 0; - findalldevs_if->desclen = htons(ldescr); - findalldevs_if->namelen = htons(lname); - findalldevs_if->flags = htonl(d->flags); + findalldevs_if.desclen = htons(ldescr); + findalldevs_if.namelen = htons(lname); + findalldevs_if.flags = htonl(d->flags); + uint16_t naddrs = 0; for (address = d->addresses; address != NULL; address = address->next) { /* @@ -1675,14 +1751,20 @@ daemon_msg_findallif_req(uint8 ver, struct daemon_slpars *pars, uint32 plen) #ifdef AF_INET6 case AF_INET6: #endif - findalldevs_if->naddr++; + naddrs++; break; default: break; } } - findalldevs_if->naddr = htons(findalldevs_if->naddr); + findalldevs_if.naddr = htons(naddrs); + findalldevs_if.dummy = 0; + + if (sock_bufferize(&findalldevs_if, sizeof(struct rpcap_findalldevs_if), sendbuf, + &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, + PCAP_ERRBUF_SIZE) == -1) + goto error; if (sock_bufferize(d->name, lname, sendbuf, &sendbufidx, RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errmsgbuf, @@ -1808,7 +1890,7 @@ daemon_msg_open_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, // This is a fake open, since we do that only to get the needed parameters, then we close the device again if ((fp = pcap_open_live(source, 1500 /* fake snaplen */, - 0 /* no promis */, + 0 /* no promisc */, 1000 /* fake timeout */, errmsgbuf)) == NULL) goto error; @@ -1980,7 +2062,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, saddrlen = sizeof(struct sockaddr_storage); if (getpeername(pars->sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getpeername() failed"); goto error; } @@ -1997,32 +2080,34 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peerhost, sizeof(peerhost), NULL, 0, NI_NUMERICHOST)) { - sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); goto error; } if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) goto error; - if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((session->sockdata = sock_open(peerhost, addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error; } else // Data connection is opened by the client toward the server { hints.ai_flags = AI_PASSIVE; - // Let's the server socket pick up a free network port for us - if (sock_initaddress(NULL, "0", &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) + // Make the server socket pick up a free network port for us + if (sock_initaddress(NULL, NULL, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1) goto error; - if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) + if ((session->sockdata = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET) goto error; // get the complete sockaddr structure used in the data connection saddrlen = sizeof(struct sockaddr_storage); if (getsockname(session->sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1) { - sock_geterror("getsockname()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getsockname() failed"); goto error; } @@ -2030,7 +2115,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, 0, portdata, sizeof(portdata), NI_NUMERICSERV)) { - sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE, + "getnameinfo() failed"); goto error; } } @@ -2098,7 +2184,8 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (socktemp == INVALID_SOCKET) { - sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE); + sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, + "accept() failed"); rpcapd_log(LOGPRIO_ERROR, "Accept of data connection failed: %s", errbuf); goto error; @@ -2273,7 +2360,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl, SSL *ctrl_ssl, struct session *session if (bf_prog.bf_len > RPCAP_BPF_MAXINSNS) { snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, - "Filter program is larger than the maximum size of %u instructions", + "Filter program is larger than the maximum size of %d instructions", RPCAP_BPF_MAXINSNS); return -2; } @@ -2357,9 +2444,9 @@ daemon_msg_updatefilter_req(uint8 ver, struct daemon_slpars *pars, // A response is needed, otherwise the other host does not know that everything went well rpcap_createhdr(&header, ver, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0); - if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) + if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE)) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } @@ -2409,7 +2496,7 @@ daemon_msg_setsampling_req(uint8 ver, struct daemon_slpars *pars, uint32 plen, if (sock_send(pars->sockctrl, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { - // That failed; log a messsage and give up. + // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } @@ -2754,19 +2841,25 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka if (sockaddrin == NULL) return; // Warning: we support only AF_INET and AF_INET6 + // + // Note: as noted above, the output structures are not + // neatly aligned on 4-byte boundaries, so we must fill + // in an aligned structure and then copy it to the output + // buffer with memcpy(). switch (sockaddrin->ss_family) { case AF_INET: { struct sockaddr_in *sockaddrin_ipv4; - struct rpcap_sockaddr_in *sockaddrout_ipv4; + struct rpcap_sockaddr_in sockaddrout_ipv4; sockaddrin_ipv4 = (struct sockaddr_in *) sockaddrin; - sockaddrout_ipv4 = (struct rpcap_sockaddr_in *) sockaddrout; - sockaddrout_ipv4->family = htons(RPCAP_AF_INET); - sockaddrout_ipv4->port = htons(sockaddrin_ipv4->sin_port); - memcpy(&sockaddrout_ipv4->addr, &sockaddrin_ipv4->sin_addr, sizeof(sockaddrout_ipv4->addr)); - memset(sockaddrout_ipv4->zero, 0, sizeof(sockaddrout_ipv4->zero)); + + sockaddrout_ipv4.family = htons(RPCAP_AF_INET); + sockaddrout_ipv4.port = htons(sockaddrin_ipv4->sin_port); + memcpy(&sockaddrout_ipv4.addr, &sockaddrin_ipv4->sin_addr, sizeof(sockaddrout_ipv4.addr)); + memset(sockaddrout_ipv4.zero, 0, sizeof(sockaddrout_ipv4.zero)); + memcpy(sockaddrout, &sockaddrout_ipv4, sizeof(struct rpcap_sockaddr_in)); break; } @@ -2774,15 +2867,16 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka case AF_INET6: { struct sockaddr_in6 *sockaddrin_ipv6; - struct rpcap_sockaddr_in6 *sockaddrout_ipv6; + struct rpcap_sockaddr_in6 sockaddrout_ipv6; sockaddrin_ipv6 = (struct sockaddr_in6 *) sockaddrin; - sockaddrout_ipv6 = (struct rpcap_sockaddr_in6 *) sockaddrout; - sockaddrout_ipv6->family = htons(RPCAP_AF_INET6); - sockaddrout_ipv6->port = htons(sockaddrin_ipv6->sin6_port); - sockaddrout_ipv6->flowinfo = htonl(sockaddrin_ipv6->sin6_flowinfo); - memcpy(&sockaddrout_ipv6->addr, &sockaddrin_ipv6->sin6_addr, sizeof(sockaddrout_ipv6->addr)); - sockaddrout_ipv6->scope_id = htonl(sockaddrin_ipv6->sin6_scope_id); + + sockaddrout_ipv6.family = htons(RPCAP_AF_INET6); + sockaddrout_ipv6.port = htons(sockaddrin_ipv6->sin6_port); + sockaddrout_ipv6.flowinfo = htonl(sockaddrin_ipv6->sin6_flowinfo); + memcpy(&sockaddrout_ipv6.addr, &sockaddrin_ipv6->sin6_addr, sizeof(sockaddrout_ipv6.addr)); + sockaddrout_ipv6.scope_id = htonl(sockaddrin_ipv6->sin6_scope_id); + memcpy(sockaddrout, &sockaddrout_ipv6, sizeof(struct rpcap_sockaddr_in6)); break; } #endif @@ -2795,6 +2889,7 @@ daemon_seraddr(struct sockaddr_storage *sockaddrin, struct rpcap_sockaddr *socka */ void sleep_secs(int secs) { +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef _WIN32 Sleep(secs*1000); #else @@ -2806,6 +2901,7 @@ void sleep_secs(int secs) while (secs_remaining != 0) secs_remaining = sleep(secs_remaining); #endif +#endif } /* @@ -2985,6 +3081,7 @@ static void session_close(struct session *session) } } +// // Check whether a capture source string is a URL or not. // This includes URLs that refer to a local device; a scheme, followed // by ://, followed by *another* scheme and ://, is just silly, and