blob: 40f0883505528da43a224b16c4d36529d950547a [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>
17#include <sys/prctl.h>
18#include <sys/socket.h>
19#include <sys/stat.h>
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -070020#include <sys/statvfs.h>
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040021#include <unistd.h>
22
Mattias Nisslere5200192018-10-18 12:29:40 +020023#include <linux/securebits.h>
24
Luis Héctor Chávez01b628c2021-01-03 05:46:57 -080025#include "syscall_wrapper.h"
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040026#include "util.h"
27
Mattias Nisslere5200192018-10-18 12:29:40 +020028/*
29 * SECBIT_NO_CAP_AMBIENT_RAISE was added in kernel 4.3, so fill in the
30 * definition if the securebits header doesn't provide it.
31 */
32#ifndef SECBIT_NO_CAP_AMBIENT_RAISE
33#define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(6))
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040034#endif
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040035
Mattias Nisslere5200192018-10-18 12:29:40 +020036#ifndef SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED
37#define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED (issecure_mask(7))
38#endif
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040039
40/*
41 * Assert the value of SECURE_ALL_BITS at compile-time.
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040042 * Android devices are currently compiled against 4.4 kernel headers. Kernel 4.3
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040043 * added a new securebit.
44 * When a new securebit is added, the new SECURE_ALL_BITS mask will return EPERM
45 * when used on older kernels. The compile-time assert will catch this situation
46 * at compile time.
47 */
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040048#if defined(__ANDROID__)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040049_Static_assert(SECURE_ALL_BITS == 0x55, "SECURE_ALL_BITS == 0x55.");
50#endif
51
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +010052/* Used by lookup_(user|group) functions. */
53#define MAX_PWENT_SZ (1 << 20)
54#define MAX_GRENT_SZ (1 << 20)
55
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040056int secure_noroot_set_and_locked(uint64_t mask)
57{
58 return (mask & (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED)) ==
59 (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED);
60}
61
Mattias Nissler48b5ff12018-10-11 15:31:41 +020062int lock_securebits(uint64_t skip_mask, bool require_keep_caps)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040063{
Mattias Nissler48b5ff12018-10-11 15:31:41 +020064 /* The general idea is to set all bits, subject to exceptions below. */
65 unsigned long securebits = SECURE_ALL_BITS | SECURE_ALL_LOCKS;
66
67 /*
68 * SECBIT_KEEP_CAPS is special in that it is automatically cleared on
69 * execve(2). This implies that attempts to set SECBIT_KEEP_CAPS (as is
70 * the default) in processes that have it locked already (such as nested
71 * minijail usage) would fail. Thus, unless the caller requires it,
72 * allow it to remain off if it is already locked.
73 */
74 if (!require_keep_caps) {
75 int current_securebits = prctl(PR_GET_SECUREBITS);
76 if (current_securebits < 0) {
77 pwarn("prctl(PR_GET_SECUREBITS) failed");
78 return -1;
79 }
80
81 if ((current_securebits & SECBIT_KEEP_CAPS_LOCKED) != 0 &&
82 (current_securebits & SECBIT_KEEP_CAPS) == 0) {
83 securebits &= ~SECBIT_KEEP_CAPS;
84 }
85 }
86
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040087 /*
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040088 * Ambient capabilities can only be raised if they're already present
89 * in the permitted *and* inheritable set. Therefore, we don't really
90 * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already
91 * configuring the permitted and inheritable set.
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040092 */
Mattias Nissler48b5ff12018-10-11 15:31:41 +020093 securebits &=
94 ~(SECBIT_NO_CAP_AMBIENT_RAISE | SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED);
95
96 /* Don't set any bits that the user requested not to be touched. */
97 securebits &= ~skip_mask;
98
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -070099 if (!securebits) {
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -0400100 warn("not locking any securebits");
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700101 return 0;
102 }
103 int securebits_ret = prctl(PR_SET_SECUREBITS, securebits);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400104 if (securebits_ret < 0) {
105 pwarn("prctl(PR_SET_SECUREBITS) failed");
106 return -1;
107 }
108
109 return 0;
110}
111
112int write_proc_file(pid_t pid, const char *content, const char *basename)
113{
114 int fd, ret;
115 size_t sz, len;
116 ssize_t written;
117 char filename[32];
118
119 sz = sizeof(filename);
120 ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename);
121 if (ret < 0 || (size_t)ret >= sz) {
122 warn("failed to generate %s filename", basename);
123 return -1;
124 }
125
126 fd = open(filename, O_WRONLY | O_CLOEXEC);
127 if (fd < 0) {
128 pwarn("failed to open '%s'", filename);
129 return -errno;
130 }
131
132 len = strlen(content);
133 written = write(fd, content, len);
134 if (written < 0) {
135 pwarn("failed to write '%s'", filename);
Jorge Lucangeli Obes005c9032021-06-18 11:26:48 -0400136 close(fd);
Jorge Lucangeli Obes673c89d2018-10-04 16:08:10 -0400137 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400138 }
139
140 if ((size_t)written < len) {
141 warn("failed to write %zu bytes to '%s'", len, filename);
Jorge Lucangeli Obes005c9032021-06-18 11:26:48 -0400142 close(fd);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400143 return -1;
144 }
145 close(fd);
146 return 0;
147}
148
149/*
150 * We specifically do not use cap_valid() as that only tells us the last
151 * valid cap we were *compiled* against (i.e. what the version of kernel
152 * headers says). If we run on a different kernel version, then it's not
153 * uncommon for that to be less (if an older kernel) or more (if a newer
154 * kernel).
155 * Normally, we suck up the answer via /proc. On Android, not all processes are
156 * guaranteed to be able to access '/proc/sys/kernel/cap_last_cap' so we
157 * programmatically find the value by calling prctl(PR_CAPBSET_READ).
158 */
159unsigned int get_last_valid_cap(void)
160{
161 unsigned int last_valid_cap = 0;
162 if (is_android()) {
163 for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0;
164 ++last_valid_cap)
165 ;
166
167 /* |last_valid_cap| will be the first failing value. */
168 if (last_valid_cap > 0) {
169 last_valid_cap--;
170 }
171 } else {
172 const char cap_file[] = "/proc/sys/kernel/cap_last_cap";
173 FILE *fp = fopen(cap_file, "re");
174 if (fscanf(fp, "%u", &last_valid_cap) != 1)
175 pdie("fscanf(%s)", cap_file);
176 fclose(fp);
177 }
178 return last_valid_cap;
179}
180
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400181int cap_ambient_supported(void)
182{
183 return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >=
184 0;
185}
186
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400187int config_net_loopback(void)
188{
189 const char ifname[] = "lo";
190 int sock;
191 struct ifreq ifr;
192
193 /* Make sure people don't try to add really long names. */
194 _Static_assert(sizeof(ifname) <= IFNAMSIZ, "interface name too long");
195
196 sock = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
197 if (sock < 0) {
198 pwarn("socket(AF_LOCAL) failed");
199 return -1;
200 }
201
202 /*
203 * Do the equiv of `ip link set up lo`. The kernel will assign
204 * IPv4 (127.0.0.1) & IPv6 (::1) addresses automatically!
205 */
206 strcpy(ifr.ifr_name, ifname);
207 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
208 pwarn("ioctl(SIOCGIFFLAGS) failed");
Jorge Lucangeli Obes005c9032021-06-18 11:26:48 -0400209 close(sock);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400210 return -1;
211 }
212
213 /* The kernel preserves ifr.ifr_name for use. */
214 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
215 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
216 pwarn("ioctl(SIOCSIFFLAGS) failed");
Jorge Lucangeli Obes005c9032021-06-18 11:26:48 -0400217 close(sock);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400218 return -1;
219 }
220
221 close(sock);
222 return 0;
223}
224
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400225int write_pid_to_path(pid_t pid, const char *path)
226{
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400227 FILE *fp = fopen(path, "we");
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400228
229 if (!fp) {
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400230 pwarn("failed to open '%s'", path);
231 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400232 }
233 if (fprintf(fp, "%d\n", (int)pid) < 0) {
234 /* fprintf(3) does not set errno on failure. */
235 warn("fprintf(%s) failed", path);
Jorge Lucangeli Obes005c9032021-06-18 11:26:48 -0400236 fclose(fp);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400237 return -1;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400238 }
239 if (fclose(fp)) {
240 pwarn("fclose(%s) failed", path);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400241 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400242 }
243
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400244 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400245}
246
247/*
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500248 * Create the |path| directory and its parents (if need be) with |mode|.
249 * If not |isdir|, then |path| is actually a file, so the last component
250 * will not be created.
251 */
252int mkdir_p(const char *path, mode_t mode, bool isdir)
253{
yusukes059e0bd2018-03-05 10:22:16 -0800254 int rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500255 char *dir = strdup(path);
yusukes059e0bd2018-03-05 10:22:16 -0800256 if (!dir) {
257 rc = errno;
258 pwarn("strdup(%s) failed", path);
259 return -rc;
260 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500261
262 /* Starting from the root, work our way out to the end. */
263 char *p = strchr(dir + 1, '/');
264 while (p) {
265 *p = '\0';
266 if (mkdir(dir, mode) && errno != EEXIST) {
yusukes059e0bd2018-03-05 10:22:16 -0800267 rc = errno;
268 pwarn("mkdir(%s, 0%o) failed", dir, mode);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500269 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800270 return -rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500271 }
272 *p = '/';
273 p = strchr(p + 1, '/');
274 }
275
276 /*
277 * Create the last directory. We still check EEXIST here in case
278 * of trailing slashes.
279 */
280 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800281 if (isdir && mkdir(path, mode) && errno != EEXIST) {
282 rc = errno;
283 pwarn("mkdir(%s, 0%o) failed", path, mode);
284 return -rc;
285 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500286 return 0;
287}
288
289/*
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400290 * setup_mount_destination: Ensures the mount target exists.
291 * Creates it if needed and possible.
292 */
293int setup_mount_destination(const char *source, const char *dest, uid_t uid,
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700294 uid_t gid, bool bind, unsigned long *mnt_flags)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400295{
296 int rc;
297 struct stat st_buf;
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400298 bool domkdir;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400299
300 rc = stat(dest, &st_buf);
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400301 if (rc == 0) /* destination exists */
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400302 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400303
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400304 /*
305 * Try to create the destination.
306 * Either make a directory or touch a file depending on the source type.
307 *
308 * If the source isn't an absolute path, assume it is a filesystem type
309 * such as "tmpfs" and create a directory to mount it on. The dest will
310 * be something like "none" or "proc" which we shouldn't be checking.
311 */
312 if (source[0] == '/') {
313 /* The source is an absolute path -- it better exist! */
314 rc = stat(source, &st_buf);
315 if (rc) {
Jorge Lucangeli Obes9299cae2019-08-23 11:28:39 -0400316 rc = errno;
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400317 pwarn("stat(%s) failed", source);
Jorge Lucangeli Obes9299cae2019-08-23 11:28:39 -0400318 return -rc;
319 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400320
321 /*
322 * If bind mounting, we only create a directory if the source
323 * is a directory, else we always bind mount it as a file to
324 * support device nodes, sockets, etc...
325 *
326 * For all other mounts, we assume a block/char source is
327 * going to want a directory to mount to. If the source is
328 * something else (e.g. a fifo or socket), this probably will
329 * not do the right thing, but we'll fail later on when we try
330 * to mount(), so shouldn't be a big deal.
331 */
332 domkdir = S_ISDIR(st_buf.st_mode) ||
333 (!bind && (S_ISBLK(st_buf.st_mode) ||
334 S_ISCHR(st_buf.st_mode)));
335
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400336 /* If bind mounting, also grab the mount flags of the source. */
337 if (bind && mnt_flags) {
338 struct statvfs stvfs_buf;
339 rc = statvfs(source, &stvfs_buf);
340 if (rc) {
341 rc = errno;
342 pwarn(
343 "failed to look up mount flags: source=%s",
344 source);
345 return -rc;
346 }
347 *mnt_flags = stvfs_buf.f_flag;
348 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400349 } else {
350 /* The source is a relative path -- assume it's a pseudo fs. */
351
352 /* Disallow relative bind mounts. */
353 if (bind) {
354 warn("relative bind-mounts are not allowed: source=%s",
355 source);
356 return -EINVAL;
357 }
358
359 domkdir = true;
Mike Frysingereaab4202017-08-14 14:57:21 -0400360 }
361
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500362 /*
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400363 * Now that we know what we want to do, do it!
364 * We always create the intermediate dirs and the final path with 0755
365 * perms and root/root ownership. This shouldn't be a problem because
366 * the actual mount will set those perms/ownership on the mount point
367 * which is all people should need to access it.
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500368 */
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400369 rc = mkdir_p(dest, 0755, domkdir);
370 if (rc)
371 return rc;
372 if (!domkdir) {
373 int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
374 if (fd < 0) {
375 rc = errno;
376 pwarn("open(%s) failed", dest);
377 return -rc;
378 }
379 close(fd);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400380 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400381 if (chown(dest, uid, gid)) {
382 rc = errno;
383 pwarn("chown(%s, %u, %u) failed", dest, uid, gid);
384 return -rc;
385 }
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400386 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400387}
Luis Hector Chavez71323552017-09-05 09:17:22 -0700388
389/*
390 * lookup_user: Gets the uid/gid for the given username.
391 */
392int lookup_user(const char *user, uid_t *uid, gid_t *gid)
393{
394 char *buf = NULL;
395 struct passwd pw;
396 struct passwd *ppw = NULL;
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100397 /*
398 * sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return
399 * a suggested starting size for the buffer, so let's try getting this
400 * size first, and fallback to a default othersise.
401 */
Luis Hector Chavez71323552017-09-05 09:17:22 -0700402 ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
403 if (sz == -1)
404 sz = 65536; /* your guess is as good as mine... */
405
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100406 do {
407 buf = malloc(sz);
408 if (!buf)
409 return -ENOMEM;
410 int err = getpwnam_r(user, &pw, buf, sz, &ppw);
411 /*
412 * We're safe to free the buffer here. The strings inside |pw|
413 * point inside |buf|, but we don't use any of them; this leaves
414 * the pointers dangling but it's safe.
415 * |ppw| points at |pw| if getpwnam_r(3) succeeded.
416 */
417 free(buf);
418 if (err == ERANGE) {
419 /* |buf| was too small, retry with a bigger one. */
420 sz <<= 1;
421 } else if (err != 0) {
422 /* We got an error not related to the size of |buf|. */
423 return -err;
424 } else if (!ppw) {
425 /* Not found. */
426 return -ENOENT;
427 } else {
428 *uid = ppw->pw_uid;
429 *gid = ppw->pw_gid;
430 return 0;
431 }
432 } while (sz <= MAX_PWENT_SZ);
Mattias Nissler160d58f2020-02-25 11:01:30 +0100433
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100434 /* A buffer of size MAX_PWENT_SZ is still too small, return an error. */
435 return -ERANGE;
Luis Hector Chavez71323552017-09-05 09:17:22 -0700436}
437
438/*
439 * lookup_group: Gets the gid for the given group name.
440 */
441int lookup_group(const char *group, gid_t *gid)
442{
443 char *buf = NULL;
444 struct group gr;
445 struct group *pgr = NULL;
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100446 /*
447 * sysconf(_SC_GETGR_R_SIZE_MAX), under glibc, is documented to return
448 * a suggested starting size for the buffer, so let's try getting this
449 * size first, and fallback to a default otherwise.
450 */
Luis Hector Chavez71323552017-09-05 09:17:22 -0700451 ssize_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
452 if (sz == -1)
453 sz = 65536; /* and mine is as good as yours, really */
454
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100455 do {
456 buf = malloc(sz);
457 if (!buf)
458 return -ENOMEM;
459 int err = getgrnam_r(group, &gr, buf, sz, &pgr);
460 /*
461 * We're safe to free the buffer here. The strings inside |gr|
462 * point inside |buf|, but we don't use any of them; this leaves
463 * the pointers dangling but it's safe.
464 * |pgr| points at |gr| if getgrnam_r(3) succeeded.
465 */
466 free(buf);
467 if (err == ERANGE) {
468 /* |buf| was too small, retry with a bigger one. */
469 sz <<= 1;
470 } else if (err != 0) {
471 /* We got an error not related to the size of |buf|. */
472 return -err;
473 } else if (!pgr) {
474 /* Not found. */
475 return -ENOENT;
476 } else {
477 *gid = pgr->gr_gid;
478 return 0;
479 }
480 } while (sz <= MAX_GRENT_SZ);
Mattias Nissler160d58f2020-02-25 11:01:30 +0100481
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100482 /* A buffer of size MAX_GRENT_SZ is still too small, return an error. */
483 return -ERANGE;
Luis Hector Chavez71323552017-09-05 09:17:22 -0700484}
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400485
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800486static bool seccomp_action_is_available(const char *wanted)
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400487{
488 if (is_android()) {
489 /*
490 * Accessing |actions_avail| is generating SELinux denials, so
491 * skip for now.
492 * TODO(crbug.com/978022, jorgelo): Remove once the denial is
493 * fixed.
494 */
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800495 return false;
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400496 }
497 const char actions_avail_path[] =
498 "/proc/sys/kernel/seccomp/actions_avail";
499 FILE *f = fopen(actions_avail_path, "re");
500
501 if (!f) {
502 pwarn("fopen(%s) failed", actions_avail_path);
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800503 return false;
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400504 }
505
506 char *actions_avail = NULL;
507 size_t buf_size = 0;
508 if (getline(&actions_avail, &buf_size, f) < 0) {
509 pwarn("getline() failed");
510 free(actions_avail);
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800511 return false;
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400512 }
513
514 /*
515 * This is just substring search, which means that partial matches will
516 * match too (e.g. "action" would match "longaction"). There are no
517 * seccomp actions which include other actions though, so we're good for
518 * now. Eventually we might want to split the string by spaces.
519 */
Luis Héctor Chávez4baeafa2021-01-03 05:47:13 -0800520 bool available = strstr(actions_avail, wanted) != NULL;
521 free(actions_avail);
522 return available;
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400523}
524
525int seccomp_ret_log_available(void)
526{
527 static int ret_log_available = -1;
528
529 if (ret_log_available == -1)
530 ret_log_available = seccomp_action_is_available("log");
531
532 return ret_log_available;
533}
534
535int seccomp_ret_kill_process_available(void)
536{
537 static int ret_kill_process_available = -1;
538
539 if (ret_kill_process_available == -1)
540 ret_kill_process_available =
541 seccomp_action_is_available("kill_process");
542
543 return ret_kill_process_available;
544}
Luis Héctor Chávez01b628c2021-01-03 05:46:57 -0800545
546bool seccomp_filter_flags_available(unsigned int flags)
547{
548 return sys_seccomp(SECCOMP_SET_MODE_FILTER, flags, NULL) != -1 ||
549 errno != EINVAL;
550}