]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/setpriv.c
textual: fix some typos and inconsistencies in usage and error messages
[thirdparty/util-linux.git] / sys-utils / setpriv.c
CommitLineData
5600c405
AL
1/*
2 * setpriv(1) - set various kernel privilege bits and run something
3 *
4 * Copyright (C) 2012 Andy Lutomirski <luto@amacapital.net>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include <cap-ng.h>
22#include <errno.h>
23#include <getopt.h>
24#include <grp.h>
25#include <linux/securebits.h>
637fa4c6 26#include <pwd.h>
5600c405
AL
27#include <stdarg.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/prctl.h>
637fa4c6 31#include <sys/types.h>
5600c405
AL
32#include <unistd.h>
33
acd8c7ab 34#include "all-io.h"
5600c405 35#include "c.h"
cef4decf 36#include "caputils.h"
5600c405
AL
37#include "closestream.h"
38#include "nls.h"
39#include "optutils.h"
40#include "strutils.h"
41#include "xalloc.h"
59c68b43 42#include "pathnames.h"
23f54ce7 43#include "signames.h"
89f95425 44#include "env.h"
ae015d71 45#include "setpriv-landlock.h"
acd8c7ab 46#include "seccomp.h"
5600c405
AL
47
48#ifndef PR_SET_NO_NEW_PRIVS
49# define PR_SET_NO_NEW_PRIVS 38
50#endif
51#ifndef PR_GET_NO_NEW_PRIVS
52# define PR_GET_NO_NEW_PRIVS 39
53#endif
54
55#define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
56
89f95425
KZ
57/* The shell to set SHELL env.variable if none is given in the user's passwd entry. */
58#define DEFAULT_SHELL "/bin/sh"
59
85c15c1f
KZ
60static gid_t get_group(const char *s, const char *err);
61
9e5dd89d
PS
62enum cap_type {
63 CAP_TYPE_EFFECTIVE = CAPNG_EFFECTIVE,
64 CAP_TYPE_PERMITTED = CAPNG_PERMITTED,
65 CAP_TYPE_INHERITABLE = CAPNG_INHERITABLE,
05a22eac
PS
66 CAP_TYPE_BOUNDING = CAPNG_BOUNDING_SET,
67 CAP_TYPE_AMBIENT = (1 << 4)
9e5dd89d
PS
68};
69
5600c405
AL
70/*
71 * Note: We are subject to https://bugzilla.redhat.com/show_bug.cgi?id=895105
72 * and we will therefore have problems if new capabilities are added. Once
73 * that bug is fixed, I'll (Andy Lutomirski) submit a corresponding fix to
74 * setpriv. In the mean time, the code here tries to work reasonably well.
75 */
76
77struct privctx {
5600c405
AL
78 unsigned int
79 nnp:1, /* no_new_privs */
80 have_ruid:1, /* real uid */
81 have_euid:1, /* effective uid */
82 have_rgid:1, /* real gid */
83 have_egid:1, /* effective gid */
94826d0d 84 have_passwd:1, /* passwd entry */
5600c405
AL
85 have_groups:1, /* add groups */
86 keep_groups:1, /* keep groups */
87 clear_groups:1, /* remove groups */
94826d0d 88 init_groups:1, /* initialize groups */
89f95425 89 reset_env:1, /* reset environment */
5600c405
AL
90 have_securebits:1; /* remove groups */
91
92 /* uids and gids */
93 uid_t ruid, euid;
94 gid_t rgid, egid;
95
94826d0d
SS
96 /* real user passwd entry */
97 struct passwd passwd;
98
5600c405
AL
99 /* supplementary groups */
100 size_t num_groups;
101 gid_t *groups;
102
103 /* caps */
104 const char *caps_to_inherit;
0c92194e 105 const char *ambient_caps;
5600c405
AL
106 const char *bounding_set;
107
108 /* securebits */
109 int securebits;
23f54ce7
PS
110 /* parent death signal (<0 clear, 0 nothing, >0 signal) */
111 int pdeathsig;
5600c405
AL
112
113 /* LSMs */
114 const char *selinux_label;
115 const char *apparmor_profile;
ae015d71 116 struct setpriv_landlock_opts landlock;
acd8c7ab 117 const char *seccomp_filter;
5600c405
AL
118};
119
86be6a32 120static void __attribute__((__noreturn__)) usage(void)
5600c405 121{
86be6a32 122 FILE *out = stdout;
5600c405 123 fputs(USAGE_HEADER, out);
298dc4ff
BS
124 fprintf(out, _(" %s [options] <program> [<argument>...]\n"),
125 program_invocation_short_name);
126
451dbcfa
BS
127 fputs(USAGE_SEPARATOR, out);
128 fputs(_("Run a program with different privilege settings.\n"), out);
129
5600c405 130 fputs(USAGE_OPTIONS, out);
4fb515f9
KZ
131 fputs(_(" -d, --dump show current state (and do not exec)\n"), out);
132 fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out);
133 fputs(_(" --ambient-caps <caps,...> set ambient capabilities\n"), out);
134 fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out);
135 fputs(_(" --bounding-set <caps> set capability bounding set\n"), out);
99179a0a
KZ
136 fputs(_(" --ruid <uid|user> set real uid\n"), out);
137 fputs(_(" --euid <uid|user> set effective uid\n"), out);
e67ad1d7 138 fputs(_(" --rgid <gid|group> set real gid\n"), out);
99179a0a
KZ
139 fputs(_(" --egid <gid|group> set effective gid\n"), out);
140 fputs(_(" --reuid <uid|user> set real and effective uid\n"), out);
141 fputs(_(" --regid <gid|group> set real and effective gid\n"), out);
4fb515f9
KZ
142 fputs(_(" --clear-groups clear supplementary groups\n"), out);
143 fputs(_(" --keep-groups keep supplementary groups\n"), out);
144 fputs(_(" --init-groups initialize supplementary groups\n"), out);
85c15c1f 145 fputs(_(" --groups <group,...> set supplementary groups by UID or name\n"), out);
4fb515f9 146 fputs(_(" --securebits <bits> set securebits\n"), out);
23f54ce7
PS
147 fputs(_(" --pdeathsig keep|clear|<signame>\n"
148 " set or clear parent death signal\n"), out);
4fb515f9
KZ
149 fputs(_(" --selinux-label <label> set SELinux label\n"), out);
150 fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
ae015d71
TW
151 fputs(_(" --landlock-access <access> add Landlock access\n"), out);
152 fputs(_(" --landlock-rule <rule> add Landlock rule\n"), out);
acd8c7ab 153 fputs(_(" --seccomp-filter <file> load seccomp filter from file\n"), out);
89f95425
KZ
154 fputs(_(" --reset-env clear all environment and initialize\n"
155 " HOME, SHELL, USER, LOGNAME and PATH\n"), out);
298dc4ff 156
5600c405 157 fputs(USAGE_SEPARATOR, out);
bad4c729 158 fprintf(out, USAGE_HELP_OPTIONS(29));
5600c405
AL
159 fputs(USAGE_SEPARATOR, out);
160 fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out);
bad4c729 161 fprintf(out, USAGE_MAN_TAIL("setpriv(1)"));
5600c405 162
ae015d71
TW
163 usage_setpriv(out);
164
86be6a32 165 exit(EXIT_SUCCESS);
5600c405
AL
166}
167
30129e2f
PS
168static int has_cap(enum cap_type which, unsigned int i)
169{
170 switch (which) {
171 case CAP_TYPE_EFFECTIVE:
172 case CAP_TYPE_BOUNDING:
173 case CAP_TYPE_INHERITABLE:
174 case CAP_TYPE_PERMITTED:
d14bcd09 175 return capng_have_capability((capng_type_t)which, i);
05a22eac
PS
176 case CAP_TYPE_AMBIENT:
177 return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET,
178 (unsigned long) i, 0UL, 0UL);
30129e2f
PS
179 default:
180 warnx(_("invalid capability type"));
181 return -1;
182 }
183}
184
5600c405 185/* Returns the number of capabilities printed. */
9e5dd89d 186static int print_caps(FILE *f, enum cap_type which)
5600c405 187{
cef4decf 188 int i, n = 0, max = cap_last_cap();
5600c405
AL
189
190 for (i = 0; i <= max; i++) {
30129e2f
PS
191 int ret = has_cap(which, i);
192
193 if (i == 0 && ret < 0)
194 return -1;
195
196 if (ret == 1) {
5600c405
AL
197 const char *name = capng_capability_to_name(i);
198 if (n)
199 fputc(',', f);
200 if (name)
201 fputs(name, f);
202 else
0eba195d 203 warnx(_("cap %d: libcap-ng is broken"), i);
5600c405
AL
204 n++;
205 }
206 }
9e5dd89d 207
5600c405
AL
208 return n;
209}
210
211static void dump_one_secbit(int *first, int *bits, int bit, const char *name)
212{
213 if (*bits & bit) {
7d55b2df 214 if (*first)
5600c405 215 *first = 0;
7d55b2df
SK
216 else
217 printf(",");
5600c405
AL
218 fputs(name, stdout);
219 *bits &= ~bit;
220 }
221}
222
223static void dump_securebits(void)
224{
225 int first = 1;
226 int bits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
227
228 if (bits < 0) {
229 warnx(_("getting process secure bits failed"));
230 return;
231 }
232
233 printf(_("Securebits: "));
234
235 dump_one_secbit(&first, &bits, SECBIT_NOROOT, "noroot");
236 dump_one_secbit(&first, &bits, SECBIT_NOROOT_LOCKED, "noroot_locked");
237 dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP,
238 "no_setuid_fixup");
239 dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP_LOCKED,
240 "no_setuid_fixup_locked");
241 bits &= ~SECBIT_KEEP_CAPS;
242 dump_one_secbit(&first, &bits, SECBIT_KEEP_CAPS_LOCKED,
243 "keep_caps_locked");
244 if (bits) {
7d55b2df 245 if (first)
5600c405 246 first = 0;
7d55b2df
SK
247 else
248 printf(",");
5600c405
AL
249 printf("0x%x", (unsigned)bits);
250 }
251
252 if (first)
253 printf(_("[none]\n"));
254 else
255 printf("\n");
256}
257
258static void dump_label(const char *name)
259{
260 char buf[4097];
261 ssize_t len;
262 int fd, e;
263
59c68b43 264 fd = open(_PATH_PROC_ATTR_CURRENT, O_RDONLY);
5600c405 265 if (fd == -1) {
59c68b43 266 warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT);
5600c405
AL
267 return;
268 }
269
270 len = read(fd, buf, sizeof(buf));
271 e = errno;
272 close(fd);
273 if (len < 0) {
274 errno = e;
47481cbd 275 warn(_("cannot read %s"), name);
5600c405
AL
276 return;
277 }
278 if (sizeof(buf) - 1 <= (size_t)len) {
279 warnx(_("%s: too long"), name);
280 return;
281 }
282
283 buf[len] = 0;
284 if (0 < len && buf[len - 1] == '\n')
285 buf[len - 1] = 0;
286 printf("%s: %s\n", name, buf);
287}
288
289static void dump_groups(void)
290{
87918040 291 int n = getgroups(0, NULL);
5600c405 292 gid_t *groups;
59c68b43 293
5600c405
AL
294 if (n < 0) {
295 warn("getgroups failed");
296 return;
297 }
298
7370501f 299 groups = xmalloc(n * sizeof(gid_t));
5600c405
AL
300 n = getgroups(n, groups);
301 if (n < 0) {
7370501f 302 free(groups);
5600c405
AL
303 warn("getgroups failed");
304 return;
305 }
306
307 printf(_("Supplementary groups: "));
308 if (n == 0)
309 printf(_("[none]"));
310 else {
311 int i;
312 for (i = 0; i < n; i++) {
313 if (0 < i)
314 printf(",");
315 printf("%ld", (long)groups[i]);
316 }
317 }
318 printf("\n");
7370501f 319 free(groups);
5600c405
AL
320}
321
23f54ce7
PS
322static void dump_pdeathsig(void)
323{
324 int pdeathsig;
325
326 if (prctl(PR_GET_PDEATHSIG, &pdeathsig) != 0) {
327 warn(_("get pdeathsig failed"));
328 return;
329 }
330
0eba195d 331 printf(_("Parent death signal: "));
23f54ce7
PS
332 if (pdeathsig && signum_to_signame(pdeathsig) != NULL)
333 printf("%s\n", signum_to_signame(pdeathsig));
334 else if (pdeathsig)
335 printf("%d\n", pdeathsig);
336 else
0eba195d 337 printf(_("[none]\n"));
23f54ce7
PS
338}
339
5600c405
AL
340static void dump(int dumplevel)
341{
342 int x;
343 uid_t ru, eu, su;
344 gid_t rg, eg, sg;
345
346 if (getresuid(&ru, &eu, &su) == 0) {
347 printf(_("uid: %u\n"), ru);
348 printf(_("euid: %u\n"), eu);
349 /* Saved and fs uids always equal euid. */
350 if (3 <= dumplevel)
351 printf(_("suid: %u\n"), su);
352 } else
353 warn(_("getresuid failed"));
354
355 if (getresgid(&rg, &eg, &sg) == 0) {
356 printf("gid: %ld\n", (long)rg);
357 printf("egid: %ld\n", (long)eg);
358 /* Saved and fs gids always equal egid. */
359 if (dumplevel >= 3)
360 printf("sgid: %ld\n", (long)sg);
361 } else
362 warn(_("getresgid failed"));
363
364 dump_groups();
365
366 x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
367 if (0 <= x)
368 printf("no_new_privs: %d\n", x);
369 else
370 warn("setting no_new_privs failed");
371
372 if (2 <= dumplevel) {
373 printf(_("Effective capabilities: "));
9e5dd89d 374 if (print_caps(stdout, CAP_TYPE_EFFECTIVE) == 0)
5600c405
AL
375 printf(_("[none]"));
376 printf("\n");
377
378 printf(_("Permitted capabilities: "));
9e5dd89d 379 if (print_caps(stdout, CAP_TYPE_PERMITTED) == 0)
5600c405
AL
380 printf(_("[none]"));
381 printf("\n");
382 }
383
384 printf(_("Inheritable capabilities: "));
9e5dd89d 385 if (print_caps(stdout, CAP_TYPE_INHERITABLE) == 0)
5600c405
AL
386 printf(_("[none]"));
387 printf("\n");
388
05a22eac
PS
389 printf(_("Ambient capabilities: "));
390 x = print_caps(stdout, CAP_TYPE_AMBIENT);
391 if (x == 0)
392 printf(_("[none]"));
393 if (x < 0)
394 printf(_("[unsupported]"));
395 printf("\n");
396
5600c405 397 printf(_("Capability bounding set: "));
9e5dd89d 398 if (print_caps(stdout, CAP_TYPE_BOUNDING) == 0)
5600c405
AL
399 printf(_("[none]"));
400 printf("\n");
401
402 dump_securebits();
23f54ce7 403 dump_pdeathsig();
5600c405 404
59c68b43 405 if (access(_PATH_SYS_SELINUX, F_OK) == 0)
5600c405
AL
406 dump_label(_("SELinux label"));
407
59c68b43 408 if (access(_PATH_SYS_APPARMOR, F_OK) == 0) {
5600c405
AL
409 dump_label(_("AppArmor profile"));
410 }
411}
412
413static void list_known_caps(void)
414{
cef4decf 415 int i, max = cap_last_cap();
5600c405
AL
416
417 for (i = 0; i <= max; i++) {
418 const char *name = capng_capability_to_name(i);
419 if (name)
420 printf("%s\n", name);
421 else
422 warnx(_("cap %d: libcap-ng is broken"), i);
423 }
424}
425
426static void parse_groups(struct privctx *opts, const char *str)
427{
428 char *groups = xstrdup(str);
429 char *buf = groups; /* We'll reuse it */
430 char *c;
431 size_t i = 0;
432
433 opts->have_groups = 1;
434 opts->num_groups = 0;
435 while ((c = strsep(&groups, ",")))
436 opts->num_groups++;
437
438 /* Start again */
439 strcpy(buf, str); /* It's exactly the right length */
440 groups = buf;
441
442 opts->groups = xcalloc(opts->num_groups, sizeof(gid_t));
443 while ((c = strsep(&groups, ",")))
85c15c1f 444 opts->groups[i++] = get_group(c, _("Invalid supplementary group id"));
5600c405
AL
445
446 free(groups);
447}
448
23f54ce7
PS
449static void parse_pdeathsig(struct privctx *opts, const char *str)
450{
451 if (!strcmp(str, "keep")) {
452 if (prctl(PR_GET_PDEATHSIG, &opts->pdeathsig) != 0)
453 errx(SETPRIV_EXIT_PRIVERR,
454 _("failed to get parent death signal"));
455 } else if (!strcmp(str, "clear")) {
456 opts->pdeathsig = -1;
457 } else if ((opts->pdeathsig = signame_to_signum(str)) < 0) {
458 errx(EXIT_FAILURE, _("unknown signal: %s"), str);
459 }
460}
461
5600c405
AL
462static void do_setresuid(const struct privctx *opts)
463{
464 uid_t ruid, euid, suid;
465 if (getresuid(&ruid, &euid, &suid) != 0)
466 err(SETPRIV_EXIT_PRIVERR, _("getresuid failed"));
467 if (opts->have_ruid)
468 ruid = opts->ruid;
469 if (opts->have_euid)
470 euid = opts->euid;
471
472 /* Also copy effective to saved (for paranoia). */
473 if (setresuid(ruid, euid, euid) != 0)
474 err(SETPRIV_EXIT_PRIVERR, _("setresuid failed"));
475}
476
477static void do_setresgid(const struct privctx *opts)
478{
479 gid_t rgid, egid, sgid;
480 if (getresgid(&rgid, &egid, &sgid) != 0)
481 err(SETPRIV_EXIT_PRIVERR, _("getresgid failed"));
482 if (opts->have_rgid)
483 rgid = opts->rgid;
484 if (opts->have_egid)
485 egid = opts->egid;
486
487 /* Also copy effective to saved (for paranoia). */
488 if (setresgid(rgid, egid, egid) != 0)
489 err(SETPRIV_EXIT_PRIVERR, _("setresgid failed"));
490}
491
492static void bump_cap(unsigned int cap)
493{
494 if (capng_have_capability(CAPNG_PERMITTED, cap))
495 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap);
496}
497
8a5af72c
PS
498static int cap_update(capng_act_t action,
499 enum cap_type type, unsigned int cap)
500{
501 switch (type) {
502 case CAP_TYPE_EFFECTIVE:
503 case CAP_TYPE_BOUNDING:
504 case CAP_TYPE_INHERITABLE:
505 case CAP_TYPE_PERMITTED:
506 return capng_update(action, (capng_type_t) type, cap);
0c92194e
PS
507 case CAP_TYPE_AMBIENT:
508 {
509 int ret;
510
511 if (action == CAPNG_ADD)
512 ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE,
513 (unsigned long) cap, 0UL, 0UL);
514 else
515 ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER,
516 (unsigned long) cap, 0UL, 0UL);
517
518 return ret;
519 }
8a5af72c
PS
520 default:
521 errx(EXIT_FAILURE, _("unsupported capability type"));
522 return -1;
523 }
524}
525
9e5dd89d 526static void do_caps(enum cap_type type, const char *caps)
5600c405
AL
527{
528 char *my_caps = xstrdup(caps);
529 char *c;
530
531 while ((c = strsep(&my_caps, ","))) {
532 capng_act_t action;
533 if (*c == '+')
534 action = CAPNG_ADD;
535 else if (*c == '-')
536 action = CAPNG_DROP;
537 else
538 errx(EXIT_FAILURE, _("bad capability string"));
539
540 if (!strcmp(c + 1, "all")) {
541 int i;
93de9f68
ÉR
542 /* We can trust the return value from cap_last_cap(),
543 * so use that directly. */
544 for (i = 0; i <= cap_last_cap(); i++)
8a5af72c 545 cap_update(action, type, i);
5600c405
AL
546 } else {
547 int cap = capng_name_to_capability(c + 1);
548 if (0 <= cap)
8a5af72c 549 cap_update(action, type, cap);
fbd15c4d 550 else if (sscanf(c + 1, "cap_%d", &cap) == 1
cef4decf 551 && 0 <= cap && cap <= cap_last_cap())
fbd15c4d 552 cap_update(action, type, cap);
5600c405
AL
553 else
554 errx(EXIT_FAILURE,
555 _("unknown capability \"%s\""), c + 1);
556 }
557 }
558
559 free(my_caps);
560}
561
562static void parse_securebits(struct privctx *opts, const char *arg)
563{
564 char *buf = xstrdup(arg);
565 char *c;
566
567 opts->have_securebits = 1;
568 opts->securebits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
569 if (opts->securebits < 0)
570 err(SETPRIV_EXIT_PRIVERR, _("getting process secure bits failed"));
571
572 if (opts->securebits & ~(int)(SECBIT_NOROOT |
573 SECBIT_NOROOT_LOCKED |
574 SECBIT_NO_SETUID_FIXUP |
575 SECBIT_NO_SETUID_FIXUP_LOCKED |
576 SECBIT_KEEP_CAPS |
577 SECBIT_KEEP_CAPS_LOCKED))
578 errx(SETPRIV_EXIT_PRIVERR,
579 _("unrecognized securebit set -- refusing to adjust"));
580
581 while ((c = strsep(&buf, ","))) {
582 if (*c != '+' && *c != '-')
583 errx(EXIT_FAILURE, _("bad securebits string"));
584
585 if (!strcmp(c + 1, "all")) {
586 if (*c == '-')
587 opts->securebits = 0;
588 else
589 errx(EXIT_FAILURE,
590 _("+all securebits is not allowed"));
591 } else {
592 int bit;
593 if (!strcmp(c + 1, "noroot"))
594 bit = SECBIT_NOROOT;
595 else if (!strcmp(c + 1, "noroot_locked"))
596 bit = SECBIT_NOROOT_LOCKED;
597 else if (!strcmp(c + 1, "no_setuid_fixup"))
598 bit = SECBIT_NO_SETUID_FIXUP;
599 else if (!strcmp(c + 1, "no_setuid_fixup_locked"))
600 bit = SECBIT_NO_SETUID_FIXUP_LOCKED;
601 else if (!strcmp(c + 1, "keep_caps"))
602 errx(EXIT_FAILURE,
603 _("adjusting keep_caps does not make sense"));
604 else if (!strcmp(c + 1, "keep_caps_locked"))
605 bit = SECBIT_KEEP_CAPS_LOCKED; /* sigh */
606 else
607 errx(EXIT_FAILURE, _("unrecognized securebit"));
608
609 if (*c == '+')
610 opts->securebits |= bit;
611 else
612 opts->securebits &= ~bit;
613 }
614 }
615
616 opts->securebits |= SECBIT_KEEP_CAPS; /* We need it, and it's reset on exec */
617
618 free(buf);
619}
620
621static void do_selinux_label(const char *label)
622{
623 int fd;
624 size_t len;
625
59c68b43 626 if (access(_PATH_SYS_SELINUX, F_OK) != 0)
5600c405
AL
627 errx(SETPRIV_EXIT_PRIVERR, _("SELinux is not running"));
628
59c68b43 629 fd = open(_PATH_PROC_ATTR_EXEC, O_RDWR);
5600c405
AL
630 if (fd == -1)
631 err(SETPRIV_EXIT_PRIVERR,
59c68b43 632 _("cannot open %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
633
634 len = strlen(label);
635 errno = 0;
636 if (write(fd, label, len) != (ssize_t) len)
637 err(SETPRIV_EXIT_PRIVERR,
59c68b43 638 _("write failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405 639
cc89383b 640 if (close(fd) != 0)
05cef8ea 641 err(SETPRIV_EXIT_PRIVERR,
cc89383b 642 _("close failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
643}
644
645static void do_apparmor_profile(const char *label)
646{
647 FILE *f;
648
59c68b43 649 if (access(_PATH_SYS_APPARMOR, F_OK) != 0)
5600c405
AL
650 errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running"));
651
d359c62c 652 f = fopen(_PATH_PROC_ATTR_EXEC, "r+");
5600c405
AL
653 if (!f)
654 err(SETPRIV_EXIT_PRIVERR,
59c68b43 655 _("cannot open %s"), _PATH_PROC_ATTR_EXEC);
5600c405 656
d359c62c 657 fprintf(f, "exec %s", label);
f99b58b3
SK
658
659 if (close_stream(f) != 0)
5600c405 660 err(SETPRIV_EXIT_PRIVERR,
59c68b43 661 _("write failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
662}
663
acd8c7ab
TW
664static void do_seccomp_filter(const char *file)
665{
666 int fd;
667 ssize_t s;
668 char *filter;
669 struct sock_fprog prog = {};
670
671 fd = open(file, O_RDONLY);
672 if (fd == -1)
673 err(SETPRIV_EXIT_PRIVERR,
674 _("cannot open %s"), file);
675
676 s = read_all_alloc(fd, &filter);
677 if (s < 0)
678 err(SETPRIV_EXIT_PRIVERR,
679 _("cannot read %s"), file);
680
681 if (s % sizeof(*prog.filter))
682 errx(SETPRIV_EXIT_PRIVERR, _("invalid filter"));
683
684 prog.len = s / sizeof(*prog.filter);
685 prog.filter = (void *)filter;
686
687 /* *SET* below will return EINVAL when either the filter is invalid or
688 * seccomp is not supported. To distinguish those cases do a *GET* here
689 */
690 if (prctl(PR_GET_SECCOMP) == -1 && errno == EINVAL)
691 err(SETPRIV_EXIT_PRIVERR, _("Seccomp non-functional"));
692
693 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
694 err(SETPRIV_EXIT_PRIVERR, _("Could not run prctl(PR_SET_NO_NEW_PRIVS)"));
695
696 if (ul_set_seccomp_filter_spec_allow(&prog))
697 err(SETPRIV_EXIT_PRIVERR, _("Could not load seccomp filter"));
698
699 free(filter);
700 close(fd);
701
702}
703
89f95425
KZ
704static void do_reset_environ(struct passwd *pw)
705{
706 char *term = getenv("TERM");
707
708 if (term)
709 term = xstrdup(term);
710#ifdef HAVE_CLEARENV
711 clearenv();
712#else
713 environ = NULL;
714#endif
da14a74d 715 if (term) {
89f95425 716 xsetenv("TERM", term, 1);
da14a74d
KZ
717 free(term);
718 }
89f95425
KZ
719
720 if (pw->pw_shell && *pw->pw_shell)
721 xsetenv("SHELL", pw->pw_shell, 1);
722 else
723 xsetenv("SHELL", DEFAULT_SHELL, 1);
724
725 xsetenv("HOME", pw->pw_dir, 1);
726 xsetenv("USER", pw->pw_name, 1);
727 xsetenv("LOGNAME", pw->pw_name, 1);
728
729 if (pw->pw_uid)
730 xsetenv("PATH", _PATH_DEFPATH, 1);
731 else
732 xsetenv("PATH", _PATH_DEFPATH_ROOT, 1);
733}
734
637fa4c6
SK
735static uid_t get_user(const char *s, const char *err)
736{
737 struct passwd *pw;
738 long tmp;
739 pw = getpwnam(s);
740 if (pw)
741 return pw->pw_uid;
742 tmp = strtol_or_err(s, err);
743 return tmp;
744}
745
746static gid_t get_group(const char *s, const char *err)
747{
748 struct group *gr;
749 long tmp;
750 gr = getgrnam(s);
751 if (gr)
752 return gr->gr_gid;
753 tmp = strtol_or_err(s, err);
754 return tmp;
755}
756
94826d0d
SS
757static struct passwd *get_passwd(const char *s, uid_t *uid, const char *err)
758{
759 struct passwd *pw;
760 long tmp;
761 pw = getpwnam(s);
762 if (pw) {
763 *uid = pw->pw_uid;
764 } else {
765 tmp = strtol_or_err(s, err);
766 *uid = tmp;
767 pw = getpwuid(*uid);
768 }
769 return pw;
770}
771
772static struct passwd *passwd_copy(struct passwd *dst, const struct passwd *src)
773{
774 struct passwd *rv;
775 rv = memcpy(dst, src, sizeof(*dst));
776 rv->pw_name = xstrdup(rv->pw_name);
777 rv->pw_passwd = xstrdup(rv->pw_passwd);
778 rv->pw_gecos = xstrdup(rv->pw_gecos);
779 rv->pw_dir = xstrdup(rv->pw_dir);
780 rv->pw_shell = xstrdup(rv->pw_shell);
781 return rv;
782}
783
5600c405
AL
784int main(int argc, char **argv)
785{
786 enum {
787 NNP = CHAR_MAX + 1,
788 RUID,
789 EUID,
790 RGID,
791 EGID,
792 REUID,
793 REGID,
794 CLEAR_GROUPS,
795 KEEP_GROUPS,
94826d0d 796 INIT_GROUPS,
5600c405
AL
797 GROUPS,
798 INHCAPS,
0c92194e 799 AMBCAPS,
5600c405
AL
800 LISTCAPS,
801 CAPBSET,
802 SECUREBITS,
23f54ce7 803 PDEATHSIG,
5600c405 804 SELINUX_LABEL,
89f95425 805 APPARMOR_PROFILE,
ae015d71
TW
806 LANDLOCK_ACCESS,
807 LANDLOCK_RULE,
acd8c7ab 808 SECCOMP_FILTER,
89f95425 809 RESET_ENV
5600c405
AL
810 };
811
812 static const struct option longopts[] = {
87918040
SK
813 { "dump", no_argument, NULL, 'd' },
814 { "nnp", no_argument, NULL, NNP },
815 { "no-new-privs", no_argument, NULL, NNP },
816 { "inh-caps", required_argument, NULL, INHCAPS },
0c92194e 817 { "ambient-caps", required_argument, NULL, AMBCAPS },
87918040
SK
818 { "list-caps", no_argument, NULL, LISTCAPS },
819 { "ruid", required_argument, NULL, RUID },
820 { "euid", required_argument, NULL, EUID },
821 { "rgid", required_argument, NULL, RGID },
822 { "egid", required_argument, NULL, EGID },
823 { "reuid", required_argument, NULL, REUID },
824 { "regid", required_argument, NULL, REGID },
825 { "clear-groups", no_argument, NULL, CLEAR_GROUPS },
826 { "keep-groups", no_argument, NULL, KEEP_GROUPS },
94826d0d 827 { "init-groups", no_argument, NULL, INIT_GROUPS },
87918040
SK
828 { "groups", required_argument, NULL, GROUPS },
829 { "bounding-set", required_argument, NULL, CAPBSET },
830 { "securebits", required_argument, NULL, SECUREBITS },
23f54ce7 831 { "pdeathsig", required_argument, NULL, PDEATHSIG, },
87918040
SK
832 { "selinux-label", required_argument, NULL, SELINUX_LABEL },
833 { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
ae015d71
TW
834 { "landlock-access", required_argument, NULL, LANDLOCK_ACCESS },
835 { "landlock-rule", required_argument, NULL, LANDLOCK_RULE },
acd8c7ab 836 { "seccomp-filter", required_argument, NULL, SECCOMP_FILTER },
87918040 837 { "help", no_argument, NULL, 'h' },
89f95425 838 { "reset-env", no_argument, NULL, RESET_ENV, },
87918040
SK
839 { "version", no_argument, NULL, 'V' },
840 { NULL, 0, NULL, 0 }
5600c405
AL
841 };
842
843 static const ul_excl_t excl[] = {
844 /* keep in same order with enum definitions */
94826d0d 845 {CLEAR_GROUPS, KEEP_GROUPS, INIT_GROUPS, GROUPS},
5600c405
AL
846 {0}
847 };
848 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
849
850 int c;
851 struct privctx opts;
94826d0d 852 struct passwd *pw = NULL;
5600c405
AL
853 int dumplevel = 0;
854 int total_opts = 0;
855 int list_caps = 0;
856
a7a5c470 857 setlocale(LC_ALL, "");
5600c405
AL
858 bindtextdomain(PACKAGE, LOCALEDIR);
859 textdomain(PACKAGE);
2c308875 860 close_stdout_atexit();
5600c405
AL
861
862 memset(&opts, 0, sizeof(opts));
ae015d71 863 init_landlock_opts(&opts.landlock);
5600c405
AL
864
865 while ((c = getopt_long(argc, argv, "+dhV", longopts, NULL)) != -1) {
866 err_exclusive_options(c, longopts, excl, excl_st);
867 total_opts++;
868 switch (c) {
869 case 'd':
870 dumplevel++;
871 break;
872 case NNP:
873 if (opts.nnp)
874 errx(EXIT_FAILURE,
875 _("duplicate --no-new-privs option"));
876 opts.nnp = 1;
877 break;
878 case RUID:
879 if (opts.have_ruid)
880 errx(EXIT_FAILURE, _("duplicate ruid"));
881 opts.have_ruid = 1;
94826d0d
SS
882 pw = get_passwd(optarg, &opts.ruid, _("failed to parse ruid"));
883 if (pw) {
884 passwd_copy(&opts.passwd, pw);
885 opts.have_passwd = 1;
886 }
5600c405
AL
887 break;
888 case EUID:
889 if (opts.have_euid)
890 errx(EXIT_FAILURE, _("duplicate euid"));
891 opts.have_euid = 1;
637fa4c6 892 opts.euid = get_user(optarg, _("failed to parse euid"));
5600c405
AL
893 break;
894 case REUID:
895 if (opts.have_ruid || opts.have_euid)
896 errx(EXIT_FAILURE, _("duplicate ruid or euid"));
897 opts.have_ruid = opts.have_euid = 1;
94826d0d
SS
898 pw = get_passwd(optarg, &opts.ruid, _("failed to parse reuid"));
899 opts.euid = opts.ruid;
900 if (pw) {
901 passwd_copy(&opts.passwd, pw);
902 opts.have_passwd = 1;
903 }
5600c405
AL
904 break;
905 case RGID:
906 if (opts.have_rgid)
907 errx(EXIT_FAILURE, _("duplicate rgid"));
908 opts.have_rgid = 1;
637fa4c6 909 opts.rgid = get_group(optarg, _("failed to parse rgid"));
5600c405
AL
910 break;
911 case EGID:
912 if (opts.have_egid)
913 errx(EXIT_FAILURE, _("duplicate egid"));
914 opts.have_egid = 1;
637fa4c6 915 opts.egid = get_group(optarg, _("failed to parse egid"));
5600c405
AL
916 break;
917 case REGID:
918 if (opts.have_rgid || opts.have_egid)
919 errx(EXIT_FAILURE, _("duplicate rgid or egid"));
920 opts.have_rgid = opts.have_egid = 1;
637fa4c6 921 opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid"));
5600c405
AL
922 break;
923 case CLEAR_GROUPS:
924 if (opts.clear_groups)
925 errx(EXIT_FAILURE,
926 _("duplicate --clear-groups option"));
927 opts.clear_groups = 1;
928 break;
929 case KEEP_GROUPS:
930 if (opts.keep_groups)
931 errx(EXIT_FAILURE,
932 _("duplicate --keep-groups option"));
933 opts.keep_groups = 1;
934 break;
94826d0d
SS
935 case INIT_GROUPS:
936 if (opts.init_groups)
937 errx(EXIT_FAILURE,
938 _("duplicate --init-groups option"));
939 opts.init_groups = 1;
940 break;
5600c405
AL
941 case GROUPS:
942 if (opts.have_groups)
943 errx(EXIT_FAILURE,
944 _("duplicate --groups option"));
945 parse_groups(&opts, optarg);
946 break;
23f54ce7
PS
947 case PDEATHSIG:
948 if (opts.pdeathsig)
949 errx(EXIT_FAILURE,
950 _("duplicate --keep-pdeathsig option"));
951 parse_pdeathsig(&opts, optarg);
952 break;
5600c405
AL
953 case LISTCAPS:
954 list_caps = 1;
955 break;
956 case INHCAPS:
957 if (opts.caps_to_inherit)
958 errx(EXIT_FAILURE,
db663995 959 _("duplicate --inh-caps option"));
5600c405
AL
960 opts.caps_to_inherit = optarg;
961 break;
0c92194e
PS
962 case AMBCAPS:
963 if (opts.ambient_caps)
964 errx(EXIT_FAILURE,
965 _("duplicate --ambient-caps option"));
966 opts.ambient_caps = optarg;
967 break;
5600c405
AL
968 case CAPBSET:
969 if (opts.bounding_set)
970 errx(EXIT_FAILURE,
971 _("duplicate --bounding-set option"));
972 opts.bounding_set = optarg;
973 break;
974 case SECUREBITS:
975 if (opts.have_securebits)
976 errx(EXIT_FAILURE,
977 _("duplicate --securebits option"));
978 parse_securebits(&opts, optarg);
979 break;
980 case SELINUX_LABEL:
981 if (opts.selinux_label)
982 errx(EXIT_FAILURE,
983 _("duplicate --selinux-label option"));
984 opts.selinux_label = optarg;
985 break;
986 case APPARMOR_PROFILE:
987 if (opts.apparmor_profile)
988 errx(EXIT_FAILURE,
989 _("duplicate --apparmor-profile option"));
990 opts.apparmor_profile = optarg;
991 break;
ae015d71
TW
992 case LANDLOCK_ACCESS:
993 parse_landlock_access(&opts.landlock, optarg);
994 break;
995 case LANDLOCK_RULE:
996 parse_landlock_rule(&opts.landlock, optarg);
997 break;
acd8c7ab
TW
998 case SECCOMP_FILTER:
999 if (opts.seccomp_filter)
1000 errx(EXIT_FAILURE,
1001 _("duplicate --secccomp-filter option"));
1002 opts.seccomp_filter = optarg;
1003 break;
89f95425
KZ
1004 case RESET_ENV:
1005 opts.reset_env = 1;
1006 break;
2c308875 1007
5600c405 1008 case 'h':
86be6a32 1009 usage();
5600c405 1010 case 'V':
2c308875 1011 print_version(EXIT_SUCCESS);
5600c405 1012 default:
677ec86c 1013 errtryhelp(EXIT_FAILURE);
5600c405
AL
1014 }
1015 }
1016
1017 if (dumplevel) {
1018 if (total_opts != dumplevel || optind < argc)
1019 errx(EXIT_FAILURE,
1020 _("--dump is incompatible with all other options"));
1021 dump(dumplevel);
1022 return EXIT_SUCCESS;
1023 }
1024
1025 if (list_caps) {
1026 if (total_opts != 1 || optind < argc)
1027 errx(EXIT_FAILURE,
1028 _("--list-caps must be specified alone"));
1029 list_known_caps();
1030 return EXIT_SUCCESS;
1031 }
1032
1033 if (argc <= optind)
1034 errx(EXIT_FAILURE, _("No program specified"));
1035
1036 if ((opts.have_rgid || opts.have_egid)
94826d0d
SS
1037 && !opts.keep_groups && !opts.clear_groups && !opts.init_groups
1038 && !opts.have_groups)
1039 errx(EXIT_FAILURE,
1040 _("--[re]gid requires --keep-groups, --clear-groups, --init-groups, or --groups"));
1041
1042 if (opts.init_groups && !opts.have_ruid)
1043 errx(EXIT_FAILURE,
1044 _("--init-groups requires --ruid or --reuid"));
1045
1046 if (opts.init_groups && !opts.have_passwd)
5600c405 1047 errx(EXIT_FAILURE,
94826d0d
SS
1048 _("uid %ld not found, --init-groups requires an user that "
1049 "can be found on the system"),
1050 (long) opts.ruid);
5600c405 1051
89f95425
KZ
1052 if (opts.reset_env) {
1053 if (opts.have_passwd)
1054 /* pwd according to --ruid or --reuid */
1055 pw = &opts.passwd;
1056 else
1057 /* pwd for the current user */
1058 pw = getpwuid(getuid());
1059 do_reset_environ(pw);
1060 }
1061
74ce680a
SK
1062 if (opts.nnp && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
1063 err(EXIT_FAILURE, _("disallow granting new privileges failed"));
5600c405
AL
1064
1065 if (opts.selinux_label)
1066 do_selinux_label(opts.selinux_label);
1067 if (opts.apparmor_profile)
1068 do_apparmor_profile(opts.apparmor_profile);
acd8c7ab
TW
1069 if (opts.seccomp_filter)
1070 do_seccomp_filter(opts.seccomp_filter);
5600c405
AL
1071
1072 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
1073 err(EXIT_FAILURE, _("keep process capabilities failed"));
1074
1075 /* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if
1076 * possible. */
1077 bump_cap(CAP_SETPCAP);
1078 bump_cap(CAP_SETUID);
1079 bump_cap(CAP_SETGID);
1080 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
1081 err(SETPRIV_EXIT_PRIVERR, _("activate capabilities"));
1082
1083 if (opts.have_ruid || opts.have_euid) {
1084 do_setresuid(&opts);
1085 /* KEEPCAPS doesn't work for the effective mask. */
1086 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
1087 err(SETPRIV_EXIT_PRIVERR, _("reactivate capabilities"));
1088 }
1089
1090 if (opts.have_rgid || opts.have_egid)
1091 do_setresgid(&opts);
1092
1093 if (opts.have_groups) {
1094 if (setgroups(opts.num_groups, opts.groups) != 0)
1095 err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
94826d0d
SS
1096 } else if (opts.init_groups) {
1097 if (initgroups(opts.passwd.pw_name, opts.passwd.pw_gid) != 0)
1098 err(SETPRIV_EXIT_PRIVERR, _("initgroups failed"));
5600c405
AL
1099 } else if (opts.clear_groups) {
1100 gid_t x = 0;
1101 if (setgroups(0, &x) != 0)
1102 err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
1103 }
1104
74ce680a
SK
1105 if (opts.have_securebits && prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0)
1106 err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed"));
5600c405
AL
1107
1108 if (opts.bounding_set) {
9e5dd89d 1109 do_caps(CAP_TYPE_BOUNDING, opts.bounding_set);
5600c405
AL
1110 errno = EPERM; /* capng doesn't set errno if we're missing CAP_SETPCAP */
1111 if (capng_apply(CAPNG_SELECT_BOUNDS) != 0)
1112 err(SETPRIV_EXIT_PRIVERR, _("apply bounding set"));
1113 }
1114
1115 if (opts.caps_to_inherit) {
9e5dd89d 1116 do_caps(CAP_TYPE_INHERITABLE, opts.caps_to_inherit);
5600c405
AL
1117 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
1118 err(SETPRIV_EXIT_PRIVERR, _("apply capabilities"));
1119 }
1120
0c92194e
PS
1121 if (opts.ambient_caps) {
1122 do_caps(CAP_TYPE_AMBIENT, opts.ambient_caps);
1123 }
1124
23f54ce7
PS
1125 /* Clear or set parent death signal */
1126 if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0)
1127 err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed"));
1128
ae015d71
TW
1129 do_landlock(&opts.landlock);
1130
5600c405 1131 execvp(argv[optind], argv + optind);
fd777151 1132 errexec(argv[optind]);
5600c405 1133}