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