]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <daniel.lezcano at free.fr> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | */ | |
23 | ||
24 | #ifndef _GNU_SOURCE | |
25 | #define _GNU_SOURCE 1 | |
26 | #endif | |
27 | #include <errno.h> | |
28 | #include <fcntl.h> | |
29 | #include <stddef.h> | |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <string.h> | |
33 | #include <unistd.h> | |
34 | #include <sys/socket.h> | |
35 | #include <sys/syscall.h> | |
36 | #include <sys/un.h> | |
37 | ||
38 | #include "config.h" | |
39 | #include "log.h" | |
40 | #include "memory_utils.h" | |
41 | #include "raw_syscalls.h" | |
42 | #include "utils.h" | |
43 | ||
44 | #ifndef HAVE_STRLCPY | |
45 | #include "include/strlcpy.h" | |
46 | #endif | |
47 | ||
48 | lxc_log_define(af_unix, lxc); | |
49 | ||
50 | static ssize_t lxc_abstract_unix_set_sockaddr(struct sockaddr_un *addr, | |
51 | const char *path) | |
52 | { | |
53 | size_t len; | |
54 | ||
55 | if (!addr || !path) { | |
56 | errno = EINVAL; | |
57 | return -1; | |
58 | } | |
59 | ||
60 | /* Clear address structure */ | |
61 | memset(addr, 0, sizeof(*addr)); | |
62 | ||
63 | addr->sun_family = AF_UNIX; | |
64 | ||
65 | len = strlen(&path[1]); | |
66 | ||
67 | /* do not enforce \0-termination */ | |
68 | if (len >= INT_MAX || len >= sizeof(addr->sun_path)) { | |
69 | errno = ENAMETOOLONG; | |
70 | return -1; | |
71 | } | |
72 | ||
73 | /* do not enforce \0-termination */ | |
74 | memcpy(&addr->sun_path[1], &path[1], len); | |
75 | return len; | |
76 | } | |
77 | ||
78 | int lxc_abstract_unix_open(const char *path, int type, int flags) | |
79 | { | |
80 | int fd, ret; | |
81 | ssize_t len; | |
82 | struct sockaddr_un addr; | |
83 | ||
84 | fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0); | |
85 | if (fd < 0) | |
86 | return -1; | |
87 | ||
88 | if (!path) | |
89 | return fd; | |
90 | ||
91 | len = lxc_abstract_unix_set_sockaddr(&addr, path); | |
92 | if (len < 0) { | |
93 | int saved_errno = errno; | |
94 | close(fd); | |
95 | errno = saved_errno; | |
96 | return -1; | |
97 | } | |
98 | ||
99 | ret = bind(fd, (struct sockaddr *)&addr, | |
100 | offsetof(struct sockaddr_un, sun_path) + len + 1); | |
101 | if (ret < 0) { | |
102 | int saved_errno = errno; | |
103 | close(fd); | |
104 | errno = saved_errno; | |
105 | return -1; | |
106 | } | |
107 | ||
108 | if (type == SOCK_STREAM) { | |
109 | ret = listen(fd, 100); | |
110 | if (ret < 0) { | |
111 | int saved_errno = errno; | |
112 | close(fd); | |
113 | errno = saved_errno; | |
114 | return -1; | |
115 | } | |
116 | } | |
117 | ||
118 | return fd; | |
119 | } | |
120 | ||
121 | void lxc_abstract_unix_close(int fd) | |
122 | { | |
123 | close(fd); | |
124 | } | |
125 | ||
126 | int lxc_abstract_unix_connect(const char *path) | |
127 | { | |
128 | int fd, ret; | |
129 | ssize_t len; | |
130 | struct sockaddr_un addr; | |
131 | ||
132 | fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); | |
133 | if (fd < 0) | |
134 | return -1; | |
135 | ||
136 | len = lxc_abstract_unix_set_sockaddr(&addr, path); | |
137 | if (len < 0) { | |
138 | int saved_errno = errno; | |
139 | close(fd); | |
140 | errno = saved_errno; | |
141 | return -1; | |
142 | } | |
143 | ||
144 | ret = connect(fd, (struct sockaddr *)&addr, | |
145 | offsetof(struct sockaddr_un, sun_path) + len + 1); | |
146 | if (ret < 0) { | |
147 | int saved_errno = errno; | |
148 | close(fd); | |
149 | errno = saved_errno; | |
150 | return -1; | |
151 | } | |
152 | ||
153 | return fd; | |
154 | } | |
155 | ||
156 | int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds, int num_sendfds, | |
157 | struct iovec *iov, size_t iovlen) | |
158 | { | |
159 | __do_free char *cmsgbuf = NULL; | |
160 | int ret; | |
161 | struct msghdr msg; | |
162 | struct cmsghdr *cmsg = NULL; | |
163 | size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); | |
164 | ||
165 | memset(&msg, 0, sizeof(msg)); | |
166 | ||
167 | cmsgbuf = malloc(cmsgbufsize); | |
168 | if (!cmsgbuf) { | |
169 | errno = ENOMEM; | |
170 | return -1; | |
171 | } | |
172 | ||
173 | msg.msg_control = cmsgbuf; | |
174 | msg.msg_controllen = cmsgbufsize; | |
175 | ||
176 | cmsg = CMSG_FIRSTHDR(&msg); | |
177 | cmsg->cmsg_level = SOL_SOCKET; | |
178 | cmsg->cmsg_type = SCM_RIGHTS; | |
179 | cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); | |
180 | ||
181 | msg.msg_controllen = cmsg->cmsg_len; | |
182 | ||
183 | memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); | |
184 | ||
185 | msg.msg_iov = iov; | |
186 | msg.msg_iovlen = iovlen; | |
187 | ||
188 | again: | |
189 | ret = sendmsg(fd, &msg, MSG_NOSIGNAL); | |
190 | if (ret < 0) | |
191 | if (errno == EINTR) | |
192 | goto again; | |
193 | ||
194 | return ret; | |
195 | } | |
196 | ||
197 | int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, | |
198 | void *data, size_t size) | |
199 | { | |
200 | char buf[1] = {0}; | |
201 | struct iovec iov = { | |
202 | .iov_base = data ? data : buf, | |
203 | .iov_len = data ? size : sizeof(buf), | |
204 | }; | |
205 | return lxc_abstract_unix_send_fds_iov(fd, sendfds, num_sendfds, &iov, | |
206 | 1); | |
207 | } | |
208 | ||
209 | int lxc_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, | |
210 | size_t size) | |
211 | { | |
212 | return lxc_abstract_unix_send_fds(fd, sendfds, num_sendfds, data, size); | |
213 | } | |
214 | ||
215 | static int lxc_abstract_unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds, | |
216 | struct iovec *iov, size_t iovlen) | |
217 | { | |
218 | __do_free char *cmsgbuf = NULL; | |
219 | int ret; | |
220 | struct msghdr msg; | |
221 | struct cmsghdr *cmsg = NULL; | |
222 | char buf[1] = {0}; | |
223 | size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) + | |
224 | CMSG_SPACE(num_recvfds * sizeof(int)); | |
225 | ||
226 | memset(&msg, 0, sizeof(msg)); | |
227 | ||
228 | cmsgbuf = malloc(cmsgbufsize); | |
229 | if (!cmsgbuf) { | |
230 | errno = ENOMEM; | |
231 | return -1; | |
232 | } | |
233 | ||
234 | msg.msg_control = cmsgbuf; | |
235 | msg.msg_controllen = cmsgbufsize; | |
236 | ||
237 | msg.msg_iov = iov; | |
238 | msg.msg_iovlen = iovlen; | |
239 | ||
240 | again: | |
241 | ret = recvmsg(fd, &msg, 0); | |
242 | if (ret < 0) { | |
243 | if (errno == EINTR) | |
244 | goto again; | |
245 | ||
246 | goto out; | |
247 | } | |
248 | if (ret == 0) | |
249 | goto out; | |
250 | ||
251 | /* | |
252 | * If SO_PASSCRED is set we will always get a ucred message. | |
253 | */ | |
254 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | |
255 | if (cmsg->cmsg_type != SCM_RIGHTS) | |
256 | continue; | |
257 | ||
258 | memset(recvfds, -1, num_recvfds * sizeof(int)); | |
259 | if (cmsg && | |
260 | cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && | |
261 | cmsg->cmsg_level == SOL_SOCKET) | |
262 | memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); | |
263 | break; | |
264 | } | |
265 | ||
266 | out: | |
267 | return ret; | |
268 | } | |
269 | ||
270 | int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, | |
271 | void *data, size_t size) | |
272 | { | |
273 | char buf[1] = {0}; | |
274 | struct iovec iov = { | |
275 | .iov_base = data ? data : buf, | |
276 | .iov_len = data ? size : sizeof(buf), | |
277 | }; | |
278 | return lxc_abstract_unix_recv_fds_iov(fd, recvfds, num_recvfds, &iov, 1); | |
279 | } | |
280 | ||
281 | int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) | |
282 | { | |
283 | struct msghdr msg = {0}; | |
284 | struct iovec iov; | |
285 | struct cmsghdr *cmsg; | |
286 | struct ucred cred = { | |
287 | .pid = lxc_raw_getpid(), .uid = getuid(), .gid = getgid(), | |
288 | }; | |
289 | char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; | |
290 | char buf[1] = {0}; | |
291 | ||
292 | msg.msg_control = cmsgbuf; | |
293 | msg.msg_controllen = sizeof(cmsgbuf); | |
294 | ||
295 | cmsg = CMSG_FIRSTHDR(&msg); | |
296 | cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); | |
297 | cmsg->cmsg_level = SOL_SOCKET; | |
298 | cmsg->cmsg_type = SCM_CREDENTIALS; | |
299 | memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); | |
300 | ||
301 | msg.msg_name = NULL; | |
302 | msg.msg_namelen = 0; | |
303 | ||
304 | iov.iov_base = data ? data : buf; | |
305 | iov.iov_len = data ? size : sizeof(buf); | |
306 | msg.msg_iov = &iov; | |
307 | msg.msg_iovlen = 1; | |
308 | ||
309 | return sendmsg(fd, &msg, MSG_NOSIGNAL); | |
310 | } | |
311 | ||
312 | int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) | |
313 | { | |
314 | struct msghdr msg = {0}; | |
315 | struct iovec iov; | |
316 | struct cmsghdr *cmsg; | |
317 | struct ucred cred; | |
318 | int ret; | |
319 | char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; | |
320 | char buf[1] = {0}; | |
321 | ||
322 | msg.msg_name = NULL; | |
323 | msg.msg_namelen = 0; | |
324 | msg.msg_control = cmsgbuf; | |
325 | msg.msg_controllen = sizeof(cmsgbuf); | |
326 | ||
327 | iov.iov_base = data ? data : buf; | |
328 | iov.iov_len = data ? size : sizeof(buf); | |
329 | msg.msg_iov = &iov; | |
330 | msg.msg_iovlen = 1; | |
331 | ||
332 | ret = recvmsg(fd, &msg, 0); | |
333 | if (ret <= 0) | |
334 | goto out; | |
335 | ||
336 | cmsg = CMSG_FIRSTHDR(&msg); | |
337 | ||
338 | if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && | |
339 | cmsg->cmsg_level == SOL_SOCKET && | |
340 | cmsg->cmsg_type == SCM_CREDENTIALS) { | |
341 | memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); | |
342 | if (cred.uid && | |
343 | (cred.uid != getuid() || cred.gid != getgid())) { | |
344 | INFO("Message denied for '%d/%d'", cred.uid, cred.gid); | |
345 | errno = EACCES; | |
346 | return -1; | |
347 | } | |
348 | } | |
349 | ||
350 | out: | |
351 | return ret; | |
352 | } | |
353 | ||
354 | int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path) | |
355 | { | |
356 | size_t len; | |
357 | ||
358 | len = strlen(path); | |
359 | if (len == 0) | |
360 | return minus_one_set_errno(EINVAL); | |
361 | if (path[0] != '/' && path[0] != '@') | |
362 | return minus_one_set_errno(EINVAL); | |
363 | if (path[1] == '\0') | |
364 | return minus_one_set_errno(EINVAL); | |
365 | ||
366 | if (len + 1 > sizeof(ret->sun_path)) | |
367 | return minus_one_set_errno(EINVAL); | |
368 | ||
369 | *ret = (struct sockaddr_un){ | |
370 | .sun_family = AF_UNIX, | |
371 | }; | |
372 | ||
373 | if (path[0] == '@') { | |
374 | memcpy(ret->sun_path + 1, path + 1, len); | |
375 | return (int)(offsetof(struct sockaddr_un, sun_path) + len); | |
376 | } | |
377 | ||
378 | memcpy(ret->sun_path, path, len + 1); | |
379 | return (int)(offsetof(struct sockaddr_un, sun_path) + len + 1); | |
380 | } | |
381 | ||
382 | int lxc_unix_connect(struct sockaddr_un *addr) | |
383 | { | |
384 | __do_close_prot_errno int fd = -EBADF; | |
385 | int ret; | |
386 | ssize_t len; | |
387 | ||
388 | fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); | |
389 | if (fd < 0) { | |
390 | SYSERROR("Failed to open new AF_UNIX socket"); | |
391 | return -1; | |
392 | } | |
393 | ||
394 | if (addr->sun_path[0] == '\0') | |
395 | len = strlen(&addr->sun_path[1]); | |
396 | else | |
397 | len = strlen(&addr->sun_path[0]); | |
398 | ||
399 | ret = connect(fd, (struct sockaddr *)addr, | |
400 | offsetof(struct sockaddr_un, sun_path) + len); | |
401 | if (ret < 0) { | |
402 | SYSERROR("Failed to bind new AF_UNIX socket"); | |
403 | return -1; | |
404 | } | |
405 | ||
406 | return move_fd(fd); | |
407 | } | |
408 | ||
409 | int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout) | |
410 | { | |
411 | struct timeval out = {0}; | |
412 | int ret; | |
413 | ||
414 | out.tv_sec = snd_timeout; | |
415 | ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&out, | |
416 | sizeof(out)); | |
417 | if (ret < 0) | |
418 | return -1; | |
419 | ||
420 | out.tv_sec = rcv_timeout; | |
421 | ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, | |
422 | sizeof(out)); | |
423 | if (ret < 0) | |
424 | return -1; | |
425 | ||
426 | return 0; | |
427 | } |