[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#769090: unblock: torsocks/2.0.0-3



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package torsocks 2.0.0-3, to an important bug (#766306)
that can potentially result in data loss.

This bug is fixed by Fix-improve-Unix-socket-passing-detection.patch
(cherry-picked from upstream), which implied to add the test it creates
(test_fd_passing test) to exclude_test_requiring_network.patch, and later on to
cherry-pick Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch as well
since the first patch made the package FTBFS on kfreebsd-*.

The debdiff is not absolutely tiny, but you'll notice that most of the lines
added are in the test suite, and more precisely in the test that we disable when
building the Debian package (that test passes here if I temporarily enable
network access in my pbuilder, though).

If it would help to show you the diff resulting from the two cherry-picked
patches combined, so that you don't have to review a diff-of-diff and then
a diff-of-diff that touches the code already modified in the previous one, just
let me know and I'll prepare that.

unblock torsocks/2.0.0-3
diff -Nru torsocks-2.0.0/debian/changelog torsocks-2.0.0/debian/changelog
--- torsocks-2.0.0/debian/changelog	2014-08-24 23:24:58.000000000 +0200
+++ torsocks-2.0.0/debian/changelog	2014-11-10 23:39:39.000000000 +0100
@@ -1,3 +1,22 @@
+torsocks (2.0.0-3) unstable; urgency=medium
+
+  * Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch:
+    new patch, cherry-picked from upstream, that fixes the FTBFS
+    kfreebsd-* that was introduced by
+    Fix-improve-Unix-socket-passing-detection.patch (Closes: #768140).
+
+ -- intrigeri <intrigeri@debian.org>  Mon, 10 Nov 2014 23:39:19 +0100
+
+torsocks (2.0.0-2) unstable; urgency=medium
+
+  * Fix-improve-Unix-socket-passing-detection.patch: new patch,
+    cherry-picked from upstream (Closes: #766306).
+  * Amend exclude_test_requiring_network.patch to add test_fd_passing test,
+    introduced by the new aforementioned patch, to the list of excluded
+    tests: it requires network access.
+
+ -- intrigeri <intrigeri@debian.org>  Tue, 28 Oct 2014 10:41:10 +0100
+
 torsocks (2.0.0-1) unstable; urgency=low
 
   * New upstream release. The 2.0 rewrite is considered stable.
diff -Nru torsocks-2.0.0/debian/patches/exclude_test_requiring_network.patch torsocks-2.0.0/debian/patches/exclude_test_requiring_network.patch
--- torsocks-2.0.0/debian/patches/exclude_test_requiring_network.patch	2014-08-24 23:24:58.000000000 +0200
+++ torsocks-2.0.0/debian/patches/exclude_test_requiring_network.patch	2014-11-10 23:39:39.000000000 +0100
@@ -5,9 +5,10 @@
 
 --- a/tests/test_list
 +++ b/tests/test_list
-@@ -1,5 +1,4 @@
+@@ -1,6 +1,4 @@
  ./test_connect
 -./test_dns
+-./test_fd_passing
  ./test_socket
  ./unit/test_onion
  ./unit/test_connection
diff -Nru torsocks-2.0.0/debian/patches/Fix-improve-Unix-socket-passing-detection.patch torsocks-2.0.0/debian/patches/Fix-improve-Unix-socket-passing-detection.patch
--- torsocks-2.0.0/debian/patches/Fix-improve-Unix-socket-passing-detection.patch	1970-01-01 01:00:00.000000000 +0100
+++ torsocks-2.0.0/debian/patches/Fix-improve-Unix-socket-passing-detection.patch	2014-11-10 23:39:39.000000000 +0100
@@ -0,0 +1,759 @@
+From: David Goulet <dgoulet@ev0ke.net>
+Date: Wed, 22 Oct 2014 15:25:23 -0400
+Bug-Debian: https://bugs.debian.org/766306
+Origin: upstream, https://gitweb.torproject.org/torsocks.git/commit/eb80d5cd10d10158b39c344ad035afe8d31a899f
+Subject: Fix: improve Unix socket passing detection
+
+This commit adds the support for the torsocks recvmsg wrapper to detect
+multiple FDs being passed through a Unix socket.
+
+Furthermore, we now don't exit anymore but simply fire a debug message
+and return EACCES to the caller.
+
+Finally, a test is added for inet socket passing detection called
+test_fd_passing.
+
+Signed-off-by: David Goulet <dgoulet@ev0ke.net>
+---
+ src/lib/recv.c          | 132 +++++++++---
+ tests/Makefile.am       |   5 +-
+ tests/test_fd_passing.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/test_list         |   1 +
+ 4 files changed, 631 insertions(+), 27 deletions(-)
+ create mode 100644 tests/test_fd_passing.c
+
+diff --git a/src/lib/recv.c b/src/lib/recv.c
+index 036fa91..b034d72 100644
+--- a/src/lib/recv.c
++++ b/src/lib/recv.c
+@@ -26,21 +26,60 @@
+ TSOCKS_LIBC_DECL(recvmsg, LIBC_RECVMSG_RET_TYPE, LIBC_RECVMSG_SIG)
+ 
+ /*
++ * This is the maximum hardcoded amount of fd that is possible to pass through
++ * a Unix socket in the Linux kernel. On FreeBSD for instance it's MLEN which
++ * is defined to MSIZE (256) minus the msg header size thus way below this
++ * Linux limit. Such a shame there is no way to dynamically get that value or
++ * get it in an exposed ABI...
++ */
++#define SCM_MAX_FD  253
++
++/*
++ * Close all fds in the given array of size count.
++ */
++static void close_fds(int *fds, size_t count)
++{
++	int i;
++
++	for (i = 0; i < count; i++) {
++		tsocks_libc_close(fds[i]);
++	}
++}
++
++/*
+  * Torsocks call for recvmsg(2)
+  *
+  * We only hijack this call to handle the FD passing between process on Unix
+  * socket. If an INET/INET6 socket is recevied, we stop everything because at
+  * that point we can't guarantee traffic going through Tor.
++ *
++ * Note that we don't rely on the given "msg" structure since it's controlled
++ * by the user and might not have been zeroed thus containing wrong values for
++ * ancillary data. Thus, we are going to expect SCM_MAX_FD and see what we can
++ * get from that if any.
+  */
+ LIBC_RECVMSG_RET_TYPE tsocks_recvmsg(LIBC_RECVMSG_SIG)
+ {
+-	int fd;
++	int sock_domain;
++	socklen_t optlen;
+ 	ssize_t ret = 0;
+-	char dummy, recv_fd[CMSG_SPACE(sizeof(fd))];
++	char dummy, recv_fd[CMSG_SPACE(SCM_MAX_FD)];
+ 	struct iovec iov[1];
+ 	struct cmsghdr *cmsg;
+ 	struct msghdr msg_hdr;
+ 
++	/* Don't bother if the socket family is NOT Unix. */
++	optlen = sizeof(sock_domain);
++	ret = getsockopt(sockfd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &optlen);
++	if (ret < 0) {
++		DBG("[recvmsg] Fail getsockopt() on sock %d", sockfd);
++		errno = EBADF;
++		goto error;
++	}
++	if (sock_domain != AF_UNIX) {
++		goto libc;
++	}
++
+ 	memset(&msg_hdr, 0, sizeof(msg_hdr));
+ 
+ 	/* Prepare to receive the structures */
+@@ -55,42 +94,83 @@ LIBC_RECVMSG_RET_TYPE tsocks_recvmsg(LIBC_RECVMSG_SIG)
+ 		/* Just peek the data to inspect the payload for fd. */
+ 		ret = tsocks_libc_recvmsg(sockfd, &msg_hdr, MSG_PEEK);
+ 	} while (ret < 0 && errno == EINTR);
+-
++	if (ret < 0) {
++		/* Use the current errno set by the call above. */
++		goto error;
++	}
+ 	cmsg = CMSG_FIRSTHDR(&msg_hdr);
+ 	if (!cmsg) {
+-		goto end;
++		/* No control message header, safe to pass to libc. */
++		goto libc;
++	}
++	if (msg_hdr.msg_flags & MSG_CTRUNC) {
++		/*
++		 * This means there are actually *more* data in the control thus
++		 * exceeding somehow our hard limit of SCM_MAX_FD. In that case, return
++		 * an error since we can't guarantee anything for socket passing
++		 */
++		errno = EMSGSIZE;
++		goto error;
+ 	}
+ 
+ 	/*
+ 	 * Detecting FD passing, the next snippet of code will check if we get a
+-	 * inet/inet6 socket. If so, everything stops immediately before going
+-	 * further.
++	 * inet/inet6 socket. If so, we are going to close the received socket,
++	 * wipe clean the cmsg payload and return an unauthorized access code.
+ 	 */
+ 	if (cmsg->cmsg_type == SCM_RIGHTS || cmsg->cmsg_level == SOL_SOCKET) {
+-		struct sockaddr addr;
+-		socklen_t addrlen;
+-		sa_family_t family = AF_UNSPEC;
+-
+-		memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+-
+-		/* Get socket protocol family. */
+-		addrlen = sizeof(addr);
+-		ret = getsockname(fd, &addr, &addrlen);
+-		if (ret < 0) {
+-			/* Use the getsockname() errno value. */
+-			goto end;
+-		}
+-
+-		family = addr.sa_family;
+-		if (family == AF_INET || family == AF_INET6) {
+-			ERR("[recvmsg] Inet socket passing detected. Aborting everything! "
+-					"A non Tor socket could be used thus leaking information.");
+-			exit(EXIT_FAILURE);
++		/*
++		 * The kernel control that len value and there is a hard limit so no
++		 * chance here of having a crazy high value that could exhaust the
++		 * stack memory.
++		 */
++		size_t sizeof_fds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
++		int i, fds[sizeof_fds];
++
++		memcpy(&fds, CMSG_DATA(cmsg), sizeof(fds));
++
++		/*
++		 * For each received fds, we will inspect them to see if there is an
++		 * inet socket in there and if so, we have to stop, close everything to
++		 * avoid fd leak and return an error.
++		 */
++		for (i = 0; i < sizeof_fds; i++) {
++			struct sockaddr addr;
++			socklen_t addrlen = sizeof(addr);
++
++			memset(&addr, 0, addrlen);
++
++			/* Get socket protocol family. */
++			ret = getsockname(fds[i], &addr, &addrlen);
++			if (ret < 0) {
++				/* Either a bad fd or not a socket. */
++				continue;
++			}
++
++			if (addr.sa_family == AF_INET || addr.sa_family == AF_INET6) {
++				DBG("[recvmsg] Inet socket passing detected. Denying it.");
++				/* We found socket, close everything and return error. */
++				close_fds(fds, sizeof_fds);
++				/*
++				 * The recv(2) man page does *not* mention that errno value
++				 * however it's acceptable because Linux LSM can return this
++				 * code if the access is denied in the application by a
++				 * security module. We are basically simulating this here.
++				 */
++				errno = EACCES;
++				ret = -1;
++				goto error;
++			}
+ 		}
+ 	}
+ 
+-end:
++	/* At this point, NO socket was detected, continue to the libc safely. */
++
++libc:
+ 	return tsocks_libc_recvmsg(LIBC_RECVMSG_ARGS);
++
++error:
++	return ret;
+ }
+ 
+ /*
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 141ac5e..b916134 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -6,7 +6,7 @@ LIBTAP=$(top_builddir)/tests/utils/tap/libtap.la
+ 
+ LIBTORSOCKS=$(top_builddir)/src/lib/libtorsocks.la
+ 
+-noinst_PROGRAMS = test_dns test_socket test_connect
++noinst_PROGRAMS = test_dns test_socket test_connect test_fd_passing
+ 
+ test_dns_SOURCES = test_dns.c
+ test_dns_LDADD = $(LIBTAP) $(LIBTORSOCKS)
+@@ -17,6 +17,9 @@ test_socket_LDADD = $(LIBTAP) $(LIBTORSOCKS)
+ test_connect_SOURCES = test_connect.c
+ test_connect_LDADD = $(LIBTAP) $(LIBTORSOCKS)
+ 
++test_fd_passing_SOURCES = test_fd_passing.c
++test_fd_passing_LDADD = $(LIBTAP) $(LIBTORSOCKS) -lpthread
++
+ check-am:
+ 	./run.sh test_list
+ 
+diff --git a/tests/test_fd_passing.c b/tests/test_fd_passing.c
+new file mode 100644
+index 0000000..1803126
+--- /dev/null
++++ b/tests/test_fd_passing.c
+@@ -0,0 +1,520 @@
++/*
++ * Copyright (C) 2014 - David Goulet <dgoulet@ev0ke.net>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License, version 2 only, as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
++ * more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc., 51
++ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <arpa/inet.h>
++#include <netinet/in.h>
++#include <pthread.h>
++#include <stdio.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <limits.h>
++#include <sys/un.h>
++
++#include <lib/torsocks.h>
++
++#include <tap/tap.h>
++
++#define NUM_TESTS 5
++
++/*
++ * Indicate if the thread recv is ready. 0 means no, 1 means yes and -1 means
++ * error occured.
++ */
++static volatile int thread_recv_ready;
++
++/* Unix socket for this test. */
++static const char *sockpath = "/tmp/torsocks-unix-fd-passing.sock";
++
++/* Order libtap output. */
++static pthread_mutex_t tsocks_test_log = PTHREAD_MUTEX_INITIALIZER;
++#define OK(cond, args...) \
++	do {	\
++		pthread_mutex_lock(&tsocks_test_log);	\
++		ok(cond, ## args);			\
++		pthread_mutex_unlock(&tsocks_test_log);	\
++	} while (0);
++
++/*
++ * Send buf data of size len. Using sendmsg API.
++ *
++ * Return the size of sent data.
++ */
++static ssize_t send_unix_sock(int sock, void *buf, size_t len)
++{
++	struct msghdr msg;
++	struct iovec iov[1];
++	ssize_t ret = -1;
++
++	memset(&msg, 0, sizeof(msg));
++
++	iov[0].iov_base = buf;
++	iov[0].iov_len = len;
++	msg.msg_iov = iov;
++	msg.msg_iovlen = 1;
++
++	ret = sendmsg(sock, &msg, 0);
++	if (ret < 0) {
++		/*
++		 * Only warn about EPIPE when quiet mode is deactivated.
++		 * We consider EPIPE as expected.
++		 */
++		if (errno != EPIPE) {
++			perror("sendmsg");
++		}
++	}
++
++	return ret;
++}
++
++static ssize_t send_fds_unix_sock(int sock, int *fds, size_t nb_fd)
++{
++	struct msghdr msg;
++	struct cmsghdr *cmptr;
++	struct iovec iov[1];
++	ssize_t ret = -1;
++	unsigned int sizeof_fds = nb_fd * sizeof(int);
++	char tmp[CMSG_SPACE(sizeof_fds)];
++	char dummy = 0;
++
++	memset(&msg, 0, sizeof(msg));
++
++	msg.msg_control = (caddr_t)tmp;
++	msg.msg_controllen = CMSG_LEN(sizeof_fds);
++
++	cmptr = CMSG_FIRSTHDR(&msg);
++	cmptr->cmsg_level = SOL_SOCKET;
++	cmptr->cmsg_type = SCM_RIGHTS;
++	cmptr->cmsg_len = CMSG_LEN(sizeof_fds);
++	memcpy(CMSG_DATA(cmptr), fds, sizeof_fds);
++	/* Sum of the length of all control messages in the buffer: */
++	msg.msg_controllen = cmptr->cmsg_len;
++
++	iov[0].iov_base = &dummy;
++	iov[0].iov_len = 1;
++	msg.msg_iov = iov;
++	msg.msg_iovlen = 1;
++
++	do {
++		ret = sendmsg(sock, &msg, 0);
++	} while (ret < 0 && errno == EINTR);
++	if (ret < 0) {
++		/*
++		 * Only warn about EPIPE when quiet mode is deactivated.
++		 * We consider EPIPE as expected.
++		 */
++		if (errno != EPIPE) {
++			perror("sendmsg");
++		}
++	}
++	return ret;
++}
++
++/*
++ * Receive data of size len in put that data into the buf param. Using recvmsg
++ * API.
++ *
++ * Return the size of received data.
++ */
++static ssize_t recv_unix_sock(int sock, void *buf, size_t len)
++{
++	struct msghdr msg;
++	struct iovec iov[1];
++	ssize_t ret = -1;
++	size_t len_last;
++
++	memset(&msg, 0, sizeof(msg));
++
++	iov[0].iov_base = buf;
++	iov[0].iov_len = len;
++	msg.msg_iov = iov;
++	msg.msg_iovlen = 1;
++
++	do {
++		len_last = iov[0].iov_len;
++		ret = recvmsg(sock, &msg, 0);
++		if (ret > 0) {
++			iov[0].iov_base += ret;
++			iov[0].iov_len -= ret;
++			assert(ret <= len_last);
++		}
++	} while ((ret > 0 && ret < len_last) || (ret < 0 && errno == EINTR));
++	if (ret < 0) {
++		perror("recvmsg");
++	} else if (ret > 0) {
++		ret = len;
++	}
++	/* Else ret = 0 meaning an orderly shutdown. */
++
++	return ret;
++}
++
++/*
++ * Recv a message accompanied by fd(s) from a unix socket.
++ *
++ * Returns the size of received data, or negative error value.
++ *
++ * Expect at most "nb_fd" file descriptors. Returns the number of fd
++ * actually received in nb_fd.
++ */
++static ssize_t recv_fds_unix_sock(int sock, int *fds, size_t nb_fd)
++{
++	struct iovec iov[1];
++	ssize_t ret = 0;
++	struct cmsghdr *cmsg;
++	size_t sizeof_fds = nb_fd * sizeof(int);
++	char recv_fd[CMSG_SPACE(sizeof_fds)];
++	struct msghdr msg;
++	char dummy;
++
++	memset(&msg, 0, sizeof(msg));
++
++	/* Prepare to receive the structures */
++	iov[0].iov_base = &dummy;
++	iov[0].iov_len = 1;
++	msg.msg_iov = iov;
++	msg.msg_iovlen = 1;
++	msg.msg_control = recv_fd;
++	msg.msg_controllen = sizeof(recv_fd);
++
++	do {
++		ret = recvmsg(sock, &msg, 0);
++	} while (ret < 0 && errno == EINTR);
++	if (ret < 0) {
++		goto end;
++	}
++	if (ret != 1) {
++		fprintf(stderr, "Error: Received %zd bytes, expected %d\n",
++				ret, 1);
++		goto end;
++	}
++	if (msg.msg_flags & MSG_CTRUNC) {
++		fprintf(stderr, "Error: Control message truncated.\n");
++		ret = -1;
++		goto end;
++	}
++	cmsg = CMSG_FIRSTHDR(&msg);
++	if (!cmsg) {
++		fprintf(stderr, "Error: Invalid control message header\n");
++		ret = -1;
++		goto end;
++	}
++	if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
++		fprintf(stderr, "Didn't received any fd\n");
++		ret = -1;
++		goto end;
++	}
++	if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) {
++		fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n",
++				(size_t) cmsg->cmsg_len, (size_t) CMSG_LEN(sizeof_fds));
++		ret = -1;
++		goto end;
++	}
++	memcpy(fds, CMSG_DATA(cmsg), sizeof_fds);
++	ret = sizeof_fds;
++end:
++	return ret;
++}
++
++/*
++ * Connect to unix socket using the path name.
++ */
++static int connect_unix_sock(const char *pathname)
++{
++	struct sockaddr_un sun;
++	int fd, ret, closeret;
++
++	fd = socket(PF_UNIX, SOCK_STREAM, 0);
++	if (fd < 0) {
++		perror("socket");
++		ret = fd;
++		goto error;
++	}
++
++	memset(&sun, 0, sizeof(sun));
++	sun.sun_family = AF_UNIX;
++	strncpy(sun.sun_path, pathname, sizeof(sun.sun_path));
++	sun.sun_path[sizeof(sun.sun_path) - 1] = '\0';
++
++	ret = connect(fd, (struct sockaddr *) &sun, sizeof(sun));
++	if (ret < 0) {
++		/*
++		 * Don't print message on connect error, because connect is used in
++		 * normal execution to detect if sessiond is alive.
++		 */
++		goto error_connect;
++	}
++
++	return fd;
++
++error_connect:
++	closeret = close(fd);
++	if (closeret) {
++		perror("close");
++	}
++error:
++	return ret;
++}
++
++/*
++ * Creates a AF_UNIX local socket using pathname bind the socket upon creation
++ * and return the fd.
++ */
++static int create_unix_sock(const char *pathname)
++{
++	struct sockaddr_un sun;
++	int fd;
++	int ret = -1;
++
++	/* Create server socket */
++	if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
++		perror("socket");
++		goto error;
++	}
++
++	memset(&sun, 0, sizeof(sun));
++	sun.sun_family = AF_UNIX;
++	strncpy(sun.sun_path, pathname, sizeof(sun.sun_path));
++	sun.sun_path[sizeof(sun.sun_path) - 1] = '\0';
++
++	/* Unlink the old file if present */
++	(void) unlink(pathname);
++	ret = bind(fd, (struct sockaddr *) &sun, sizeof(sun));
++	if (ret < 0) {
++		perror("bind");
++		goto error;
++	}
++
++	return fd;
++
++error:
++	return ret;
++}
++
++/*
++ * Do an accept(2) on the sock and return the new file descriptor. The socket
++ * MUST be bind(2) before.
++ */
++static int accept_unix_sock(int sock)
++{
++	int new_fd;
++	struct sockaddr_un sun;
++	socklen_t len = 0;
++
++	/* Blocking call */
++	new_fd = accept(sock, (struct sockaddr *) &sun, &len);
++	if (new_fd < 0) {
++		perror("accept");
++	}
++
++	return new_fd;
++}
++
++void *thread_recv(void *data)
++{
++	int ret, new_sock, sock, fds[3] = {-1, -1, -1};
++	char buf[4];
++	ssize_t len;
++
++	sock = create_unix_sock(sockpath);
++	if (sock < 0) {
++		fail("Create unix socket at %s", sockpath);
++		goto error;
++	}
++
++	OK(sock >= 0, "Unix socket %d created at %s", sock, sockpath);
++
++	ret = listen(sock, 10);
++	if (ret < 0) {
++		fail("Listen on unix socket %d", sock);
++		goto error;
++	}
++
++	/* Notify we are ready to test. */
++	thread_recv_ready = 1;
++
++	new_sock = accept_unix_sock(sock);
++	if (new_sock < 0) {
++		fail("Accept on unix sock %d", sock);
++		close(sock);
++		goto error;
++	}
++
++	/* First receive a normal message saying "hello" to make sure the recvmsg
++	 * call is not borked. */
++	len = recv_unix_sock(new_sock, buf, sizeof(buf));
++	if (len < 0) {
++		fail("Recv normal data failed");
++		goto error;
++	}
++	OK(len == sizeof(buf) &&
++		strncmp(buf, "hello", sizeof(buf)) == 0,
++		"Data received successfully");
++
++	len = recv_fds_unix_sock(new_sock, fds, 3);
++	if (len < 0) {
++		/* This is suppose to fail with a errno set to EACCESS. */
++		OK(errno == EACCES,
++				"Passing INET socket denied.");
++	} else {
++		fail("Received INET socket through the unix socket");
++	}
++
++	close(sock);
++	close(new_sock);
++	close(fds[0]);
++	close(fds[1]);
++	close(fds[2]);
++
++error:
++	thread_recv_ready = -1;
++	return NULL;
++}
++
++void *thread_send(void *data)
++{
++	int sock, fds[3], pipe_fds[2];
++	ssize_t len;
++
++	sock = connect_unix_sock(sockpath);
++	if (sock < 0) {
++		fail("Unable to connect to unix socket at %s", sockpath);
++		goto error;
++	}
++
++	if (pipe(pipe_fds) < 0) {
++		fail("Unable to create pipe");
++		goto error;
++	}
++
++	/* First send regular data. */
++	len = send_unix_sock(sock, "hello", 4);
++	if (len < 0) {
++		fail("Sending regular data.");
++		goto error;
++	}
++
++	/*
++	 * We are going to pass 3 fds, two of them are pipse in position 0 and 2
++	 * and the inet socket is at position 1.
++	 */
++	fds[0] = pipe_fds[0];
++	fds[1] = *((int *)data);
++	fds[2] = pipe_fds[1];
++
++	len = send_fds_unix_sock(sock, fds, 3);
++	if (len < 0) {
++		fail("Send inet socket through Unix sock");
++		goto error;
++	}
++	OK(len == 1, "Inet socket %d sent successfully.", fds[1]);
++
++error:
++	if (sock >= 0) {
++		close(sock);
++	}
++	if (pipe_fds[0] >= 0) {
++		close(pipe_fds[0]);
++	}
++	if (pipe_fds[1] >= 0) {
++		close(pipe_fds[1]);
++	}
++	return NULL;
++}
++
++/*
++ * This test will spawn two thread, one accepting a Unix socket connection
++ * which will recv the fd(s). The second thread will connect and send the fds.
++ * Usually this is between processes but for the sake of the test threads are
++ * enough.
++ */
++static void test_inet_socket(void)
++{
++	int ret, i, inet_sock = -1;
++	void *status;
++	pthread_t th[2];
++	struct sockaddr_in addr;
++	const char *ip = "93.95.227.222";
++
++	/*
++	 * First of all, we are going to try to create an inet socket to a public
++	 * known IP being www.torproject.org --> 93.95.227.222.
++	 */
++	inet_sock = socket(AF_INET, SOCK_STREAM, 0);
++	if (inet_sock < 0) {
++		fail("Creating inet socket");
++		goto error;
++	}
++
++	addr.sin_family = AF_INET;
++	addr.sin_port = htons(443);
++	inet_pton(addr.sin_family, ip, &addr.sin_addr);
++	memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
++
++	ret = connect(inet_sock, (struct sockaddr *) &addr, sizeof(addr));
++	if (ret < 0) {
++		fail("Unable to connect inet socket");
++		goto error;
++	}
++
++	OK(!ret, "Inet socket %d created connected to %s", inet_sock, ip);
++
++	ret = pthread_create(&th[0], NULL, thread_recv, NULL);
++	if (ret < 0) {
++		fail("pthread_create thread recv");
++		goto error;
++	}
++
++	/* Active wait for the thread recv to be ready. */
++	while (thread_recv_ready == 0) {
++		continue;
++	}
++
++	if (thread_recv_ready == -1) {
++		goto error;
++	}
++
++	ret = pthread_create(&th[1], NULL, thread_send, (void *) &inet_sock);
++	if (ret < 0) {
++		fail("pthread_create thread send");
++		goto error;
++	}
++
++	for (i = 0; i < 2; i++) {
++		ret = pthread_join(th[i], &status);
++		if (ret < 0) {
++			perror("pthread_join");
++		}
++	}
++
++error:
++	if (inet_sock >= 0) {
++		close(inet_sock);
++	}
++	unlink(sockpath);
++	return;
++}
++
++int main(int argc, char **argv)
++{
++	/* Libtap call for the number of tests planned. */
++	plan_tests(NUM_TESTS);
++
++	test_inet_socket();
++
++    return 0;
++}
+diff --git a/tests/test_list b/tests/test_list
+index bb812b1..fb320db 100644
+--- a/tests/test_list
++++ b/tests/test_list
+@@ -1,5 +1,6 @@
+ ./test_connect
+ ./test_dns
++./test_fd_passing
+ ./test_socket
+ ./unit/test_onion
+ ./unit/test_connection
diff -Nru torsocks-2.0.0/debian/patches/Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch torsocks-2.0.0/debian/patches/Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch
--- torsocks-2.0.0/debian/patches/Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch	1970-01-01 01:00:00.000000000 +0100
+++ torsocks-2.0.0/debian/patches/Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch	2014-11-10 23:39:39.000000000 +0100
@@ -0,0 +1,51 @@
+From: David Goulet <dgoulet@ev0ke.net>
+Date: Sat, 25 Oct 2014 12:33:00 -0400
+Subject: Fix: use getsockname instead of getsockopt to get socket family
+Bug-Debian: https://bugs.debian.org/768140
+Bug: https://trac.torproject.org/projects/tor/ticket/13571
+Origin: upstream, https://gitweb.torproject.org/torsocks.git/commit/8406ef884df2fb7dbdfd475ea7e548a40dcdce3f
+
+Seems that OS X 10.10 Yosemite does not support the SO_DOMAIN anymore so
+use a known compatible call to get the socket family in recv(2).
+
+Fixes #13571
+
+Signed-off-by: David Goulet <dgoulet@ev0ke.net>
+---
+ src/lib/recv.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/lib/recv.c b/src/lib/recv.c
+index b034d72..6e8a20a 100644
+--- a/src/lib/recv.c
++++ b/src/lib/recv.c
+@@ -60,23 +60,23 @@ static void close_fds(int *fds, size_t count)
+  */
+ LIBC_RECVMSG_RET_TYPE tsocks_recvmsg(LIBC_RECVMSG_SIG)
+ {
+-	int sock_domain;
+-	socklen_t optlen;
++	socklen_t addrlen;
+ 	ssize_t ret = 0;
+ 	char dummy, recv_fd[CMSG_SPACE(SCM_MAX_FD)];
+ 	struct iovec iov[1];
+ 	struct cmsghdr *cmsg;
+ 	struct msghdr msg_hdr;
++	struct sockaddr addr;
+ 
+ 	/* Don't bother if the socket family is NOT Unix. */
+-	optlen = sizeof(sock_domain);
+-	ret = getsockopt(sockfd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &optlen);
++	addrlen = sizeof(addr);
++	ret = getsockname(sockfd, &addr, &addrlen);
+ 	if (ret < 0) {
+-		DBG("[recvmsg] Fail getsockopt() on sock %d", sockfd);
++		DBG("[recvmsg] Fail getsockname() on sock %d", sockfd);
+ 		errno = EBADF;
+ 		goto error;
+ 	}
+-	if (sock_domain != AF_UNIX) {
++	if (addr.sa_family != AF_UNIX) {
+ 		goto libc;
+ 	}
+ 
diff -Nru torsocks-2.0.0/debian/patches/series torsocks-2.0.0/debian/patches/series
--- torsocks-2.0.0/debian/patches/series	2014-08-24 23:24:58.000000000 +0200
+++ torsocks-2.0.0/debian/patches/series	2014-11-10 23:39:39.000000000 +0100
@@ -1 +1,3 @@
+Fix-improve-Unix-socket-passing-detection.patch
+Fix-use-getsockname-instead-of-getsockopt-to-get-soc.patch
 exclude_test_requiring_network.patch

Reply to: