Ticket #12737: fdpass.c

File fdpass.c, 5.8 KB (added by korli, 8 years ago)
Line 
1/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
2
3/*
4 fdpass.c - File descriptor passing between processes via UNIX sockets
5
6 This isn't fully portable, but pretty much all UNIXes nowadays should
7 support this. If you're having runtime problems with fd_read(), check the
8 end of fd_read() and play with the if condition. If you're having problems
9 with fd_send(), try defining BUGGY_CMSG_MACROS.
10
11 If this file doesn't compile at all, you should check if this is supported
12 in your system at all. It may require some extra #define to enable it.
13 If not, you're pretty much out of luck. Cygwin didn't last I checked.
14*/
15
16#define _XPG4_2
17
18#if defined(irix) || defined (__irix__) || defined(sgi) || defined (__sgi__)
19# define _XOPEN_SOURCE 4 /* for IRIX */
20#endif
21
22#if !defined(_AIX) && !defined(_XOPEN_SOURCE_EXTENDED)
23# define _XOPEN_SOURCE_EXTENDED /* for Tru64, breaks AIX */
24#endif
25
26#ifdef HAVE_CONFIG_H
27# include "lib.h"
28#else
29# define i_assert(x)
30#endif
31
32#include <string.h>
33#include <limits.h>
34#include <sys/types.h>
35
36#include <sys/socket.h>
37#include <sys/un.h>
38#include <sys/uio.h>
39
40#include "fdpass.h"
41
42#ifndef HAVE_CONFIG_H
43struct const_iovec {
44 const void *iov_base;
45 size_t iov_len;
46};
47#endif
48
49/* RFC 2292 defines CMSG_*() macros, but some operating systems don't have them
50 so we'll define our own if they don't exist.
51
52 CMSG_LEN(data) is used to calculate size of sizeof(struct cmsghdr) +
53 sizeof(data) and padding between them.
54
55 CMSG_SPACE(data) also calculates the padding needed after the data, in case
56 multiple objects are sent.
57
58 cmsghdr contains cmsg_len field and two integers. cmsg_len is sometimes
59 defined as sockaddr_t and sometimes size_t, so it can be either 32bit or
60 64bit. This padding is added by compiler in sizeof(struct cmsghdr).
61
62 Padding required by CMSG_DATA() can vary. Usually it wants size_t or 32bit.
63 With Solaris it's in _CMSG_DATA_ALIGNMENT (32bit), we assume others want
64 size_t.
65
66 We don't really need CMSG_SPACE() to be exactly correct, because currently
67 we send only one object at a time. But anyway I'm trying to keep that
68 correct in case it's sometimes needed..
69*/
70
71#ifdef BUGGY_CMSG_MACROS
72/* Some OSes have broken CMSG macros in 64bit systems. The macros use 64bit
73 alignment while kernel uses 32bit alignment. */
74# undef CMSG_SPACE
75# undef CMSG_LEN
76# undef CMSG_DATA
77# define CMSG_DATA(cmsg) ((char *)((cmsg) + 1))
78# define _CMSG_DATA_ALIGNMENT 4
79# define _CMSG_HDR_ALIGNMENT 4
80#endif
81
82#ifndef CMSG_SPACE
83# define MY_ALIGN(len, align) \
84 (((len) + align - 1) & ~(align - 1))
85
86/* Alignment between cmsghdr and data */
87# ifndef _CMSG_DATA_ALIGNMENT
88# define _CMSG_DATA_ALIGNMENT sizeof(size_t)
89# endif
90/* Alignment between data and next cmsghdr */
91# ifndef _CMSG_HDR_ALIGNMENT
92# define _CMSG_HDR_ALIGNMENT sizeof(size_t)
93# endif
94
95# define CMSG_SPACE(len) \
96 (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + \
97 MY_ALIGN(len, _CMSG_HDR_ALIGNMENT))
98# define CMSG_LEN(len) \
99 (MY_ALIGN(sizeof(struct cmsghdr), _CMSG_DATA_ALIGNMENT) + (len))
100#endif
101
102#ifdef SCM_RIGHTS
103
104ssize_t fd_send(int handle, int send_fd, const void *data, size_t size)
105{
106 struct msghdr msg;
107 struct const_iovec iov;
108 struct cmsghdr *cmsg;
109 char buf[CMSG_SPACE(sizeof(int))];
110
111 /* at least one byte is required to be sent with fd passing */
112 i_assert(size > 0 && size < INT_MAX);
113
114 memset(&msg, 0, sizeof(struct msghdr));
115
116 iov.iov_base = data;
117 iov.iov_len = size;
118
119 msg.msg_iov = (void *)&iov;
120 msg.msg_iovlen = 1;
121
122 if (send_fd != -1) {
123 /* set the control and controllen before CMSG_FIRSTHDR(). */
124 memset(buf, 0, sizeof(buf));
125 msg.msg_control = buf;
126 msg.msg_controllen = sizeof(buf);
127
128 cmsg = CMSG_FIRSTHDR(&msg);
129 cmsg->cmsg_level = SOL_SOCKET;
130 cmsg->cmsg_type = SCM_RIGHTS;
131 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
132 memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd));
133
134 /* set the real length we want to use. Do it after all is
135 set just in case CMSG macros required the extra padding
136 in the end. */
137 msg.msg_controllen = cmsg->cmsg_len;
138 }
139
140 return sendmsg(handle, &msg, 0);
141}
142
143#ifdef LINUX20
144/* Linux 2.0.x doesn't set any cmsg fields. Note that this might make some
145 attacks possible so don't do it unless you really have to. */
146# define CHECK_CMSG(cmsg) ((cmsg) != NULL)
147#else
148# define CHECK_CMSG(cmsg) \
149 ((cmsg) != NULL && \
150 (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \
151 (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS)
152#endif
153
154ssize_t fd_read(int handle, void *data, size_t size, int *fd)
155{
156 struct msghdr msg;
157 struct iovec iov;
158 struct cmsghdr *cmsg;
159 ssize_t ret;
160 char buf[CMSG_SPACE(sizeof(int))];
161
162 i_assert(size > 0 && size < INT_MAX);
163
164 memset(&msg, 0, sizeof (struct msghdr));
165
166 iov.iov_base = data;
167 iov.iov_len = size;
168
169 msg.msg_iov = &iov;
170 msg.msg_iovlen = 1;
171
172 memset(buf, 0, sizeof(buf));
173 msg.msg_control = buf;
174 msg.msg_controllen = sizeof(buf);
175
176 ret = recvmsg(handle, &msg, 0);
177 if (ret <= 0) {
178 *fd = -1;
179 return ret;
180 }
181
182 /* at least one byte transferred - we should have the fd now.
183 do extra checks to make sure it really is an fd that is being
184 transferred to avoid potential DoS conditions. some systems don't
185 set all these values correctly however so CHECK_CMSG() is somewhat
186 system dependent */
187 cmsg = CMSG_FIRSTHDR(&msg);
188 if (!CHECK_CMSG(cmsg))
189 *fd = -1;
190 else
191 memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd));
192 return ret;
193}
194
195#else
196# ifdef __GNUC__
197# warning SCM_RIGHTS not supported, privilege separation not possible
198# endif
199ssize_t fd_send(int handle ATTR_UNUSED, int send_fd ATTR_UNUSED,
200 const void *data ATTR_UNUSED, size_t size ATTR_UNUSED)
201{
202 errno = ENOSYS;
203 return -1;
204}
205
206ssize_t fd_read(int handle ATTR_UNUSED, void *data ATTR_UNUSED,
207 size_t size ATTR_UNUSED, int *fd ATTR_UNUSED)
208{
209 errno = ENOSYS;
210 return -1;
211}
212#endif