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