Example #1
0
File: recv.c Project: lgrz/torsocks
/*
 * 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.
 */
LIBC_RECVMSG_RET_TYPE tsocks_recvmsg(LIBC_RECVMSG_SIG)
{
	int fd;
	ssize_t ret = 0;
	char dummy, recv_fd[CMSG_SPACE(sizeof(fd))];
	struct iovec iov[1];
	struct cmsghdr *cmsg;
	struct msghdr msg_hdr;

	memset(&msg_hdr, 0, sizeof(msg_hdr));

	/* Prepare to receive the structures */
	iov[0].iov_base = &dummy;
	iov[0].iov_len = 1;
	msg_hdr.msg_iov = iov;
	msg_hdr.msg_iovlen = 1;
	msg_hdr.msg_control = recv_fd;
	msg_hdr.msg_controllen = sizeof(recv_fd);

	do {
		/* Just peek the data to inspect the payload for fd. */
		ret = tsocks_libc_recvmsg(sockfd, &msg_hdr, MSG_PEEK);
	} while (ret < 0 && errno == EINTR);

	cmsg = CMSG_FIRSTHDR(&msg_hdr);
	if (!cmsg) {
		goto end;
	}

	/*
	 * 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.
	 */
	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);
		}
	}

end:
	return tsocks_libc_recvmsg(LIBC_RECVMSG_ARGS);
}
Example #2
0
/*
 * 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)
{
	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. */
	addrlen = sizeof(addr);
	ret = getsockname(sockfd, &addr, &addrlen);
	if (ret < 0) {
		DBG("[recvmsg] Fail getsockname() on sock %d", sockfd);
		errno = EBADF;
		goto error;
	}
	if (addr.sa_family != AF_UNIX) {
		goto libc;
	}

	memset(&msg_hdr, 0, sizeof(msg_hdr));

	/* Prepare to receive the structures */
	iov[0].iov_base = &dummy;
	iov[0].iov_len = 1;
	msg_hdr.msg_iov = iov;
	msg_hdr.msg_iovlen = 1;
	msg_hdr.msg_control = recv_fd;
	msg_hdr.msg_controllen = sizeof(recv_fd);

	do {
		/* 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) {
		/* 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, 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) {
		/*
		 * 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;
			}
		}
	}

	/* At this point, NO socket was detected, continue to the libc safely. */

libc:
	return tsocks_libc_recvmsg(LIBC_RECVMSG_ARGS);

error:
	return ret;
}