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