fdpass.c revision 300621f765cdee3e05664169b67aedba9982fe60
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2004 Timo Sirainen */
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen/*
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen fdpass.c - File descriptor passing between processes via UNIX sockets
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen This isn't fully portable, but pretty much all UNIXes nowadays should
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen support this. If you're having runtime problems, check the end of fd_read()
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen and play with the if condition.
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen If this file doesn't compile at all, you should check if this is supported
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen in your system at all. It may require some extra #define to enable it.
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen If not, you're pretty much out of luck. Cygwin didn't last I checked.
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen*/
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#define _XPG4_2
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__)
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen# define _XOPEN_SOURCE 4 /* for IRIX */
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen#endif
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen#if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED)
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen# define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen#endif
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen#include "lib.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "fdpass.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include <sys/socket.h>
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include <sys/un.h>
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include <sys/uio.h>
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen/* Solaris uses 32bit socklen_t as cmsg_len, so with Solaris we use
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen _CMSG_DATA_ALIGN() macro to do the alignment for us.
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen Perhaps the best solution would be to change sizeof(size_t) calculations
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen to sizeof(cmsg->cmsg_len)? At least if other OSes have similiar problems.. */
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen#ifndef CMSG_SPACE
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen# if defined(_CMSG_DATA_ALIGN) && defined(_CMSG_HDR_ALIGN) /* for Solaris */
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# define CMSG_SPACE(len) \
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (_CMSG_DATA_ALIGN(len) + _CMSG_DATA_ALIGN(sizeof(struct cmsghdr)))
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# define CMSG_LEN(len) \
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (_CMSG_DATA_ALIGN(sizeof(struct cmsghdr)) + (len))
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen# else
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen# define CMSG_ALIGN(len) \
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1))
3b32bc12710240f86465a00fbb2bd1ef030e6c40Timo Sirainen# define CMSG_SPACE(len) \
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen (CMSG_ALIGN(len) + CMSG_ALIGN(sizeof(struct cmsghdr)))
d22301419109ed4a38351715e6760011421dadecTimo Sirainen# define CMSG_LEN(len) \
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
d22301419109ed4a38351715e6760011421dadecTimo Sirainen# endif
d22301419109ed4a38351715e6760011421dadecTimo Sirainen#endif
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#ifdef SCM_RIGHTS
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenssize_t fd_send(int handle, int send_fd, const void *data, size_t size)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen{
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct msghdr msg;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct iovec iov;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct cmsghdr *cmsg;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen char buf[CMSG_SPACE(sizeof(int))];
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen /* at least one byte is required to be sent with fd passing */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen memset(&msg, 0, sizeof (struct msghdr));
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen iov.iov_base = (void *) data;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen iov.iov_len = size;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen msg.msg_iov = &iov;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen msg.msg_iovlen = 1;
b42697a5749b85659a24316d97f1c208d469e4e8Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (send_fd != -1) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen /* set the control and controllen before CMSG_FIRSTHDR() */
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen memset(buf, 0, sizeof(buf));
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen msg.msg_control = buf;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen msg.msg_controllen = sizeof(buf);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen cmsg->cmsg_level = SOL_SOCKET;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen cmsg->cmsg_type = SCM_RIGHTS;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen cmsg->cmsg_len = CMSG_LEN(sizeof(int));
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen *((int *) CMSG_DATA(cmsg)) = send_fd;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen /* set the real length we want to use. it's different than
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen sizeof(buf) in 64bit systems. */
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen msg.msg_controllen = cmsg->cmsg_len;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen }
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen return sendmsg(handle, &msg, 0);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen}
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#ifdef __osf__
3d6fdafca17c073606b63745ca8638e035e871f4Timo Sirainen# define CHECK_MSG(msg) TRUE /* Tru64 */
3d6fdafca17c073606b63745ca8638e035e871f4Timo Sirainen#else
3d6fdafca17c073606b63745ca8638e035e871f4Timo Sirainen# define CHECK_MSG(msg) (msg).msg_controllen >= CMSG_SPACE(sizeof(int))
3d6fdafca17c073606b63745ca8638e035e871f4Timo Sirainen#endif
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen#ifdef LINUX20
9ffdc9d18870acef2e4dde99715d8528ff4b080dTimo Sirainen/* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen attacks possible so don't do it unless you really have to. */
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# define CHECK_CMSG(cmsg) ((cmsg) != NULL)
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen#else
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# define CHECK_CMSG(cmsg) \
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen ((cmsg) != NULL && \
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen#endif
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainenssize_t fd_read(int handle, void *data, size_t size, int *fd)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct msghdr msg;
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen struct iovec iov;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen struct cmsghdr *cmsg;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ssize_t ret;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen char buf[CMSG_SPACE(sizeof(int))];
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen
dca6d617a23e3f93af3b8df59acb46478179fe55Timo Sirainen memset(&msg, 0, sizeof (struct msghdr));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
1108376e39a19912e8394e64e19b1bc6f6691cf6Timo Sirainen iov.iov_base = data;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen iov.iov_len = size;
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen msg.msg_iov = &iov;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen msg.msg_iovlen = 1;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen memset(buf, 0, sizeof(buf));
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen msg.msg_control = buf;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen msg.msg_controllen = sizeof(buf);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen
306cfd77100131c08b243de10f6d40500f4c27c6Timo Sirainen ret = recvmsg(handle, &msg, 0);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (ret <= 0) {
09c08fad8e7cc694a6c8d1711e67839acd3a2f04Timo Sirainen *fd = -1;
438f12d7a776da695019114884b48188d94613efTimo Sirainen return ret;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen }
9ffdc9d18870acef2e4dde99715d8528ff4b080dTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* at least one byte transferred - we should have the fd now.
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen do extra checks to make sure it really is an fd that is being
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen transferred to avoid potential DoS conditions. some systems don't
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen set all these values correctly however so CHECK_MSG() and
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen CHECK_CMSG() are somewhat system dependent */
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen if (!CHECK_MSG(msg) || !CHECK_CMSG(cmsg))
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen *fd = -1;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen else
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen *fd = *((int *) CMSG_DATA(cmsg));
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen return ret;
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen}
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#else
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# ifdef __GNUC__
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen# warning SCM_RIGHTS not supported, privilege separation not possible
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen# endif
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainenssize_t fd_send(int handle __attr_unused__, int send_fd __attr_unused__,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen const void *data __attr_unused__, size_t size __attr_unused__)
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen{
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen errno = ENOSYS;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen return -1;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen}
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainenssize_t fd_read(int handle __attr_unused__, void *data __attr_unused__,
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen size_t size __attr_unused__, int *fd __attr_unused__)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen errno = ENOSYS;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen return -1;
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen}
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen#endif
c6a57378d3c54988f525f81e19c0c5d132a0770dTimo Sirainen