blob: 2e5c232a006bd9883eb476636fcbbaeadb0a38d6 [file] [log] [blame]
Mike Frysinger50e31fa2018-01-19 18:59:49 -05001/* Copyright 2017 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -04004 */
5
6#include "system.h"
7
8#include <errno.h>
9#include <fcntl.h>
Luis Hector Chavez71323552017-09-05 09:17:22 -070010#include <grp.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040011#include <net/if.h>
Luis Hector Chavez71323552017-09-05 09:17:22 -070012#include <pwd.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040013#include <stdbool.h>
14#include <stdio.h>
15#include <string.h>
16#include <sys/ioctl.h>
Kevin Hamacher64cf3cb2019-06-05 17:28:46 +020017#include <sys/mount.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040018#include <sys/prctl.h>
19#include <sys/socket.h>
20#include <sys/stat.h>
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -070021#include <sys/statvfs.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040022#include <unistd.h>
23
Mattias Nisslere5200192018-10-18 12:29:40 +020024#include <linux/securebits.h>
25
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040026#include "util.h"
27
Kevin Hamacher64cf3cb2019-06-05 17:28:46 +020028/* Old libc versions might not define all constants that we need. */
29#ifndef ST_RDONLY
30#define ST_RDONLY 0x0001
31#endif
32#ifndef ST_NOSUID
33#define ST_NOSUID 0x0002
34#endif
35#ifndef ST_NODEV
36#define ST_NODEV 0x0004
37#endif
38#ifndef ST_NOEXEC
39#define ST_NOEXEC 0x0008
40#endif
41#ifndef ST_SYNCHRONOUS
42#define ST_SYNCHRONOUS 0x0010
43#endif
44#ifndef ST_MANDLOCK
45#define ST_MANDLOCK 0x0040
46#endif
47#ifndef ST_NOATIME
48#define ST_NOATIME 0x0400
49#endif
50#ifndef ST_NODIRATIME
51#define ST_NODIRATIME 0x0800
52#endif
53#ifndef ST_RELATIME
54#define ST_RELATIME 0x1000
55#endif
56
Mattias Nisslere5200192018-10-18 12:29:40 +020057/*
58 * SECBIT_NO_CAP_AMBIENT_RAISE was added in kernel 4.3, so fill in the
59 * definition if the securebits header doesn't provide it.
60 */
61#ifndef SECBIT_NO_CAP_AMBIENT_RAISE
62#define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(6))
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040063#endif
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040064
Mattias Nisslere5200192018-10-18 12:29:40 +020065#ifndef SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED
66#define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED (issecure_mask(7))
67#endif
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040068
69/*
70 * Assert the value of SECURE_ALL_BITS at compile-time.
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040071 * Android devices are currently compiled against 4.4 kernel headers. Kernel 4.3
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040072 * added a new securebit.
73 * When a new securebit is added, the new SECURE_ALL_BITS mask will return EPERM
74 * when used on older kernels. The compile-time assert will catch this situation
75 * at compile time.
76 */
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040077#if defined(__ANDROID__)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040078_Static_assert(SECURE_ALL_BITS == 0x55, "SECURE_ALL_BITS == 0x55.");
79#endif
80
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040081int secure_noroot_set_and_locked(uint64_t mask)
82{
83 return (mask & (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED)) ==
84 (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED);
85}
86
Mattias Nissler48b5ff12018-10-11 15:31:41 +020087int lock_securebits(uint64_t skip_mask, bool require_keep_caps)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040088{
Mattias Nissler48b5ff12018-10-11 15:31:41 +020089 /* The general idea is to set all bits, subject to exceptions below. */
90 unsigned long securebits = SECURE_ALL_BITS | SECURE_ALL_LOCKS;
91
92 /*
93 * SECBIT_KEEP_CAPS is special in that it is automatically cleared on
94 * execve(2). This implies that attempts to set SECBIT_KEEP_CAPS (as is
95 * the default) in processes that have it locked already (such as nested
96 * minijail usage) would fail. Thus, unless the caller requires it,
97 * allow it to remain off if it is already locked.
98 */
99 if (!require_keep_caps) {
100 int current_securebits = prctl(PR_GET_SECUREBITS);
101 if (current_securebits < 0) {
102 pwarn("prctl(PR_GET_SECUREBITS) failed");
103 return -1;
104 }
105
106 if ((current_securebits & SECBIT_KEEP_CAPS_LOCKED) != 0 &&
107 (current_securebits & SECBIT_KEEP_CAPS) == 0) {
108 securebits &= ~SECBIT_KEEP_CAPS;
109 }
110 }
111
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400112 /*
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400113 * Ambient capabilities can only be raised if they're already present
114 * in the permitted *and* inheritable set. Therefore, we don't really
115 * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already
116 * configuring the permitted and inheritable set.
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400117 */
Mattias Nissler48b5ff12018-10-11 15:31:41 +0200118 securebits &=
119 ~(SECBIT_NO_CAP_AMBIENT_RAISE | SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED);
120
121 /* Don't set any bits that the user requested not to be touched. */
122 securebits &= ~skip_mask;
123
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700124 if (!securebits) {
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -0400125 warn("not locking any securebits");
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700126 return 0;
127 }
128 int securebits_ret = prctl(PR_SET_SECUREBITS, securebits);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400129 if (securebits_ret < 0) {
130 pwarn("prctl(PR_SET_SECUREBITS) failed");
131 return -1;
132 }
133
134 return 0;
135}
136
137int write_proc_file(pid_t pid, const char *content, const char *basename)
138{
139 int fd, ret;
140 size_t sz, len;
141 ssize_t written;
142 char filename[32];
143
144 sz = sizeof(filename);
145 ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename);
146 if (ret < 0 || (size_t)ret >= sz) {
147 warn("failed to generate %s filename", basename);
148 return -1;
149 }
150
151 fd = open(filename, O_WRONLY | O_CLOEXEC);
152 if (fd < 0) {
153 pwarn("failed to open '%s'", filename);
154 return -errno;
155 }
156
157 len = strlen(content);
158 written = write(fd, content, len);
159 if (written < 0) {
160 pwarn("failed to write '%s'", filename);
Jorge Lucangeli Obes673c89d2018-10-04 16:08:10 -0400161 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400162 }
163
164 if ((size_t)written < len) {
165 warn("failed to write %zu bytes to '%s'", len, filename);
166 return -1;
167 }
168 close(fd);
169 return 0;
170}
171
172/*
173 * We specifically do not use cap_valid() as that only tells us the last
174 * valid cap we were *compiled* against (i.e. what the version of kernel
175 * headers says). If we run on a different kernel version, then it's not
176 * uncommon for that to be less (if an older kernel) or more (if a newer
177 * kernel).
178 * Normally, we suck up the answer via /proc. On Android, not all processes are
179 * guaranteed to be able to access '/proc/sys/kernel/cap_last_cap' so we
180 * programmatically find the value by calling prctl(PR_CAPBSET_READ).
181 */
182unsigned int get_last_valid_cap(void)
183{
184 unsigned int last_valid_cap = 0;
185 if (is_android()) {
186 for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0;
187 ++last_valid_cap)
188 ;
189
190 /* |last_valid_cap| will be the first failing value. */
191 if (last_valid_cap > 0) {
192 last_valid_cap--;
193 }
194 } else {
195 const char cap_file[] = "/proc/sys/kernel/cap_last_cap";
196 FILE *fp = fopen(cap_file, "re");
197 if (fscanf(fp, "%u", &last_valid_cap) != 1)
198 pdie("fscanf(%s)", cap_file);
199 fclose(fp);
200 }
201 return last_valid_cap;
202}
203
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400204int cap_ambient_supported(void)
205{
206 return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >=
207 0;
208}
209
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400210int config_net_loopback(void)
211{
212 const char ifname[] = "lo";
213 int sock;
214 struct ifreq ifr;
215
216 /* Make sure people don't try to add really long names. */
217 _Static_assert(sizeof(ifname) <= IFNAMSIZ, "interface name too long");
218
219 sock = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
220 if (sock < 0) {
221 pwarn("socket(AF_LOCAL) failed");
222 return -1;
223 }
224
225 /*
226 * Do the equiv of `ip link set up lo`. The kernel will assign
227 * IPv4 (127.0.0.1) & IPv6 (::1) addresses automatically!
228 */
229 strcpy(ifr.ifr_name, ifname);
230 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
231 pwarn("ioctl(SIOCGIFFLAGS) failed");
232 return -1;
233 }
234
235 /* The kernel preserves ifr.ifr_name for use. */
236 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
237 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
238 pwarn("ioctl(SIOCSIFFLAGS) failed");
239 return -1;
240 }
241
242 close(sock);
243 return 0;
244}
245
246int setup_pipe_end(int fds[2], size_t index)
247{
248 if (index > 1)
249 return -1;
250
251 close(fds[1 - index]);
252 return fds[index];
253}
254
255int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd)
256{
257 if (index > 1)
258 return -1;
259
260 close(fds[1 - index]);
261 /* dup2(2) the corresponding end of the pipe into |fd|. */
262 return dup2(fds[index], fd);
263}
264
265int write_pid_to_path(pid_t pid, const char *path)
266{
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400267 FILE *fp = fopen(path, "we");
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400268
269 if (!fp) {
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400270 pwarn("failed to open '%s'", path);
271 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400272 }
273 if (fprintf(fp, "%d\n", (int)pid) < 0) {
274 /* fprintf(3) does not set errno on failure. */
275 warn("fprintf(%s) failed", path);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400276 return -1;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400277 }
278 if (fclose(fp)) {
279 pwarn("fclose(%s) failed", path);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400280 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400281 }
282
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400283 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400284}
285
286/*
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500287 * Create the |path| directory and its parents (if need be) with |mode|.
288 * If not |isdir|, then |path| is actually a file, so the last component
289 * will not be created.
290 */
291int mkdir_p(const char *path, mode_t mode, bool isdir)
292{
yusukes059e0bd2018-03-05 10:22:16 -0800293 int rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500294 char *dir = strdup(path);
yusukes059e0bd2018-03-05 10:22:16 -0800295 if (!dir) {
296 rc = errno;
297 pwarn("strdup(%s) failed", path);
298 return -rc;
299 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500300
301 /* Starting from the root, work our way out to the end. */
302 char *p = strchr(dir + 1, '/');
303 while (p) {
304 *p = '\0';
305 if (mkdir(dir, mode) && errno != EEXIST) {
yusukes059e0bd2018-03-05 10:22:16 -0800306 rc = errno;
307 pwarn("mkdir(%s, 0%o) failed", dir, mode);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500308 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800309 return -rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500310 }
311 *p = '/';
312 p = strchr(p + 1, '/');
313 }
314
315 /*
316 * Create the last directory. We still check EEXIST here in case
317 * of trailing slashes.
318 */
319 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800320 if (isdir && mkdir(path, mode) && errno != EEXIST) {
321 rc = errno;
322 pwarn("mkdir(%s, 0%o) failed", path, mode);
323 return -rc;
324 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500325 return 0;
326}
327
328/*
Kevin Hamacher64cf3cb2019-06-05 17:28:46 +0200329 * get_mount_flags_for_directory: Returns the mount flags for the given
330 * directory.
331 */
332int get_mount_flags_for_directory(const char *path, unsigned long *mnt_flags)
333{
334 int rc;
335 struct statvfs stvfs_buf;
336
337 if (!mnt_flags)
338 return 0;
339
340 rc = statvfs(path, &stvfs_buf);
341 if (rc) {
342 rc = errno;
343 pwarn("failed to look up mount flags: source=%s", path);
344 return -rc;
345 }
346 *mnt_flags = vfs_flags_to_mount_flags(stvfs_buf.f_flag);
347 return 0;
348}
349
350/*
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400351 * setup_mount_destination: Ensures the mount target exists.
352 * Creates it if needed and possible.
353 */
354int setup_mount_destination(const char *source, const char *dest, uid_t uid,
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700355 uid_t gid, bool bind, unsigned long *mnt_flags)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400356{
357 int rc;
358 struct stat st_buf;
Mike Frysingereaab4202017-08-14 14:57:21 -0400359 bool domkdir;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400360
361 rc = stat(dest, &st_buf);
362 if (rc == 0) /* destination exists */
Kevin Hamacher64cf3cb2019-06-05 17:28:46 +0200363 return get_mount_flags_for_directory(source, mnt_flags);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400364
365 /*
366 * Try to create the destination.
367 * Either make a directory or touch a file depending on the source type.
Mike Frysingereaab4202017-08-14 14:57:21 -0400368 *
369 * If the source isn't an absolute path, assume it is a filesystem type
370 * such as "tmpfs" and create a directory to mount it on. The dest will
371 * be something like "none" or "proc" which we shouldn't be checking.
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400372 */
Mike Frysingereaab4202017-08-14 14:57:21 -0400373 if (source[0] == '/') {
374 /* The source is an absolute path -- it better exist! */
375 rc = stat(source, &st_buf);
yusukes059e0bd2018-03-05 10:22:16 -0800376 if (rc) {
377 rc = errno;
378 pwarn("stat(%s) failed", source);
379 return -rc;
380 }
Mike Frysingereaab4202017-08-14 14:57:21 -0400381
382 /*
383 * If bind mounting, we only create a directory if the source
384 * is a directory, else we always bind mount it as a file to
385 * support device nodes, sockets, etc...
386 *
387 * For all other mounts, we assume a block/char source is
388 * going to want a directory to mount to. If the source is
389 * something else (e.g. a fifo or socket), this probably will
390 * not do the right thing, but we'll fail later on when we try
391 * to mount(), so shouldn't be a big deal.
392 */
393 domkdir = S_ISDIR(st_buf.st_mode) ||
394 (!bind && (S_ISBLK(st_buf.st_mode) ||
395 S_ISCHR(st_buf.st_mode)));
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700396
Mike Frysingereaab4202017-08-14 14:57:21 -0400397 } else {
398 /* The source is a relative path -- assume it's a pseudo fs. */
399
400 /* Disallow relative bind mounts. */
yusukes059e0bd2018-03-05 10:22:16 -0800401 if (bind) {
402 warn("relative bind-mounts are not allowed: source=%s",
403 source);
Mike Frysingereaab4202017-08-14 14:57:21 -0400404 return -EINVAL;
yusukes059e0bd2018-03-05 10:22:16 -0800405 }
Mike Frysingereaab4202017-08-14 14:57:21 -0400406
407 domkdir = true;
408 }
409
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500410 /*
411 * Now that we know what we want to do, do it!
412 * We always create the intermediate dirs and the final path with 0755
413 * perms and root/root ownership. This shouldn't be a problem because
414 * the actual mount will set those perms/ownership on the mount point
415 * which is all people should need to access it.
416 */
yusukes059e0bd2018-03-05 10:22:16 -0800417 rc = mkdir_p(dest, 0755, domkdir);
418 if (rc)
419 return rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500420 if (!domkdir) {
Mike Frysingereaab4202017-08-14 14:57:21 -0400421 int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
yusukes059e0bd2018-03-05 10:22:16 -0800422 if (fd < 0) {
423 rc = errno;
424 pwarn("open(%s) failed", dest);
425 return -rc;
426 }
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400427 close(fd);
428 }
yusukes059e0bd2018-03-05 10:22:16 -0800429 if (chown(dest, uid, gid)) {
430 rc = errno;
431 pwarn("chown(%s, %u, %u) failed", dest, uid, gid);
432 return -rc;
433 }
Kevin Hamacher64cf3cb2019-06-05 17:28:46 +0200434 return get_mount_flags_for_directory(source, mnt_flags);
435}
436
437/*
438 * vfs_flags_to_mount_flags: Converts the given flags returned by statvfs to
439 * flags that can be used by mount().
440 */
441unsigned long vfs_flags_to_mount_flags(unsigned long vfs_flags)
442{
443 unsigned int i;
444 unsigned long mount_flags = 0;
445
446 static struct {
447 unsigned long mount_flag;
448 unsigned long vfs_flag;
449 } const flag_translation_table[] = {
450 {MS_NOSUID, ST_NOSUID}, {MS_NODEV, ST_NODEV},
451 {MS_NOEXEC, ST_NOEXEC}, {MS_SYNCHRONOUS, ST_SYNCHRONOUS},
452 {MS_MANDLOCK, ST_MANDLOCK}, {MS_NOATIME, ST_NOATIME},
453 {MS_NODIRATIME, ST_NODIRATIME}, {MS_RELATIME, ST_RELATIME},
454 };
455
456 for (i = 0; i < ARRAY_SIZE(flag_translation_table); i++) {
457 if (vfs_flags & flag_translation_table[i].vfs_flag) {
458 mount_flags |= flag_translation_table[i].mount_flag;
459 }
460 }
461
462 return mount_flags;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400463}
Luis Hector Chavez71323552017-09-05 09:17:22 -0700464
465/*
466 * lookup_user: Gets the uid/gid for the given username.
467 */
468int lookup_user(const char *user, uid_t *uid, gid_t *gid)
469{
470 char *buf = NULL;
471 struct passwd pw;
472 struct passwd *ppw = NULL;
473 ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
474 if (sz == -1)
475 sz = 65536; /* your guess is as good as mine... */
476
477 /*
478 * sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return
479 * the maximum needed size of the buffer, so we don't have to search.
480 */
481 buf = malloc(sz);
482 if (!buf)
483 return -ENOMEM;
484 getpwnam_r(user, &pw, buf, sz, &ppw);
485 /*
486 * We're safe to free the buffer here. The strings inside |pw| point
487 * inside |buf|, but we don't use any of them; this leaves the pointers
488 * dangling but it's safe. |ppw| points at |pw| if getpwnam_r(3)
489 * succeeded.
490 */
491 free(buf);
492 /* getpwnam_r(3) does *not* set errno when |ppw| is NULL. */
493 if (!ppw)
494 return -1;
495
496 *uid = ppw->pw_uid;
497 *gid = ppw->pw_gid;
498 return 0;
499}
500
501/*
502 * lookup_group: Gets the gid for the given group name.
503 */
504int lookup_group(const char *group, gid_t *gid)
505{
506 char *buf = NULL;
507 struct group gr;
508 struct group *pgr = NULL;
509 ssize_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
510 if (sz == -1)
511 sz = 65536; /* and mine is as good as yours, really */
512
513 /*
514 * sysconf(_SC_GETGR_R_SIZE_MAX), under glibc, is documented to return
515 * the maximum needed size of the buffer, so we don't have to search.
516 */
517 buf = malloc(sz);
518 if (!buf)
519 return -ENOMEM;
520 getgrnam_r(group, &gr, buf, sz, &pgr);
521 /*
522 * We're safe to free the buffer here. The strings inside gr point
523 * inside buf, but we don't use any of them; this leaves the pointers
524 * dangling but it's safe. pgr points at gr if getgrnam_r succeeded.
525 */
526 free(buf);
527 /* getgrnam_r(3) does *not* set errno when |pgr| is NULL. */
528 if (!pgr)
529 return -1;
530
531 *gid = pgr->gr_gid;
532 return 0;
533}
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400534
535static int seccomp_action_is_available(const char *wanted)
536{
537 if (is_android()) {
538 /*
539 * Accessing |actions_avail| is generating SELinux denials, so
540 * skip for now.
541 * TODO(crbug.com/978022, jorgelo): Remove once the denial is
542 * fixed.
543 */
544 return 0;
545 }
546 const char actions_avail_path[] =
547 "/proc/sys/kernel/seccomp/actions_avail";
548 FILE *f = fopen(actions_avail_path, "re");
549
550 if (!f) {
551 pwarn("fopen(%s) failed", actions_avail_path);
552 return 0;
553 }
554
555 char *actions_avail = NULL;
556 size_t buf_size = 0;
557 if (getline(&actions_avail, &buf_size, f) < 0) {
558 pwarn("getline() failed");
559 free(actions_avail);
560 return 0;
561 }
562
563 /*
564 * This is just substring search, which means that partial matches will
565 * match too (e.g. "action" would match "longaction"). There are no
566 * seccomp actions which include other actions though, so we're good for
567 * now. Eventually we might want to split the string by spaces.
568 */
569 return strstr(actions_avail, wanted) != NULL;
570}
571
572int seccomp_ret_log_available(void)
573{
574 static int ret_log_available = -1;
575
576 if (ret_log_available == -1)
577 ret_log_available = seccomp_action_is_available("log");
578
579 return ret_log_available;
580}
581
582int seccomp_ret_kill_process_available(void)
583{
584 static int ret_kill_process_available = -1;
585
586 if (ret_kill_process_available == -1)
587 ret_kill_process_available =
588 seccomp_action_is_available("kill_process");
589
590 return ret_kill_process_available;
591}