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