/src/dovecot/src/lib/fdpass.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level 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 |
43 | | struct 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 | | |
104 | | ssize_t fd_send(int handle, int send_fd, const void *data, size_t size) |
105 | 0 | { |
106 | 0 | struct msghdr msg; |
107 | 0 | struct const_iovec iov; |
108 | 0 | struct cmsghdr *cmsg; |
109 | 0 | char buf[CMSG_SPACE(sizeof(int))]; |
110 | | |
111 | | /* at least one byte is required to be sent with fd passing */ |
112 | 0 | i_assert(size > 0 && size < INT_MAX); |
113 | | |
114 | 0 | memset(&msg, 0, sizeof(struct msghdr)); |
115 | |
|
116 | 0 | iov.iov_base = data; |
117 | 0 | iov.iov_len = size; |
118 | |
|
119 | 0 | msg.msg_iov = (void *)&iov; |
120 | 0 | msg.msg_iovlen = 1; |
121 | |
|
122 | 0 | if (send_fd != -1) { |
123 | | /* set the control and controllen before CMSG_FIRSTHDR(). */ |
124 | 0 | memset(buf, 0, sizeof(buf)); |
125 | 0 | msg.msg_control = buf; |
126 | 0 | msg.msg_controllen = sizeof(buf); |
127 | |
|
128 | 0 | cmsg = CMSG_FIRSTHDR(&msg); |
129 | 0 | cmsg->cmsg_level = SOL_SOCKET; |
130 | 0 | cmsg->cmsg_type = SCM_RIGHTS; |
131 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
132 | 0 | 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 | 0 | msg.msg_controllen = cmsg->cmsg_len; |
138 | 0 | } |
139 | |
|
140 | 0 | return sendmsg(handle, &msg, 0); |
141 | 0 | } |
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 | 0 | ((cmsg) != NULL && \ |
150 | 0 | (size_t)(cmsg)->cmsg_len >= (size_t)CMSG_LEN(sizeof(int)) && \ |
151 | 0 | (cmsg)->cmsg_level == SOL_SOCKET && (cmsg)->cmsg_type == SCM_RIGHTS) |
152 | | #endif |
153 | | |
154 | | ssize_t fd_read(int handle, void *data, size_t size, int *fd) |
155 | 0 | { |
156 | 0 | struct msghdr msg; |
157 | 0 | struct iovec iov; |
158 | 0 | struct cmsghdr *cmsg; |
159 | 0 | ssize_t ret; |
160 | 0 | char buf[CMSG_SPACE(sizeof(int))]; |
161 | |
|
162 | 0 | i_assert(size > 0 && size < INT_MAX); |
163 | | |
164 | 0 | memset(&msg, 0, sizeof (struct msghdr)); |
165 | |
|
166 | 0 | iov.iov_base = data; |
167 | 0 | iov.iov_len = size; |
168 | |
|
169 | 0 | msg.msg_iov = &iov; |
170 | 0 | msg.msg_iovlen = 1; |
171 | |
|
172 | 0 | memset(buf, 0, sizeof(buf)); |
173 | 0 | msg.msg_control = buf; |
174 | 0 | msg.msg_controllen = sizeof(buf); |
175 | |
|
176 | 0 | ret = recvmsg(handle, &msg, 0); |
177 | 0 | if (ret <= 0) { |
178 | 0 | *fd = -1; |
179 | 0 | return ret; |
180 | 0 | } |
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 | 0 | cmsg = CMSG_FIRSTHDR(&msg); |
188 | 0 | if (!CHECK_CMSG(cmsg)) |
189 | 0 | *fd = -1; |
190 | 0 | else |
191 | 0 | memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); |
192 | 0 | return ret; |
193 | 0 | } |
194 | | |
195 | | #else |
196 | | # ifdef __GNUC__ |
197 | | # warning SCM_RIGHTS not supported, privilege separation not possible |
198 | | # endif |
199 | | ssize_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 | | |
206 | | ssize_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 |