]> git.proxmox.com Git - mirror_lxcfs.git/blame - src/utils.c
lxcfs: don't cause a uaf
[mirror_lxcfs.git] / src / utils.c
CommitLineData
db0463bf 1/* SPDX-License-Identifier: LGPL-2.1+ */
1f5596dd
CB
2
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE
5#endif
6
7#ifndef FUSE_USE_VERSION
8#define FUSE_USE_VERSION 26
9#endif
10
11#define _FILE_OFFSET_BITS 64
12
b0f33646 13#include <ctype.h>
1d81c6a6
CB
14#include <errno.h>
15#include <fcntl.h>
1f5596dd 16#include <fuse.h>
1d81c6a6
CB
17#include <inttypes.h>
18#include <sched.h>
19#include <stdarg.h>
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
1f5596dd 25#include <sys/epoll.h>
1d81c6a6
CB
26#include <sys/stat.h>
27#include <sys/types.h>
4ec5c9da 28#include <sys/wait.h>
1d81c6a6
CB
29#include <unistd.h>
30
1f5596dd 31#include "bindings.h"
1d81c6a6
CB
32#include "config.h"
33#include "macro.h"
34#include "memory_utils.h"
35#include "utils.h"
36
37/*
38 * append the given formatted string to *src.
39 * src: a pointer to a char* in which to append the formatted string.
40 * sz: the number of characters printed so far, minus trailing \0.
41 * asz: the allocated size so far
42 * format: string format. See printf for details.
43 * ...: varargs. See printf for details.
44 */
87f7558b
CB
45/*
46 * append the given formatted string to *src.
47 * src: a pointer to a char* in which to append the formatted string.
48 * sz: the number of characters printed so far, minus trailing \0.
49 * asz: the allocated size so far
50 * format: string format. See printf for details.
51 * ...: varargs. See printf for details.
52 */
53char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
1d81c6a6
CB
54{
55 char tmp[BUF_RESERVE_SIZE];
87f7558b
CB
56 va_list args;
57 int tmplen;
1d81c6a6
CB
58
59 va_start (args, format);
87f7558b 60 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
1d81c6a6
CB
61 va_end(args);
62
63 if (!*src || tmplen + *sz + 1 >= *asz) {
87f7558b 64 char *str;
1d81c6a6 65 do {
87f7558b
CB
66 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
67 } while (!str);
68 *src = str;
1d81c6a6
CB
69 *asz += BUF_RESERVE_SIZE;
70 }
71 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
72 *sz += tmplen;
87f7558b
CB
73
74 return *src;
1d81c6a6
CB
75}
76
77/**
78 * in_same_namespace - Check whether two processes are in the same namespace.
79 * @pid1 - PID of the first process.
80 * @pid2 - PID of the second process.
81 * @ns - Name of the namespace to check. Must correspond to one of the names
82 * for the namespaces as shown in /proc/<pid/ns/
83 *
84 * If the two processes are not in the same namespace returns an fd to the
85 * namespace of the second process identified by @pid2. If the two processes are
86 * in the same namespace returns -EINVAL, -1 if an error occurred.
87 */
88static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
89{
05b7a16d 90 __do_close int ns_fd1 = -1, ns_fd2 = -1;
1d81c6a6
CB
91 int ret = -1;
92 struct stat ns_st1, ns_st2;
93
94 ns_fd1 = preserve_ns(pid1, ns);
95 if (ns_fd1 < 0) {
96 /* The kernel does not support this namespace. This is not an
97 * error.
98 */
99 if (errno == ENOENT)
100 return -EINVAL;
101
102 return -1;
103 }
104
105 ns_fd2 = preserve_ns(pid2, ns);
106 if (ns_fd2 < 0)
107 return -1;
108
109 ret = fstat(ns_fd1, &ns_st1);
110 if (ret < 0)
111 return -1;
112
113 ret = fstat(ns_fd2, &ns_st2);
114 if (ret < 0)
115 return -1;
116
117 /* processes are in the same namespace */
118 if ((ns_st1.st_dev == ns_st2.st_dev) && (ns_st1.st_ino == ns_st2.st_ino))
119 return -EINVAL;
120
121 /* processes are in different namespaces */
122 return move_fd(ns_fd2);
123}
124
125bool is_shared_pidns(pid_t pid)
126{
127 if (pid != 1)
128 return false;
129
130 if (in_same_namespace(pid, getpid(), "pid") == -EINVAL)
131 return true;
132
133 return false;
134}
135
136int preserve_ns(const int pid, const char *ns)
137{
138 int ret;
139/* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
140#define __NS_PATH_LEN 50
141 char path[__NS_PATH_LEN];
142
143 /* This way we can use this function to also check whether namespaces
144 * are supported by the kernel by passing in the NULL or the empty
145 * string.
146 */
147 ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
148 !ns || strcmp(ns, "") == 0 ? "" : "/",
149 !ns || strcmp(ns, "") == 0 ? "" : ns);
150 if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
151 errno = EFBIG;
152 return -1;
153 }
154
155 return open(path, O_RDONLY | O_CLOEXEC);
156}
580fe4df
CB
157
158void do_release_file_info(struct fuse_file_info *fi)
159{
99b183fb 160 struct file_info *f;
580fe4df 161
99b183fb 162 f = INTTYPE_TO_PTR(fi->fh);
580fe4df
CB
163 if (!f)
164 return;
165
166 fi->fh = 0;
167
168 free_disarm(f->controller);
169 free_disarm(f->cgroup);
170 free_disarm(f->file);
171 free_disarm(f->buf);
172 free_disarm(f);
173}
1f5596dd
CB
174
175#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
176
177bool wait_for_sock(int sock, int timeout)
178{
05b7a16d 179 __do_close int epfd = -EBADF;
1f5596dd 180 struct epoll_event ev;
915c14d0 181 int ret, now, starttime, deltatime;
1f5596dd
CB
182
183 if ((starttime = time(NULL)) < 0)
184 return false;
185
41eb015d
CB
186 epfd = epoll_create(1);
187 if (epfd < 0)
188 return log_error(false, "%s\n", "Failed to create epoll socket: %m");
1f5596dd
CB
189
190 ev.events = POLLIN_SET;
191 ev.data.fd = sock;
41eb015d
CB
192 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
193 return log_error(false, "Failed adding socket to epoll: %m");
1f5596dd
CB
194
195again:
41eb015d 196 if ((now = time(NULL)) < 0)
1f5596dd 197 return false;
1f5596dd
CB
198
199 deltatime = (starttime + timeout) - now;
41eb015d 200 if (deltatime < 0)
1f5596dd 201 return false;
41eb015d 202
1f5596dd
CB
203 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
204 if (ret < 0 && errno == EINTR)
205 goto again;
1f5596dd 206
41eb015d 207 if (ret <= 0)
1f5596dd 208 return false;
41eb015d 209
1f5596dd
CB
210 return true;
211}
212
213bool recv_creds(int sock, struct ucred *cred, char *v)
214{
215 struct msghdr msg = { 0 };
216 struct iovec iov;
217 struct cmsghdr *cmsg;
218 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
219 char buf[1];
220 int ret;
221 int optval = 1;
222
223 *v = '1';
224
225 cred->pid = -1;
226 cred->uid = -1;
227 cred->gid = -1;
228
41eb015d
CB
229 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
230 return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
231
1f5596dd 232 buf[0] = '1';
41eb015d
CB
233 if (write(sock, buf, 1) != 1)
234 return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
1f5596dd
CB
235
236 msg.msg_name = NULL;
237 msg.msg_namelen = 0;
238 msg.msg_control = cmsgbuf;
239 msg.msg_controllen = sizeof(cmsgbuf);
240
241 iov.iov_base = buf;
242 iov.iov_len = sizeof(buf);
243 msg.msg_iov = &iov;
244 msg.msg_iovlen = 1;
245
41eb015d
CB
246 if (!wait_for_sock(sock, 2))
247 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
248
1f5596dd 249 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
41eb015d
CB
250 if (ret < 0)
251 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno));
1f5596dd
CB
252
253 cmsg = CMSG_FIRSTHDR(&msg);
254
255 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
256 cmsg->cmsg_level == SOL_SOCKET &&
257 cmsg->cmsg_type == SCM_CREDENTIALS) {
258 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
259 }
260 *v = buf[0];
261
262 return true;
263}
264
265static int msgrecv(int sockfd, void *buf, size_t len)
266{
267 if (!wait_for_sock(sockfd, 2))
268 return -1;
41eb015d 269
1f5596dd
CB
270 return recv(sockfd, buf, len, MSG_DONTWAIT);
271}
272
273int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
274{
275 struct msghdr msg = { 0 };
276 struct iovec iov;
277 struct cmsghdr *cmsg;
278 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
279 char buf[1];
280 buf[0] = 'p';
281
41eb015d
CB
282 if (pingfirst && msgrecv(sock, buf, 1) != 1)
283 return log_error(SEND_CREDS_FAIL, "%s - Failed getting reply from server over socketpair: %d",
284 strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
285
286 msg.msg_control = cmsgbuf;
287 msg.msg_controllen = sizeof(cmsgbuf);
288
289 cmsg = CMSG_FIRSTHDR(&msg);
290 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
291 cmsg->cmsg_level = SOL_SOCKET;
292 cmsg->cmsg_type = SCM_CREDENTIALS;
293 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
294
295 msg.msg_name = NULL;
296 msg.msg_namelen = 0;
297
298 buf[0] = v;
299 iov.iov_base = buf;
300 iov.iov_len = sizeof(buf);
301 msg.msg_iov = &iov;
302 msg.msg_iovlen = 1;
303
304 if (sendmsg(sock, &msg, 0) < 0) {
1f5596dd 305 if (errno == 3)
41eb015d
CB
306 return log_error(SEND_CREDS_NOTSK, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_NOTSK);
307
308 return log_error(SEND_CREDS_FAIL, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
309 }
310
311 return SEND_CREDS_OK;
312}
313
314int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
315{
316 __do_free char *line = NULL;
317 __do_fclose FILE *f = NULL;
318 size_t linelen = 0, total_len = 0;
319 char *cache = d->buf;
320 size_t cache_size = d->buflen;
321
dbb1f822 322 f = fopen(path, "re");
1f5596dd
CB
323 if (!f)
324 return 0;
325
326 while (getline(&line, &linelen, f) != -1) {
327 ssize_t l = snprintf(cache, cache_size, "%s", line);
41eb015d
CB
328 if (l < 0)
329 return log_error(0, "Failed to write cache");
330 if (l >= cache_size)
331 return log_error(0, "Write to cache was truncated");
332
1f5596dd
CB
333 cache += l;
334 cache_size -= l;
335 total_len += l;
336 }
337
338 d->size = total_len;
339 if (total_len > size)
340 total_len = size;
341
342 /* read from off 0 */
343 memcpy(buf, d->buf, total_len);
344
345 if (d->size > total_len)
346 d->cached = d->size - total_len;
cbfc55fd 347
1f5596dd
CB
348 return total_len;
349}
4ec5c9da 350
cbfc55fd
CB
351int read_file_fuse_with_offset(const char *path, char *buf, size_t size,
352 off_t offset, struct file_info *d)
353{
354 if (offset) {
355 ssize_t total_len = 0;
356 char *cache = d->buf;
357 int left;
358
359 if (offset > d->size)
360 return -EINVAL;
361
362 if (!d->cached)
363 return 0;
364
365 left = d->size - offset;
366 total_len = left > size ? size : left;
367 memcpy(buf, cache + offset, total_len);
368
369 return total_len;
370 }
371
372 return read_file_fuse(path, buf, size, d);
373}
374
4ec5c9da
CB
375#define INITSCOPE "/init.scope"
376void prune_init_slice(char *cg)
377{
378 char *point;
379 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
380
381 if (cg_len < initscope_len)
382 return;
383
384 point = cg + cg_len - initscope_len;
385 if (strcmp(point, INITSCOPE) == 0) {
386 if (point == cg)
700dd417 387 *(point + 1) = '\0';
4ec5c9da
CB
388 else
389 *point = '\0';
390 }
391}
392
393int wait_for_pid(pid_t pid)
394{
395 int status, ret;
396
397 if (pid <= 0)
398 return -1;
399
400again:
401 ret = waitpid(pid, &status, 0);
402 if (ret == -1) {
403 if (errno == EINTR)
404 goto again;
405 return -1;
406 }
407 if (ret != pid)
408 goto again;
409 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
410 return -1;
411 return 0;
412}
757a63e7
CB
413
414static ssize_t read_nointr(int fd, void *buf, size_t count)
415{
416 ssize_t ret;
417again:
418 ret = read(fd, buf, count);
419 if (ret < 0 && errno == EINTR)
420 goto again;
421
422 return ret;
423}
424
425static void *must_realloc(void *orig, size_t sz)
426{
427 void *ret;
428
429 do {
430 ret = realloc(orig, sz);
431 } while (!ret);
432
433 return ret;
434}
435
28519477 436static char *fd_to_buf(int fd, size_t *length)
757a63e7 437{
757a63e7 438 __do_free char *copy = NULL;
757a63e7
CB
439
440 if (!length)
441 return NULL;
442
757a63e7
CB
443 *length = 0;
444 for (;;) {
28519477
CB
445 ssize_t bytes_read;
446 char buf[4096];
757a63e7
CB
447 char *old = copy;
448
28519477
CB
449 bytes_read = read_nointr(fd, buf, sizeof(buf));
450 if (bytes_read < 0)
757a63e7 451 return NULL;
28519477
CB
452
453 if (!bytes_read)
757a63e7
CB
454 break;
455
28519477
CB
456 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
457 memcpy(copy + *length, buf, bytes_read);
458 *length += bytes_read;
757a63e7
CB
459 }
460
461 return move_ptr(copy);
462}
463
28519477
CB
464static char *file_to_buf(const char *path, size_t *length)
465{
05b7a16d 466 __do_close int fd = -EBADF;
28519477
CB
467
468 if (!length)
469 return NULL;
470
471 fd = open(path, O_RDONLY | O_CLOEXEC);
472 if (fd < 0)
473 return NULL;
474
475 return fd_to_buf(fd, length);
476}
477
757a63e7
CB
478FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
479{
480 __do_free char *buf = NULL;
481 size_t len = 0;
482 FILE *f;
483
484 buf = file_to_buf(path, &len);
485 if (!buf)
486 return NULL;
487
488 f = fmemopen(buf, len, mode);
489 if (!f)
490 return NULL;
491 *caller_freed_buffer = move_ptr(buf);
492 return f;
493}
28519477 494
28519477
CB
495FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
496{
497 __do_free char *buf = NULL;
498 size_t len = 0;
499 FILE *f;
500
501 buf = fd_to_buf(fd, &len);
502 if (!buf)
503 return NULL;
504
505 f = fmemopen(buf, len, mode);
506 if (!f)
507 return NULL;
508
509 *caller_freed_buffer = move_ptr(buf);
510 return f;
511}
2f543378
CB
512
513ssize_t write_nointr(int fd, const void *buf, size_t count)
514{
515 ssize_t ret;
516
517 do {
518 ret = write(fd, buf, count);
519 } while (ret < 0 && errno == EINTR);
520
521 return ret;
522}
b0f33646
CB
523
524int safe_uint64(const char *numstr, uint64_t *converted, int base)
525{
526 char *err = NULL;
527 uint64_t u;
528
529 while (isspace(*numstr))
530 numstr++;
531
532 if (*numstr == '-')
533 return -EINVAL;
534
535 errno = 0;
536 u = strtoull(numstr, &err, base);
537 if (errno == ERANGE && u == UINT64_MAX)
538 return -ERANGE;
539
540 if (err == numstr || *err != '\0')
541 return -EINVAL;
542
543 *converted = u;
544 return 0;
545}