From 2aa69b04d8173b18a0e3492e0c8f2f7fabdf642d Mon Sep 17 00:00:00 2001
From: Guy Harris <gharris@sonic.net>
Date: Thu, 28 Sep 2023 00:37:57 -0700
Subject: [PATCH] Have sock_initaddress() return the list of addrinfo
 structures or NULL.

Its return address is currently 0 for success and -1 for failure, with a
pointer to the first element of the list of struct addrinfos returned
through a pointer on success; change it to return that pointer on
success and NULL on failure.

That way, we don't have to worry about what happens to the pointer
pointeed to by the argument in question on failure; we know that we got
NULL back if no struct addrinfos were found because getaddrinfo()
failed.  Thus, we know that we have something to free iff
sock_initaddress() returned a pointer to that something rather than
returning NULL.

This avoids a double-free in some cases.

This is apparently CVE-2023-40400.

(backported from commit 262e4f34979872d822ccedf9f318ed89c4d31c03)

Upstream-Status: Backport [https://github.com/the-tcpdump-group/libpcap/commit/2aa69b04d8173b18a0e3492e0c8f2f7fabdf642d]
CVE: CVE-2023-7256
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
---
 pcap-rpcap.c    | 48 ++++++++++++++++++++--------------------
 rpcapd/daemon.c |  8 +++++--
 rpcapd/rpcapd.c |  8 +++++--
 sockutils.c     | 58 ++++++++++++++++++++++++++++---------------------
 sockutils.h     |  5 ++---
 5 files changed, 72 insertions(+), 55 deletions(-)

diff --git a/pcap-rpcap.c b/pcap-rpcap.c
index 91f8557..733077b 100644
--- a/pcap-rpcap.c
+++ b/pcap-rpcap.c
@@ -995,7 +995,6 @@ rpcap_remoteact_getsock(const char *host, int *error, char *errbuf)
 {
 	struct activehosts *temp;			/* temp var needed to scan the host list chain */
 	struct addrinfo hints, *addrinfo, *ai_next;	/* temp var needed to translate between hostname to its address */
-	int retval;
 
 	/* retrieve the network address corresponding to 'host' */
 	addrinfo = NULL;
@@ -1003,9 +1002,9 @@ rpcap_remoteact_getsock(const char *host, int *error, char *errbuf)
 	hints.ai_family = PF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
 
-	retval = sock_initaddress(host, NULL, &hints, &addrinfo, errbuf,
+	addrinfo = sock_initaddress(host, NULL, &hints, errbuf,
 	    PCAP_ERRBUF_SIZE);
-	if (retval != 0)
+	if (addrinfo == NULL)
 	{
 		*error = 1;
 		return NULL;
@@ -1153,7 +1152,9 @@ static int pcap_startcapture_remote(pcap_t *fp)
 		hints.ai_flags = AI_PASSIVE;	/* Data connection is opened by the server toward the client */
 
 		/* Let's the server pick up a free network port for us */
-		if (sock_initaddress(NULL, NULL, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1)
+		addrinfo = sock_initaddress(NULL, NULL, &hints, fp->errbuf,
+		    PCAP_ERRBUF_SIZE);
+		if (addrinfo == NULL)
 			goto error_nodiscard;
 
 		if ((sockdata = sock_open(addrinfo, SOCKOPEN_SERVER,
@@ -1277,7 +1278,9 @@ static int pcap_startcapture_remote(pcap_t *fp)
 			snprintf(portstring, PCAP_BUF_SIZE, "%d", ntohs(startcapreply.portdata));
 
 			/* Let's the server pick up a free network port for us */
-			if (sock_initaddress(host, portstring, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1)
+			addrinfo = sock_initaddress(host, portstring, &hints,
+			    fp->errbuf, PCAP_ERRBUF_SIZE);
+			if (addrinfo == NULL)
 				goto error;
 
 			if ((sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
@@ -2220,16 +2223,16 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth,
 		if (port[0] == 0)
 		{
 			/* the user chose not to specify the port */
-			if (sock_initaddress(host, RPCAP_DEFAULT_NETPORT,
-			    &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
-				return -1;
+			addrinfo = sock_initaddress(host, RPCAP_DEFAULT_NETPORT,
+			    &hints, errbuf, PCAP_ERRBUF_SIZE);
 		}
 		else
 		{
-			if (sock_initaddress(host, port, &hints, &addrinfo,
-			    errbuf, PCAP_ERRBUF_SIZE) == -1)
-				return -1;
+			addrinfo = sock_initaddress(host, port, &hints,
+			    errbuf, PCAP_ERRBUF_SIZE);
 		}
+		if (addrinfo == NULL)
+			return -1;
 
 		if ((*sockctrlp = sock_open(addrinfo, SOCKOPEN_CLIENT, 0,
 		    errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
@@ -2825,19 +2828,19 @@ SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, const cha
 	/* Do the work */
 	if ((port == NULL) || (port[0] == 0))
 	{
-		if (sock_initaddress(address, RPCAP_DEFAULT_NETPORT_ACTIVE, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
-		{
-			return (SOCKET)-2;
-		}
+		addrinfo = sock_initaddress(address,
+		    RPCAP_DEFAULT_NETPORT_ACTIVE, &hints, errbuf,
+		    PCAP_ERRBUF_SIZE);
 	}
 	else
 	{
-		if (sock_initaddress(address, port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
-		{
-			return (SOCKET)-2;
-		}
+		addrinfo = sock_initaddress(address, port, &hints, errbuf,
+		    PCAP_ERRBUF_SIZE);
+	}
+	if (addrinfo == NULL)
+	{
+		return (SOCKET)-2;
 	}
-
 
 	if ((sockmain = sock_open(addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
 	{
@@ -2994,7 +2997,6 @@ int pcap_remoteact_close(const char *host, char *errbuf)
 {
 	struct activehosts *temp, *prev;	/* temp var needed to scan the host list chain */
 	struct addrinfo hints, *addrinfo, *ai_next;	/* temp var needed to translate between hostname to its address */
-	int retval;
 
 	temp = activeHosts;
 	prev = NULL;
@@ -3005,9 +3007,9 @@ int pcap_remoteact_close(const char *host, char *errbuf)
 	hints.ai_family = PF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
 
-	retval = sock_initaddress(host, NULL, &hints, &addrinfo, errbuf,
+	addrinfo = sock_initaddress(host, NULL, &hints, errbuf,
 	    PCAP_ERRBUF_SIZE);
-	if (retval != 0)
+	if (addrinfo == NULL)
 	{
 		return -1;
 	}
diff --git a/rpcapd/daemon.c b/rpcapd/daemon.c
index 8f50899..925d381 100644
--- a/rpcapd/daemon.c
+++ b/rpcapd/daemon.c
@@ -2065,7 +2065,9 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen,
 			goto error;
 		}
 
-		if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1)
+		addrinfo = sock_initaddress(peerhost, portdata, &hints,
+		    errmsgbuf, PCAP_ERRBUF_SIZE);
+		if (addrinfo == NULL)
 			goto error;
 
 		if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
@@ -2076,7 +2078,9 @@ daemon_msg_startcap_req(uint8 ver, struct daemon_slpars *pars, uint32 plen,
 		hints.ai_flags = AI_PASSIVE;
 
 		// Make the server socket pick up a free network port for us
-		if (sock_initaddress(NULL, NULL, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1)
+		addrinfo = sock_initaddress(NULL, NULL, &hints, errmsgbuf,
+		    PCAP_ERRBUF_SIZE);
+		if (addrinfo == NULL)
 			goto error;
 
 		if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
diff --git a/rpcapd/rpcapd.c b/rpcapd/rpcapd.c
index b91a401..74c138b 100644
--- a/rpcapd/rpcapd.c
+++ b/rpcapd/rpcapd.c
@@ -610,7 +610,9 @@ void main_startup(void)
 		//
 		// Get a list of sockets on which to listen.
 		//
-		if (sock_initaddress((address[0]) ? address : NULL, port, &mainhints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+		addrinfo = sock_initaddress((address[0]) ? address : NULL,
+		    port, &mainhints, errbuf, PCAP_ERRBUF_SIZE);
+		if (addrinfo == NULL)
 		{
 			rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
 			return;
@@ -1347,7 +1349,9 @@ main_active(void *ptr)
 	memset(errbuf, 0, sizeof(errbuf));
 
 	// Do the work
-	if (sock_initaddress(activepars->address, activepars->port, &hints, &addrinfo, errbuf, PCAP_ERRBUF_SIZE) == -1)
+	addrinfo = sock_initaddress(activepars->address, activepars->port,
+	    &hints, errbuf, PCAP_ERRBUF_SIZE);
+	if (addrinfo == NULL)
 	{
 		rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
 		return 0;
diff --git a/sockutils.c b/sockutils.c
index 0b0bcee..4d02d96 100644
--- a/sockutils.c
+++ b/sockutils.c
@@ -704,20 +704,21 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
  * \param errbuflen: length of the buffer that will contains the error. The error message cannot be
  * larger than 'errbuflen - 1' because the last char is reserved for the string terminator.
  *
- * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned
- * in the 'errbuf' variable. The addrinfo variable that has to be used in the following sockets calls is
- * returned into the addrinfo parameter.
+ * \return a pointer to the first element in a list of addrinfo structures
+ * if everything is fine, NULL if some errors occurred. The error message
+ * is returned in the 'errbuf' variable.
  *
- * \warning The 'addrinfo' variable has to be deleted by the programmer by calling freeaddrinfo() when
- * it is no longer needed.
+ * \warning The list of addrinfo structures returned has to be deleted by
+ * the programmer by calling freeaddrinfo() when it is no longer needed.
  *
  * \warning This function requires the 'hints' variable as parameter. The semantic of this variable is the same
  * of the one of the corresponding variable used into the standard getaddrinfo() socket function. We suggest
  * the programmer to look at that function in order to set the 'hints' variable appropriately.
  */
-int sock_initaddress(const char *host, const char *port,
-    struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen)
+struct addrinfo *sock_initaddress(const char *host, const char *port,
+    struct addrinfo *hints, char *errbuf, int errbuflen)
 {
+	struct addrinfo *addrinfo;
 	int retval;
 
 	/*
@@ -729,9 +730,13 @@ int sock_initaddress(const char *host, const char *port,
 	 * as those messages won't talk about a problem with the port if
 	 * no port was specified.
 	 */
-	retval = getaddrinfo(host, port == NULL ? "0" : port, hints, addrinfo);
+	retval = getaddrinfo(host, port == NULL ? "0" : port, hints, &addrinfo);
 	if (retval != 0)
 	{
+		/*
+		 * That call failed.
+		 * Determine whether the problem is that the host is bad.
+		 */
 		if (errbuf)
 		{
 			if (host != NULL && port != NULL) {
@@ -743,7 +748,7 @@ int sock_initaddress(const char *host, const char *port,
 				int try_retval;
 
 				try_retval = getaddrinfo(host, NULL, hints,
-				    addrinfo);
+				    &addrinfo);
 				if (try_retval == 0) {
 					/*
 					 * Worked with just the host,
@@ -752,14 +757,16 @@ int sock_initaddress(const char *host, const char *port,
 					 *
 					 * Free up the addres info first.
 					 */
-					freeaddrinfo(*addrinfo);
+					freeaddrinfo(addrinfo);
 					get_gai_errstring(errbuf, errbuflen,
 					    "", retval, NULL, port);
 				} else {
 					/*
 					 * Didn't work with just the host,
 					 * so assume the problem is
-					 * with the host.
+					 * with the host; we assume
+					 * the original error indicates
+					 * the underlying problem.
 					 */
 					get_gai_errstring(errbuf, errbuflen,
 					    "", retval, host, NULL);
@@ -767,13 +774,14 @@ int sock_initaddress(const char *host, const char *port,
 			} else {
 				/*
 				 * Either the host or port was null, so
-				 * there's nothing to determine.
+				 * there's nothing to determine; report
+				 * the error from the original call.
 				 */
 				get_gai_errstring(errbuf, errbuflen, "",
 				    retval, host, port);
 			}
 		}
-		return -1;
+		return NULL;
 	}
 	/*
 	 * \warning SOCKET: I should check all the accept() in order to bind to all addresses in case
@@ -788,30 +796,28 @@ int sock_initaddress(const char *host, const char *port,
 	 * ignore all addresses that are neither?  (What, no IPX
 	 * support? :-))
 	 */
-	if (((*addrinfo)->ai_family != PF_INET) &&
-	    ((*addrinfo)->ai_family != PF_INET6))
+	if ((addrinfo->ai_family != PF_INET) &&
+	    (addrinfo->ai_family != PF_INET6))
 	{
 		if (errbuf)
 			snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
-		freeaddrinfo(*addrinfo);
-		*addrinfo = NULL;
-		return -1;
+		freeaddrinfo(addrinfo);
+		return NULL;
 	}
 
 	/*
 	 * You can't do multicast (or broadcast) TCP.
 	 */
-	if (((*addrinfo)->ai_socktype == SOCK_STREAM) &&
-	    (sock_ismcastaddr((*addrinfo)->ai_addr) == 0))
+	if ((addrinfo->ai_socktype == SOCK_STREAM) &&
+	    (sock_ismcastaddr(addrinfo->ai_addr) == 0))
 	{
 		if (errbuf)
 			snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
-		freeaddrinfo(*addrinfo);
-		*addrinfo = NULL;
-		return -1;
+		freeaddrinfo(addrinfo);
+		return NULL;
 	}
 
-	return 0;
+	return addrinfo;
 }
 
 /*
@@ -1720,7 +1726,9 @@ int sock_present2network(const char *address, struct sockaddr_storage *sockaddr,
 
 	hints.ai_family = addr_family;
 
-	if (sock_initaddress(address, "22222" /* fake port */, &hints, &addrinfo, errbuf, errbuflen) == -1)
+	addrinfo = sock_initaddress(address, "22222" /* fake port */, &hints,
+	    errbuf, errbuflen);
+	if (addrinfo == NULL)
 		return 0;
 
 	if (addrinfo->ai_family == PF_INET)
diff --git a/sockutils.h b/sockutils.h
index e748662..ede86a1 100644
--- a/sockutils.h
+++ b/sockutils.h
@@ -129,9 +129,8 @@ int sock_init(char *errbuf, int errbuflen);
 void sock_cleanup(void);
 void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen);
 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);
+struct addrinfo *sock_initaddress(const char *address, const char *port,
+    struct addrinfo *hints, char *errbuf, int errbuflen);
 int sock_recv(SOCKET sock, SSL *, void *buffer, size_t size, int receiveall,
     char *errbuf, int errbuflen);
 int sock_recv_dgram(SOCKET sock, SSL *, void *buffer, size_t size,
-- 
2.25.1

