fdpass.c revision b7f61a644ca10a271e756201144b68b461a05bca
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger/*
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen fdpass.c - File descriptor passing between processes via UNIX sockets
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen This isn't fully portable, but pretty much all UNIXes nowadays should
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen support this. If you're having runtime problems, check the end of fd_read()
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen and play with the if condition.
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen If this file doesn't compile at all, you should check if this is supported
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen in your system at all. It may require some extra #define to enable it.
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen If not, you're pretty much out of luck. Cygwin didn't last I checked.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen*/
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#define _XPG4_2
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen# define _XOPEN_SOURCE 4 /* for IRIX */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#endif
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
b624773984e35dd894db8dff976c1a2114c70782Timo Sirainen
b624773984e35dd894db8dff976c1a2114c70782Timo Sirainen#if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED)
12d38e76ba7f70d6219c89ec7416fea0d5de7e02Timo Sirainen# define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */
b624773984e35dd894db8dff976c1a2114c70782Timo Sirainen#endif
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "lib.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include "fdpass.h"
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <sys/socket.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <sys/un.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#include <sys/uio.h>
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
3a7113e3e2dac0e333e1a3f62af7d682896f59c6Timo Sirainen#ifndef CMSG_SPACE
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen# if defined(_CMSG_DATA_ALIGN) && defined(_CMSG_HDR_ALIGN) /* for Solaris */
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen# define CMSG_ALIGN(len) _CMSG_DATA_ALIGN(len)
be5c76fabc7439fd33bc799bc3ab3f570799977bTimo Sirainen# define CMSG_SPACE(len) \
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen (_CMSG_DATA_ALIGN(len) + _CMSG_HDR_ALIGN(sizeof(struct cmsghdr)))
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen# define CMSG_LEN(len) \
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen (_CMSG_HDR_ALIGN(sizeof(struct cmsghdr)) + (len))
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen# else
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen# define CMSG_ALIGN(len) \
e9371f899a3d4207a0ffd3923ea5ec7250cf5e75Timo Sirainen (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1))
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen# define CMSG_SPACE(len) \
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen (CMSG_ALIGN(len) + CMSG_ALIGN(sizeof(struct cmsghdr)))
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen# define CMSG_LEN(len) \
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
788a0754cfd38dcfec1902844b085e4e84cfe7e6Timo Sirainen# endif
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen#endif
788a0754cfd38dcfec1902844b085e4e84cfe7e6Timo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen#ifdef SCM_RIGHTS
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenssize_t fd_send(int handle, int send_fd, const void *data, size_t size)
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen{
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen struct msghdr msg;
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen struct iovec iov;
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen struct cmsghdr *cmsg;
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen char buf[CMSG_SPACE(sizeof(int))];
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen /* at least one byte is required to be sent with fd passing */
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen memset(&msg, 0, sizeof (struct msghdr));
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen
f93c833d644ecff0b0f80bee4f1cdde3e697b5c8Timo Sirainen iov.iov_base = (void *) data;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen iov.iov_len = size;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen msg.msg_iov = &iov;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen msg.msg_iovlen = 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen if (send_fd != -1) {
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* set the control and controllen before CMSG_FIRSTHDR() */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen msg.msg_control = buf;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen msg.msg_controllen = sizeof(buf);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen cmsg->cmsg_level = SOL_SOCKET;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen cmsg->cmsg_type = SCM_RIGHTS;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen cmsg->cmsg_len = CMSG_LEN(sizeof(int));
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen *((int *) CMSG_DATA(cmsg)) = send_fd;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen /* set the real length we want to use. it's different than
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen sizeof(buf) in 64bit systems. */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen msg.msg_controllen = cmsg->cmsg_len;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen }
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen return sendmsg(handle, &msg, 0);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#ifdef __osf__
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen# define CHECK_MSG(msg) TRUE /* Tru64 */
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen#else
83d2e37f065eabe38dc92db485c5ca39ee43ce05Timo Sirainen# define CHECK_MSG(msg) (msg).msg_controllen >= CMSG_SPACE(sizeof(int))
83d2e37f065eabe38dc92db485c5ca39ee43ce05Timo Sirainen#endif
83d2e37f065eabe38dc92db485c5ca39ee43ce05Timo Sirainen
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen#ifdef LINUX20
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen/* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some
7569ab8537418b7fc369265f26595b0ef9e4cb35Timo Sirainen attacks possible so don't do it unless you really have to. */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen# define CHECK_CMSG(cmsg) ((cmsg) != NULL)
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen#else
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen# define CHECK_CMSG(cmsg) \
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen ((cmsg) != NULL && \
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS)
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen#endif
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen
798cfe56c9871262770384da1239162b3800cce1Timo Sirainenssize_t fd_read(int handle, void *data, size_t size, int *fd)
3a7113e3e2dac0e333e1a3f62af7d682896f59c6Timo Sirainen{
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen struct msghdr msg;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen struct iovec iov;
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen struct cmsghdr *cmsg;
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen ssize_t ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen char buf[CMSG_SPACE(sizeof(int))];
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen memset(&msg, 0, sizeof (struct msghdr));
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
31633d676642b83305b8d46da495d9bb4e2d1ff8Timo Sirainen iov.iov_base = data;
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen iov.iov_len = size;
47bb4a7615c85f212f061499f04f121d6d625387Timo Sirainen
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen msg.msg_iov = &iov;
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen msg.msg_iovlen = 1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen memset(buf, 0, sizeof(buf));
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen msg.msg_control = buf;
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen msg.msg_controllen = sizeof(buf);
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen ret = recvmsg(handle, &msg, 0);
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen if (ret <= 0) {
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen *fd = -1;
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen return ret;
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen }
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen /* at least one byte transferred - we should have the fd now.
b0e9375a1ff97c9c7d40655922af5ccc73ecaa76Timo Sirainen do extra checks to make sure it really is an fd that is being
ee6df9526e9716b3f1734d85b566e00fc41208bcTimo Sirainen transferred to avoid potential DoS conditions. some systems don't
33dd58ab84a020c4f061d2f6031eb6d4c168df1bTimo Sirainen set all these values correctly however so CHECK_MSG() and
5d4855d7b4dcffb6975ed8e3c9c376dac74e5c8aTimo Sirainen CHECK_CMSG() are somewhat system dependent */
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
798cfe56c9871262770384da1239162b3800cce1Timo Sirainen if (!CHECK_MSG(msg) || !CHECK_CMSG(cmsg))
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen *fd = -1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen else
82f53ea81671bcc7b9bf24a34b04a4ba2752efd3Timo Sirainen *fd = *((int *) CMSG_DATA(cmsg));
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return ret;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen
ce6b6093957885a74fd6e85c18801dbb727d61ecTimo Sirainen#else
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen# ifdef __GNUC__
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen# warning SCM_RIGHTS not supported, privilege separation not possible
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen# endif
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainenssize_t fd_send(int handle __attr_unused__, int send_fd __attr_unused__,
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen const void *data __attr_unused__, size_t size __attr_unused__)
adc409a7ac9689d3baf811712ad5a5432cab2d87Timo Sirainen{
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen errno = ENOSYS;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen return -1;
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen}
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainenssize_t fd_read(int handle __attr_unused__, void *data __attr_unused__,
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen size_t size __attr_unused__, int *fd __attr_unused__)
ce6b6093957885a74fd6e85c18801dbb727d61ecTimo Sirainen{
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen errno = ENOSYS;
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen return -1;
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen}
31597236d79ac38a5cea7ab65a9d0a3df64ed201Timo Sirainen#endif
2e29e4797a48d78d669821722bdb54fd0a1d3b94Timo Sirainen