fdpass.c revision d0f2a0acfe7b88cce6d5ec94452002cd01e99595
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2003 Timo Sirainen */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
80c1d98d3638b71e57a39cafa88b9122bf8169c6Timo Sirainen fdpass.c - File descriptor passing between processes via UNIX sockets
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen This isn't fully portable, but pretty much all UNIXes nowadays should
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen support this. If you're having runtime problems, check the end of fd_read()
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen and play with the if condition.
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen If this file doesn't compile at all, you should check if this is supported
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen in your system at all. It may require some extra #define to enable it.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen If not, you're pretty much out of luck. Cygwin didn't last I checked.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen*/
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define _XPG4_2
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen#if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__)
8887bf3757d51d73887dd20b1db3334d867d3817Timo Sirainen# define _XOPEN_SOURCE 4 /* for IRIX */
8887bf3757d51d73887dd20b1db3334d867d3817Timo Sirainen#endif
369a1084c500a9df7448ffa9409ce32e42060bc2Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen#if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED)
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen# define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen#endif
589a9c6e8ee22071c14171c04bfc6bfe17121871Timo Sirainen
589a9c6e8ee22071c14171c04bfc6bfe17121871Timo Sirainen#include "lib.h"
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen#include "fdpass.h"
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen#include <sys/socket.h>
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen#include <sys/un.h>
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainen#include <sys/uio.h>
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifndef CMSG_SPACE
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# define CMSG_ALIGN(len) \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# define CMSG_SPACE(len) \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (CMSG_ALIGN(len) + CMSG_ALIGN(sizeof(struct cmsghdr)))
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen# define CMSG_LEN(len) \
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
6ec7cf71ccd0eed1f9cc1b0bda8960796b04160bTimo Sirainen#endif
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef SCM_RIGHTS
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
db5164c9a1129af0cfb11fc18d88da361a8011fbTimo Sirainenssize_t fd_send(int handle, int send_fd, const void *data, size_t size)
db5164c9a1129af0cfb11fc18d88da361a8011fbTimo Sirainen{
db5164c9a1129af0cfb11fc18d88da361a8011fbTimo Sirainen struct msghdr msg;
db5164c9a1129af0cfb11fc18d88da361a8011fbTimo Sirainen struct iovec iov;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct cmsghdr *cmsg;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char buf[CMSG_SPACE(sizeof(int))];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* at least one byte is required to be sent with fd passing */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&msg, 0, sizeof (struct msghdr));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen iov.iov_base = (void *) data;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen iov.iov_len = size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen msg.msg_iov = &iov;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen msg.msg_iovlen = 1;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (send_fd != -1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set the control and controllen before CMSG_FIRSTHDR() */
811f2e26d9782d9cb99fdf82e18ffa0a77564fe2Timo Sirainen msg.msg_control = buf;
811f2e26d9782d9cb99fdf82e18ffa0a77564fe2Timo Sirainen msg.msg_controllen = sizeof(buf);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cmsg->cmsg_level = SOL_SOCKET;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cmsg->cmsg_type = SCM_RIGHTS;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cmsg->cmsg_len = CMSG_LEN(sizeof(int));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *((int *) CMSG_DATA(cmsg)) = send_fd;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* set the real length we want to use. it's different than
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen sizeof(buf) in 64bit systems. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen msg.msg_controllen = cmsg->cmsg_len;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen return sendmsg(handle, &msg, 0);
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef __osf__
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# define CHECK_MSG(msg) TRUE /* Tru64 */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#else
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen# define CHECK_MSG(msg) (msg).msg_controllen >= CMSG_SPACE(sizeof(int))
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen#endif
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen#ifdef LINUX20
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen attacks possible so don't do it unless you really have to. */
2a734f36105e33ab452d057df6bc7a2b7d9f96f0Timo Sirainen# define CHECK_CMSG(cmsg) ((cmsg) != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#else
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen# define CHECK_CMSG(cmsg) \
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen ((cmsg) != NULL && (cmsg)->cmsg_len >= CMSG_LEN(sizeof(int)) && \
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#endif
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenssize_t fd_read(int handle, void *data, size_t size, int *fd)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct msghdr msg;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct iovec iov;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct cmsghdr *cmsg;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen ssize_t ret;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen char buf[CMSG_SPACE(sizeof(int))];
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen i_assert(size > 0 && size < SSIZE_T_MAX);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen memset(&msg, 0, sizeof (struct msghdr));
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen iov.iov_base = data;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen iov.iov_len = size;
b9c44feadade0481b957f2978640afb3317bd1dfTimo Sirainen
b9c44feadade0481b957f2978640afb3317bd1dfTimo Sirainen msg.msg_iov = &iov;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen msg.msg_iovlen = 1;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen memset(buf, 0, sizeof(buf));
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen msg.msg_control = buf;
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen msg.msg_controllen = sizeof(buf);
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen ret = recvmsg(handle, &msg, 0);
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen if (ret <= 0) {
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen *fd = -1;
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen return ret;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen }
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* at least one byte transferred - we should have the fd now.
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen do extra checks to make sure it really is an fd that is being
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen transferred to avoid potential DoS conditions. some systems don't
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen set all these values correctly however so CHECK_MSG() and
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen CHECK_CMSG() are somewhat system dependent */
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen cmsg = CMSG_FIRSTHDR(&msg);
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen if (!CHECK_MSG(msg) || !CHECK_CMSG(cmsg))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *fd = -1;
6bc98d3898c475ba7615ba2b016e5142c8b2c09fTimo Sirainen else
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen *fd = *((int *) CMSG_DATA(cmsg));
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen return ret;
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen}
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen#else
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen# ifdef __GNUC__
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen# warning SCM_RIGHTS not supported, privilege separation not possible
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainen# endif
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainenssize_t fd_send(int handle __attr_unused__, int send_fd __attr_unused__,
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen const void *data __attr_unused__, size_t size __attr_unused__)
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen{
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen errno = ENOSYS;
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen return -1;
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen}
7761758f43d6150be4b07f4c54457ce662f78c4cTimo Sirainen
6bc98d3898c475ba7615ba2b016e5142c8b2c09fTimo Sirainenssize_t fd_read(int handle __attr_unused__, void *data __attr_unused__,
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen size_t size __attr_unused__, int *fd __attr_unused__)
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen{
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen errno = ENOSYS;
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen return -1;
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen}
6bc98d3898c475ba7615ba2b016e5142c8b2c09fTimo Sirainen#endif
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen