]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/af_unix.c
tree-wide: s/__do_close_prot_errno/__do_close/g
[mirror_lxc.git] / src / lxc / af_unix.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/socket.h>
14 #include <sys/syscall.h>
15 #include <sys/un.h>
16
17 #include "config.h"
18 #include "log.h"
19 #include "macro.h"
20 #include "memory_utils.h"
21 #include "raw_syscalls.h"
22 #include "utils.h"
23
24 #ifndef HAVE_STRLCPY
25 #include "include/strlcpy.h"
26 #endif
27
28 lxc_log_define(af_unix, lxc);
29
30 static ssize_t lxc_abstract_unix_set_sockaddr(struct sockaddr_un *addr,
31 const char *path)
32 {
33 size_t len;
34
35 if (!addr || !path)
36 return ret_errno(EINVAL);
37
38 /* Clear address structure */
39 memset(addr, 0, sizeof(*addr));
40
41 addr->sun_family = AF_UNIX;
42
43 len = strlen(&path[1]);
44
45 /* do not enforce \0-termination */
46 if (len >= INT_MAX || len >= sizeof(addr->sun_path))
47 return ret_errno(ENAMETOOLONG);
48
49 /* do not enforce \0-termination */
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 {
56 __do_close int fd = -EBADF;
57 int ret;
58 ssize_t len;
59 struct sockaddr_un addr;
60
61 fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0);
62 if (fd < 0)
63 return -1;
64
65 if (!path)
66 return move_fd(fd);
67
68 len = lxc_abstract_unix_set_sockaddr(&addr, path);
69 if (len < 0)
70 return -1;
71
72 ret = bind(fd, (struct sockaddr *)&addr,
73 offsetof(struct sockaddr_un, sun_path) + len + 1);
74 if (ret < 0)
75 return -1;
76
77 if (type == SOCK_STREAM) {
78 ret = listen(fd, 100);
79 if (ret < 0)
80 return -1;
81 }
82
83 return move_fd(fd);
84 }
85
86 void lxc_abstract_unix_close(int fd)
87 {
88 close(fd);
89 }
90
91 int lxc_abstract_unix_connect(const char *path)
92 {
93 __do_close int fd = -EBADF;
94 int ret;
95 ssize_t len;
96 struct sockaddr_un addr;
97
98 fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
99 if (fd < 0)
100 return -1;
101
102 len = lxc_abstract_unix_set_sockaddr(&addr, path);
103 if (len < 0)
104 return -1;
105
106 ret = connect(fd, (struct sockaddr *)&addr,
107 offsetof(struct sockaddr_un, sun_path) + len + 1);
108 if (ret < 0)
109 return -1;
110
111 return move_fd(fd);
112 }
113
114 int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds, int num_sendfds,
115 struct iovec *iov, size_t iovlen)
116 {
117 __do_free char *cmsgbuf = NULL;
118 int ret;
119 struct msghdr msg;
120 struct cmsghdr *cmsg = NULL;
121 size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int));
122
123 memset(&msg, 0, sizeof(msg));
124
125 cmsgbuf = malloc(cmsgbufsize);
126 if (!cmsgbuf) {
127 errno = ENOMEM;
128 return -1;
129 }
130
131 msg.msg_control = cmsgbuf;
132 msg.msg_controllen = cmsgbufsize;
133
134 cmsg = CMSG_FIRSTHDR(&msg);
135 cmsg->cmsg_level = SOL_SOCKET;
136 cmsg->cmsg_type = SCM_RIGHTS;
137 cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int));
138
139 msg.msg_controllen = cmsg->cmsg_len;
140
141 memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int));
142
143 msg.msg_iov = iov;
144 msg.msg_iovlen = iovlen;
145
146 do {
147 ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
148 } while (ret < 0 && errno == EINTR);
149
150 return ret;
151 }
152
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 };
161 return lxc_abstract_unix_send_fds_iov(fd, sendfds, num_sendfds, &iov, 1);
162 }
163
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
170 static int lxc_abstract_unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds,
171 struct iovec *iov, size_t iovlen)
172 {
173 __do_free char *cmsgbuf = NULL;
174 int ret;
175 struct msghdr msg;
176 size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) +
177 CMSG_SPACE(num_recvfds * sizeof(int));
178
179 memset(&msg, 0, sizeof(msg));
180
181 cmsgbuf = malloc(cmsgbufsize);
182 if (!cmsgbuf)
183 return ret_errno(ENOMEM);
184
185 msg.msg_control = cmsgbuf;
186 msg.msg_controllen = cmsgbufsize;
187
188 msg.msg_iov = iov;
189 msg.msg_iovlen = iovlen;
190
191 do {
192 ret = recvmsg(fd, &msg, 0);
193 } while (ret < 0 && errno == EINTR);
194 if (ret < 0 || ret == 0)
195 return ret;
196
197 /*
198 * If SO_PASSCRED is set we will always get a ucred message.
199 */
200 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
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 }
211
212 return ret;
213 }
214
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
226 int lxc_abstract_unix_send_credential(int fd, void *data, size_t size)
227 {
228 struct msghdr msg = {0};
229 struct iovec iov;
230 struct cmsghdr *cmsg;
231 struct ucred cred = {
232 .pid = lxc_raw_getpid(),
233 .uid = getuid(),
234 .gid = getgid(),
235 };
236 char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0};
237 char buf[1] = {0};
238
239 msg.msg_control = cmsgbuf;
240 msg.msg_controllen = sizeof(cmsgbuf);
241
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;
246 memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
247
248 msg.msg_name = NULL;
249 msg.msg_namelen = 0;
250
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;
255
256 return sendmsg(fd, &msg, MSG_NOSIGNAL);
257 }
258
259 int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size)
260 {
261 struct msghdr msg = {0};
262 struct iovec iov;
263 struct cmsghdr *cmsg;
264 struct ucred cred;
265 int ret;
266 char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0};
267 char buf[1] = {0};
268
269 msg.msg_name = NULL;
270 msg.msg_namelen = 0;
271 msg.msg_control = cmsgbuf;
272 msg.msg_controllen = sizeof(cmsgbuf);
273
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;
278
279 ret = recvmsg(fd, &msg, 0);
280 if (ret <= 0)
281 return ret;
282
283 cmsg = CMSG_FIRSTHDR(&msg);
284
285 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
286 cmsg->cmsg_level == SOL_SOCKET &&
287 cmsg->cmsg_type == SCM_CREDENTIALS) {
288 memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred));
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);
294 }
295
296 return ret;
297 }
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)
305 return ret_set_errno(-1, EINVAL);
306 if (path[0] != '/' && path[0] != '@')
307 return ret_set_errno(-1, EINVAL);
308 if (path[1] == '\0')
309 return ret_set_errno(-1, EINVAL);
310
311 if (len + 1 > sizeof(ret->sun_path))
312 return ret_set_errno(-1, EINVAL);
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
327 int lxc_unix_connect_type(struct sockaddr_un *addr, int type)
328 {
329 __do_close int fd = -EBADF;
330 int ret;
331 ssize_t len;
332
333 fd = socket(AF_UNIX, type | SOCK_CLOEXEC, 0);
334 if (fd < 0)
335 return log_error_errno(-1, errno,
336 "Failed to open new AF_UNIX socket");
337
338 if (addr->sun_path[0] == '\0')
339 len = strlen(&addr->sun_path[1]);
340 else
341 len = strlen(&addr->sun_path[0]);
342
343 ret = connect(fd, (struct sockaddr *)addr,
344 offsetof(struct sockaddr_un, sun_path) + len);
345 if (ret < 0)
346 return log_error_errno(-1, errno,
347 "Failed to bind new AF_UNIX socket");
348
349 return move_fd(fd);
350 }
351
352 int lxc_unix_connect(struct sockaddr_un *addr, int type)
353 {
354 return lxc_unix_connect_type(addr, SOCK_STREAM);
355 }
356
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 }