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