blob: ae7f02cb1a065bb323abd694a927bfc96910c43f [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
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040025#include "util.h"
26
Mattias Nisslere5200192018-10-18 12:29:40 +020027/*
28 * SECBIT_NO_CAP_AMBIENT_RAISE was added in kernel 4.3, so fill in the
29 * definition if the securebits header doesn't provide it.
30 */
31#ifndef SECBIT_NO_CAP_AMBIENT_RAISE
32#define SECBIT_NO_CAP_AMBIENT_RAISE (issecure_mask(6))
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040033#endif
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040034
Mattias Nisslere5200192018-10-18 12:29:40 +020035#ifndef SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED
36#define SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED (issecure_mask(7))
37#endif
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040038
39/*
40 * Assert the value of SECURE_ALL_BITS at compile-time.
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040041 * Android devices are currently compiled against 4.4 kernel headers. Kernel 4.3
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040042 * added a new securebit.
43 * When a new securebit is added, the new SECURE_ALL_BITS mask will return EPERM
44 * when used on older kernels. The compile-time assert will catch this situation
45 * at compile time.
46 */
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040047#if defined(__ANDROID__)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040048_Static_assert(SECURE_ALL_BITS == 0x55, "SECURE_ALL_BITS == 0x55.");
49#endif
50
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +010051/* Used by lookup_(user|group) functions. */
52#define MAX_PWENT_SZ (1 << 20)
53#define MAX_GRENT_SZ (1 << 20)
54
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040055int secure_noroot_set_and_locked(uint64_t mask)
56{
57 return (mask & (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED)) ==
58 (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED);
59}
60
Mattias Nissler48b5ff12018-10-11 15:31:41 +020061int lock_securebits(uint64_t skip_mask, bool require_keep_caps)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040062{
Mattias Nissler48b5ff12018-10-11 15:31:41 +020063 /* The general idea is to set all bits, subject to exceptions below. */
64 unsigned long securebits = SECURE_ALL_BITS | SECURE_ALL_LOCKS;
65
66 /*
67 * SECBIT_KEEP_CAPS is special in that it is automatically cleared on
68 * execve(2). This implies that attempts to set SECBIT_KEEP_CAPS (as is
69 * the default) in processes that have it locked already (such as nested
70 * minijail usage) would fail. Thus, unless the caller requires it,
71 * allow it to remain off if it is already locked.
72 */
73 if (!require_keep_caps) {
74 int current_securebits = prctl(PR_GET_SECUREBITS);
75 if (current_securebits < 0) {
76 pwarn("prctl(PR_GET_SECUREBITS) failed");
77 return -1;
78 }
79
80 if ((current_securebits & SECBIT_KEEP_CAPS_LOCKED) != 0 &&
81 (current_securebits & SECBIT_KEEP_CAPS) == 0) {
82 securebits &= ~SECBIT_KEEP_CAPS;
83 }
84 }
85
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040086 /*
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -040087 * Ambient capabilities can only be raised if they're already present
88 * in the permitted *and* inheritable set. Therefore, we don't really
89 * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already
90 * configuring the permitted and inheritable set.
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -040091 */
Mattias Nissler48b5ff12018-10-11 15:31:41 +020092 securebits &=
93 ~(SECBIT_NO_CAP_AMBIENT_RAISE | SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED);
94
95 /* Don't set any bits that the user requested not to be touched. */
96 securebits &= ~skip_mask;
97
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -070098 if (!securebits) {
Jorge Lucangeli Obes54234212018-04-26 11:52:15 -040099 warn("not locking any securebits");
Luis Hector Chavezec0a2c12017-06-29 20:29:57 -0700100 return 0;
101 }
102 int securebits_ret = prctl(PR_SET_SECUREBITS, securebits);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400103 if (securebits_ret < 0) {
104 pwarn("prctl(PR_SET_SECUREBITS) failed");
105 return -1;
106 }
107
108 return 0;
109}
110
111int write_proc_file(pid_t pid, const char *content, const char *basename)
112{
113 int fd, ret;
114 size_t sz, len;
115 ssize_t written;
116 char filename[32];
117
118 sz = sizeof(filename);
119 ret = snprintf(filename, sz, "/proc/%d/%s", pid, basename);
120 if (ret < 0 || (size_t)ret >= sz) {
121 warn("failed to generate %s filename", basename);
122 return -1;
123 }
124
125 fd = open(filename, O_WRONLY | O_CLOEXEC);
126 if (fd < 0) {
127 pwarn("failed to open '%s'", filename);
128 return -errno;
129 }
130
131 len = strlen(content);
132 written = write(fd, content, len);
133 if (written < 0) {
134 pwarn("failed to write '%s'", filename);
Jorge Lucangeli Obes673c89d2018-10-04 16:08:10 -0400135 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400136 }
137
138 if ((size_t)written < len) {
139 warn("failed to write %zu bytes to '%s'", len, filename);
140 return -1;
141 }
142 close(fd);
143 return 0;
144}
145
146/*
147 * We specifically do not use cap_valid() as that only tells us the last
148 * valid cap we were *compiled* against (i.e. what the version of kernel
149 * headers says). If we run on a different kernel version, then it's not
150 * uncommon for that to be less (if an older kernel) or more (if a newer
151 * kernel).
152 * Normally, we suck up the answer via /proc. On Android, not all processes are
153 * guaranteed to be able to access '/proc/sys/kernel/cap_last_cap' so we
154 * programmatically find the value by calling prctl(PR_CAPBSET_READ).
155 */
156unsigned int get_last_valid_cap(void)
157{
158 unsigned int last_valid_cap = 0;
159 if (is_android()) {
160 for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0;
161 ++last_valid_cap)
162 ;
163
164 /* |last_valid_cap| will be the first failing value. */
165 if (last_valid_cap > 0) {
166 last_valid_cap--;
167 }
168 } else {
169 const char cap_file[] = "/proc/sys/kernel/cap_last_cap";
170 FILE *fp = fopen(cap_file, "re");
171 if (fscanf(fp, "%u", &last_valid_cap) != 1)
172 pdie("fscanf(%s)", cap_file);
173 fclose(fp);
174 }
175 return last_valid_cap;
176}
177
Jorge Lucangeli Obesa6eb21a2017-04-20 10:44:00 -0400178int cap_ambient_supported(void)
179{
180 return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >=
181 0;
182}
183
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400184int config_net_loopback(void)
185{
186 const char ifname[] = "lo";
187 int sock;
188 struct ifreq ifr;
189
190 /* Make sure people don't try to add really long names. */
191 _Static_assert(sizeof(ifname) <= IFNAMSIZ, "interface name too long");
192
193 sock = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
194 if (sock < 0) {
195 pwarn("socket(AF_LOCAL) failed");
196 return -1;
197 }
198
199 /*
200 * Do the equiv of `ip link set up lo`. The kernel will assign
201 * IPv4 (127.0.0.1) & IPv6 (::1) addresses automatically!
202 */
203 strcpy(ifr.ifr_name, ifname);
204 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
205 pwarn("ioctl(SIOCGIFFLAGS) failed");
206 return -1;
207 }
208
209 /* The kernel preserves ifr.ifr_name for use. */
210 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
211 if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) {
212 pwarn("ioctl(SIOCSIFFLAGS) failed");
213 return -1;
214 }
215
216 close(sock);
217 return 0;
218}
219
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400220int write_pid_to_path(pid_t pid, const char *path)
221{
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400222 FILE *fp = fopen(path, "we");
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400223
224 if (!fp) {
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400225 pwarn("failed to open '%s'", path);
226 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400227 }
228 if (fprintf(fp, "%d\n", (int)pid) < 0) {
229 /* fprintf(3) does not set errno on failure. */
230 warn("fprintf(%s) failed", path);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400231 return -1;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400232 }
233 if (fclose(fp)) {
234 pwarn("fclose(%s) failed", path);
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400235 return -errno;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400236 }
237
Jorge Lucangeli Obes1f5d0952019-06-04 09:18:26 -0400238 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400239}
240
241/*
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500242 * Create the |path| directory and its parents (if need be) with |mode|.
243 * If not |isdir|, then |path| is actually a file, so the last component
244 * will not be created.
245 */
246int mkdir_p(const char *path, mode_t mode, bool isdir)
247{
yusukes059e0bd2018-03-05 10:22:16 -0800248 int rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500249 char *dir = strdup(path);
yusukes059e0bd2018-03-05 10:22:16 -0800250 if (!dir) {
251 rc = errno;
252 pwarn("strdup(%s) failed", path);
253 return -rc;
254 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500255
256 /* Starting from the root, work our way out to the end. */
257 char *p = strchr(dir + 1, '/');
258 while (p) {
259 *p = '\0';
260 if (mkdir(dir, mode) && errno != EEXIST) {
yusukes059e0bd2018-03-05 10:22:16 -0800261 rc = errno;
262 pwarn("mkdir(%s, 0%o) failed", dir, mode);
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500263 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800264 return -rc;
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500265 }
266 *p = '/';
267 p = strchr(p + 1, '/');
268 }
269
270 /*
271 * Create the last directory. We still check EEXIST here in case
272 * of trailing slashes.
273 */
274 free(dir);
yusukes059e0bd2018-03-05 10:22:16 -0800275 if (isdir && mkdir(path, mode) && errno != EEXIST) {
276 rc = errno;
277 pwarn("mkdir(%s, 0%o) failed", path, mode);
278 return -rc;
279 }
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500280 return 0;
281}
282
283/*
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400284 * setup_mount_destination: Ensures the mount target exists.
285 * Creates it if needed and possible.
286 */
287int setup_mount_destination(const char *source, const char *dest, uid_t uid,
Luis Hector Chavez0bacbf82018-07-10 20:06:55 -0700288 uid_t gid, bool bind, unsigned long *mnt_flags)
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400289{
290 int rc;
291 struct stat st_buf;
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400292 bool domkdir;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400293
294 rc = stat(dest, &st_buf);
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400295 if (rc == 0) /* destination exists */
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400296 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400297
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400298 /*
299 * Try to create the destination.
300 * Either make a directory or touch a file depending on the source type.
301 *
302 * If the source isn't an absolute path, assume it is a filesystem type
303 * such as "tmpfs" and create a directory to mount it on. The dest will
304 * be something like "none" or "proc" which we shouldn't be checking.
305 */
306 if (source[0] == '/') {
307 /* The source is an absolute path -- it better exist! */
308 rc = stat(source, &st_buf);
309 if (rc) {
Jorge Lucangeli Obes9299cae2019-08-23 11:28:39 -0400310 rc = errno;
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400311 pwarn("stat(%s) failed", source);
Jorge Lucangeli Obes9299cae2019-08-23 11:28:39 -0400312 return -rc;
313 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400314
315 /*
316 * If bind mounting, we only create a directory if the source
317 * is a directory, else we always bind mount it as a file to
318 * support device nodes, sockets, etc...
319 *
320 * For all other mounts, we assume a block/char source is
321 * going to want a directory to mount to. If the source is
322 * something else (e.g. a fifo or socket), this probably will
323 * not do the right thing, but we'll fail later on when we try
324 * to mount(), so shouldn't be a big deal.
325 */
326 domkdir = S_ISDIR(st_buf.st_mode) ||
327 (!bind && (S_ISBLK(st_buf.st_mode) ||
328 S_ISCHR(st_buf.st_mode)));
329
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400330 /* If bind mounting, also grab the mount flags of the source. */
331 if (bind && mnt_flags) {
332 struct statvfs stvfs_buf;
333 rc = statvfs(source, &stvfs_buf);
334 if (rc) {
335 rc = errno;
336 pwarn(
337 "failed to look up mount flags: source=%s",
338 source);
339 return -rc;
340 }
341 *mnt_flags = stvfs_buf.f_flag;
342 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400343 } else {
344 /* The source is a relative path -- assume it's a pseudo fs. */
345
346 /* Disallow relative bind mounts. */
347 if (bind) {
348 warn("relative bind-mounts are not allowed: source=%s",
349 source);
350 return -EINVAL;
351 }
352
353 domkdir = true;
Mike Frysingereaab4202017-08-14 14:57:21 -0400354 }
355
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500356 /*
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400357 * Now that we know what we want to do, do it!
358 * We always create the intermediate dirs and the final path with 0755
359 * perms and root/root ownership. This shouldn't be a problem because
360 * the actual mount will set those perms/ownership on the mount point
361 * which is all people should need to access it.
Mike Frysinger5fdba4e2018-01-17 15:39:48 -0500362 */
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400363 rc = mkdir_p(dest, 0755, domkdir);
364 if (rc)
365 return rc;
366 if (!domkdir) {
367 int fd = open(dest, O_RDWR | O_CREAT | O_CLOEXEC, 0700);
368 if (fd < 0) {
369 rc = errno;
370 pwarn("open(%s) failed", dest);
371 return -rc;
372 }
373 close(fd);
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400374 }
Jorge Lucangeli Obes7654c6e2019-09-09 10:45:38 -0400375 if (chown(dest, uid, gid)) {
376 rc = errno;
377 pwarn("chown(%s, %u, %u) failed", dest, uid, gid);
378 return -rc;
379 }
Jorge Lucangeli Obesb4b7c5a2019-09-09 10:47:36 -0400380 return 0;
Jorge Lucangeli Obes0b208772017-04-19 14:15:46 -0400381}
Luis Hector Chavez71323552017-09-05 09:17:22 -0700382
383/*
384 * lookup_user: Gets the uid/gid for the given username.
385 */
386int lookup_user(const char *user, uid_t *uid, gid_t *gid)
387{
388 char *buf = NULL;
389 struct passwd pw;
390 struct passwd *ppw = NULL;
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100391 /*
392 * sysconf(_SC_GETPW_R_SIZE_MAX), under glibc, is documented to return
393 * a suggested starting size for the buffer, so let's try getting this
394 * size first, and fallback to a default othersise.
395 */
Luis Hector Chavez71323552017-09-05 09:17:22 -0700396 ssize_t sz = sysconf(_SC_GETPW_R_SIZE_MAX);
397 if (sz == -1)
398 sz = 65536; /* your guess is as good as mine... */
399
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100400 do {
401 buf = malloc(sz);
402 if (!buf)
403 return -ENOMEM;
404 int err = getpwnam_r(user, &pw, buf, sz, &ppw);
405 /*
406 * We're safe to free the buffer here. The strings inside |pw|
407 * point inside |buf|, but we don't use any of them; this leaves
408 * the pointers dangling but it's safe.
409 * |ppw| points at |pw| if getpwnam_r(3) succeeded.
410 */
411 free(buf);
412 if (err == ERANGE) {
413 /* |buf| was too small, retry with a bigger one. */
414 sz <<= 1;
415 } else if (err != 0) {
416 /* We got an error not related to the size of |buf|. */
417 return -err;
418 } else if (!ppw) {
419 /* Not found. */
420 return -ENOENT;
421 } else {
422 *uid = ppw->pw_uid;
423 *gid = ppw->pw_gid;
424 return 0;
425 }
426 } while (sz <= MAX_PWENT_SZ);
Mattias Nissler160d58f2020-02-25 11:01:30 +0100427
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100428 /* A buffer of size MAX_PWENT_SZ is still too small, return an error. */
429 return -ERANGE;
Luis Hector Chavez71323552017-09-05 09:17:22 -0700430}
431
432/*
433 * lookup_group: Gets the gid for the given group name.
434 */
435int lookup_group(const char *group, gid_t *gid)
436{
437 char *buf = NULL;
438 struct group gr;
439 struct group *pgr = NULL;
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100440 /*
441 * sysconf(_SC_GETGR_R_SIZE_MAX), under glibc, is documented to return
442 * a suggested starting size for the buffer, so let's try getting this
443 * size first, and fallback to a default otherwise.
444 */
Luis Hector Chavez71323552017-09-05 09:17:22 -0700445 ssize_t sz = sysconf(_SC_GETGR_R_SIZE_MAX);
446 if (sz == -1)
447 sz = 65536; /* and mine is as good as yours, really */
448
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100449 do {
450 buf = malloc(sz);
451 if (!buf)
452 return -ENOMEM;
453 int err = getgrnam_r(group, &gr, buf, sz, &pgr);
454 /*
455 * We're safe to free the buffer here. The strings inside |gr|
456 * point inside |buf|, but we don't use any of them; this leaves
457 * the pointers dangling but it's safe.
458 * |pgr| points at |gr| if getgrnam_r(3) succeeded.
459 */
460 free(buf);
461 if (err == ERANGE) {
462 /* |buf| was too small, retry with a bigger one. */
463 sz <<= 1;
464 } else if (err != 0) {
465 /* We got an error not related to the size of |buf|. */
466 return -err;
467 } else if (!pgr) {
468 /* Not found. */
469 return -ENOENT;
470 } else {
471 *gid = pgr->gr_gid;
472 return 0;
473 }
474 } while (sz <= MAX_GRENT_SZ);
Mattias Nissler160d58f2020-02-25 11:01:30 +0100475
Stéphane Lesimple84ffabc2020-02-14 20:33:34 +0100476 /* A buffer of size MAX_GRENT_SZ is still too small, return an error. */
477 return -ERANGE;
Luis Hector Chavez71323552017-09-05 09:17:22 -0700478}
Jorge Lucangeli Obes32201f82019-06-12 14:45:06 -0400479
480static int seccomp_action_is_available(const char *wanted)
481{
482 if (is_android()) {
483 /*
484 * Accessing |actions_avail| is generating SELinux denials, so
485 * skip for now.
486 * TODO(crbug.com/978022, jorgelo): Remove once the denial is
487 * fixed.
488 */
489 return 0;
490 }
491 const char actions_avail_path[] =
492 "/proc/sys/kernel/seccomp/actions_avail";
493 FILE *f = fopen(actions_avail_path, "re");
494
495 if (!f) {
496 pwarn("fopen(%s) failed", actions_avail_path);
497 return 0;
498 }
499
500 char *actions_avail = NULL;
501 size_t buf_size = 0;
502 if (getline(&actions_avail, &buf_size, f) < 0) {
503 pwarn("getline() failed");
504 free(actions_avail);
505 return 0;
506 }
507
508 /*
509 * This is just substring search, which means that partial matches will
510 * match too (e.g. "action" would match "longaction"). There are no
511 * seccomp actions which include other actions though, so we're good for
512 * now. Eventually we might want to split the string by spaces.
513 */
514 return strstr(actions_avail, wanted) != NULL;
515}
516
517int seccomp_ret_log_available(void)
518{
519 static int ret_log_available = -1;
520
521 if (ret_log_available == -1)
522 ret_log_available = seccomp_action_is_available("log");
523
524 return ret_log_available;
525}
526
527int seccomp_ret_kill_process_available(void)
528{
529 static int ret_kill_process_available = -1;
530
531 if (ret_kill_process_available == -1)
532 ret_kill_process_available =
533 seccomp_action_is_available("kill_process");
534
535 return ret_kill_process_available;
536}