]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/setpriv.c
libmount: make mnt_context_is_fs_mounted work for /proc
[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
5600c405
AL
34#include "c.h"
35#include "closestream.h"
36#include "nls.h"
37#include "optutils.h"
38#include "strutils.h"
39#include "xalloc.h"
59c68b43 40#include "pathnames.h"
5600c405
AL
41
42#ifndef PR_SET_NO_NEW_PRIVS
43# define PR_SET_NO_NEW_PRIVS 38
44#endif
45#ifndef PR_GET_NO_NEW_PRIVS
46# define PR_GET_NO_NEW_PRIVS 39
47#endif
48
49#define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
50
51/*
52 * Note: We are subject to https://bugzilla.redhat.com/show_bug.cgi?id=895105
53 * and we will therefore have problems if new capabilities are added. Once
54 * that bug is fixed, I'll (Andy Lutomirski) submit a corresponding fix to
55 * setpriv. In the mean time, the code here tries to work reasonably well.
56 */
57
58struct privctx {
5600c405
AL
59 unsigned int
60 nnp:1, /* no_new_privs */
61 have_ruid:1, /* real uid */
62 have_euid:1, /* effective uid */
63 have_rgid:1, /* real gid */
64 have_egid:1, /* effective gid */
65 have_groups:1, /* add groups */
66 keep_groups:1, /* keep groups */
67 clear_groups:1, /* remove groups */
68 have_securebits:1; /* remove groups */
69
70 /* uids and gids */
71 uid_t ruid, euid;
72 gid_t rgid, egid;
73
74 /* supplementary groups */
75 size_t num_groups;
76 gid_t *groups;
77
78 /* caps */
79 const char *caps_to_inherit;
80 const char *bounding_set;
81
82 /* securebits */
83 int securebits;
84
85 /* LSMs */
86 const char *selinux_label;
87 const char *apparmor_profile;
88};
89
90static void __attribute__((__noreturn__)) usage(FILE *out)
91{
92 fputs(USAGE_HEADER, out);
298dc4ff
BS
93 fprintf(out, _(" %s [options] <program> [<argument>...]\n"),
94 program_invocation_short_name);
95
451dbcfa
BS
96 fputs(USAGE_SEPARATOR, out);
97 fputs(_("Run a program with different privilege settings.\n"), out);
98
5600c405
AL
99 fputs(USAGE_OPTIONS, out);
100 fputs(_(" -d, --dump show current state (and do not exec anything)\n"), out);
101 fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out);
102 fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out);
103 fputs(_(" --bounding-set <caps> set capability bounding set\n"), out);
104 fputs(_(" --ruid <uid> set real uid\n"), out);
105 fputs(_(" --euid <uid> set effective uid\n"), out);
106 fputs(_(" --rgid <gid> set real gid\n"), out);
107 fputs(_(" --egid <gid> set effective gid\n"), out);
108 fputs(_(" --reuid <uid> set real and effective uid\n"), out);
109 fputs(_(" --regid <gid> set real and effective gid\n"), out);
110 fputs(_(" --clear-groups clear supplementary groups\n"), out);
111 fputs(_(" --keep-groups keep supplementary groups\n"), out);
112 fputs(_(" --groups <group,...> set supplementary groups\n"), out);
113 fputs(_(" --securebits <bits> set securebits\n"), out);
de81a77d
KZ
114 fputs(_(" --selinux-label <label> set SELinux label\n"), out);
115 fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
298dc4ff 116
5600c405
AL
117 fputs(USAGE_SEPARATOR, out);
118 fputs(USAGE_HELP, out);
119 fputs(USAGE_VERSION, out);
120 fputs(USAGE_SEPARATOR, out);
121 fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out);
122 fprintf(out, USAGE_MAN_TAIL("setpriv(1)"));
123
124 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
125}
126
127static int real_cap_last_cap(void)
128{
129 /* CAP_LAST_CAP is untrustworthy. */
130 static int ret = -1;
131 int matched;
132 FILE *f;
133
134 if (ret != -1)
135 return ret;
136
59c68b43 137 f = fopen(_PATH_PROC_CAPLASTCAP, "r");
5600c405
AL
138 if (!f) {
139 ret = CAP_LAST_CAP; /* guess */
140 return ret;
141 }
142
143 matched = fscanf(f, "%d", &ret);
144 fclose(f);
145
146 if (matched != 1)
147 ret = CAP_LAST_CAP; /* guess */
148
149 return ret;
150}
151
152/* Returns the number of capabilities printed. */
153static int print_caps(FILE *f, capng_type_t which)
154{
155 int i, n = 0, max = real_cap_last_cap();
156
157 for (i = 0; i <= max; i++) {
158 if (capng_have_capability(which, i)) {
159 const char *name = capng_capability_to_name(i);
160 if (n)
161 fputc(',', f);
162 if (name)
163 fputs(name, f);
164 else
165 /* cap-ng has very poor handling of
166 * CAP_LAST_CAP changes. This is the
167 * best we can do. */
168 printf("cap_%d", i);
169 n++;
170 }
171 }
172 return n;
173}
174
175static void dump_one_secbit(int *first, int *bits, int bit, const char *name)
176{
177 if (*bits & bit) {
7d55b2df 178 if (*first)
5600c405 179 *first = 0;
7d55b2df
SK
180 else
181 printf(",");
5600c405
AL
182 fputs(name, stdout);
183 *bits &= ~bit;
184 }
185}
186
187static void dump_securebits(void)
188{
189 int first = 1;
190 int bits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
191
192 if (bits < 0) {
193 warnx(_("getting process secure bits failed"));
194 return;
195 }
196
197 printf(_("Securebits: "));
198
199 dump_one_secbit(&first, &bits, SECBIT_NOROOT, "noroot");
200 dump_one_secbit(&first, &bits, SECBIT_NOROOT_LOCKED, "noroot_locked");
201 dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP,
202 "no_setuid_fixup");
203 dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP_LOCKED,
204 "no_setuid_fixup_locked");
205 bits &= ~SECBIT_KEEP_CAPS;
206 dump_one_secbit(&first, &bits, SECBIT_KEEP_CAPS_LOCKED,
207 "keep_caps_locked");
208 if (bits) {
7d55b2df 209 if (first)
5600c405 210 first = 0;
7d55b2df
SK
211 else
212 printf(",");
5600c405
AL
213 printf("0x%x", (unsigned)bits);
214 }
215
216 if (first)
217 printf(_("[none]\n"));
218 else
219 printf("\n");
220}
221
222static void dump_label(const char *name)
223{
224 char buf[4097];
225 ssize_t len;
226 int fd, e;
227
59c68b43 228 fd = open(_PATH_PROC_ATTR_CURRENT, O_RDONLY);
5600c405 229 if (fd == -1) {
59c68b43 230 warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT);
5600c405
AL
231 return;
232 }
233
234 len = read(fd, buf, sizeof(buf));
235 e = errno;
236 close(fd);
237 if (len < 0) {
238 errno = e;
47481cbd 239 warn(_("cannot read %s"), name);
5600c405
AL
240 return;
241 }
242 if (sizeof(buf) - 1 <= (size_t)len) {
243 warnx(_("%s: too long"), name);
244 return;
245 }
246
247 buf[len] = 0;
248 if (0 < len && buf[len - 1] == '\n')
249 buf[len - 1] = 0;
250 printf("%s: %s\n", name, buf);
251}
252
253static void dump_groups(void)
254{
87918040 255 int n = getgroups(0, NULL);
5600c405 256 gid_t *groups;
59c68b43 257
5600c405
AL
258 if (n < 0) {
259 warn("getgroups failed");
260 return;
261 }
262
7370501f 263 groups = xmalloc(n * sizeof(gid_t));
5600c405
AL
264 n = getgroups(n, groups);
265 if (n < 0) {
7370501f 266 free(groups);
5600c405
AL
267 warn("getgroups failed");
268 return;
269 }
270
271 printf(_("Supplementary groups: "));
272 if (n == 0)
273 printf(_("[none]"));
274 else {
275 int i;
276 for (i = 0; i < n; i++) {
277 if (0 < i)
278 printf(",");
279 printf("%ld", (long)groups[i]);
280 }
281 }
282 printf("\n");
7370501f 283 free(groups);
5600c405
AL
284}
285
286static void dump(int dumplevel)
287{
288 int x;
289 uid_t ru, eu, su;
290 gid_t rg, eg, sg;
291
292 if (getresuid(&ru, &eu, &su) == 0) {
293 printf(_("uid: %u\n"), ru);
294 printf(_("euid: %u\n"), eu);
295 /* Saved and fs uids always equal euid. */
296 if (3 <= dumplevel)
297 printf(_("suid: %u\n"), su);
298 } else
299 warn(_("getresuid failed"));
300
301 if (getresgid(&rg, &eg, &sg) == 0) {
302 printf("gid: %ld\n", (long)rg);
303 printf("egid: %ld\n", (long)eg);
304 /* Saved and fs gids always equal egid. */
305 if (dumplevel >= 3)
306 printf("sgid: %ld\n", (long)sg);
307 } else
308 warn(_("getresgid failed"));
309
310 dump_groups();
311
312 x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
313 if (0 <= x)
314 printf("no_new_privs: %d\n", x);
315 else
316 warn("setting no_new_privs failed");
317
318 if (2 <= dumplevel) {
319 printf(_("Effective capabilities: "));
320 if (print_caps(stdout, CAPNG_EFFECTIVE) == 0)
321 printf(_("[none]"));
322 printf("\n");
323
324 printf(_("Permitted capabilities: "));
325 if (print_caps(stdout, CAPNG_PERMITTED) == 0)
326 printf(_("[none]"));
327 printf("\n");
328 }
329
330 printf(_("Inheritable capabilities: "));
331 if (print_caps(stdout, CAPNG_INHERITABLE) == 0)
332 printf(_("[none]"));
333 printf("\n");
334
335 printf(_("Capability bounding set: "));
336 if (print_caps(stdout, CAPNG_BOUNDING_SET) == 0)
337 printf(_("[none]"));
338 printf("\n");
339
340 dump_securebits();
341
59c68b43 342 if (access(_PATH_SYS_SELINUX, F_OK) == 0)
5600c405
AL
343 dump_label(_("SELinux label"));
344
59c68b43 345 if (access(_PATH_SYS_APPARMOR, F_OK) == 0) {
5600c405
AL
346 dump_label(_("AppArmor profile"));
347 }
348}
349
350static void list_known_caps(void)
351{
352 int i, max = real_cap_last_cap();
353
354 for (i = 0; i <= max; i++) {
355 const char *name = capng_capability_to_name(i);
356 if (name)
357 printf("%s\n", name);
358 else
359 warnx(_("cap %d: libcap-ng is broken"), i);
360 }
361}
362
363static void parse_groups(struct privctx *opts, const char *str)
364{
365 char *groups = xstrdup(str);
366 char *buf = groups; /* We'll reuse it */
367 char *c;
368 size_t i = 0;
369
370 opts->have_groups = 1;
371 opts->num_groups = 0;
372 while ((c = strsep(&groups, ",")))
373 opts->num_groups++;
374
375 /* Start again */
376 strcpy(buf, str); /* It's exactly the right length */
377 groups = buf;
378
379 opts->groups = xcalloc(opts->num_groups, sizeof(gid_t));
380 while ((c = strsep(&groups, ",")))
381 opts->groups[i++] = (gid_t) strtol_or_err(c,
59c68b43 382 _("Invalid supplementary group id"));
5600c405
AL
383
384 free(groups);
385}
386
387static void do_setresuid(const struct privctx *opts)
388{
389 uid_t ruid, euid, suid;
390 if (getresuid(&ruid, &euid, &suid) != 0)
391 err(SETPRIV_EXIT_PRIVERR, _("getresuid failed"));
392 if (opts->have_ruid)
393 ruid = opts->ruid;
394 if (opts->have_euid)
395 euid = opts->euid;
396
397 /* Also copy effective to saved (for paranoia). */
398 if (setresuid(ruid, euid, euid) != 0)
399 err(SETPRIV_EXIT_PRIVERR, _("setresuid failed"));
400}
401
402static void do_setresgid(const struct privctx *opts)
403{
404 gid_t rgid, egid, sgid;
405 if (getresgid(&rgid, &egid, &sgid) != 0)
406 err(SETPRIV_EXIT_PRIVERR, _("getresgid failed"));
407 if (opts->have_rgid)
408 rgid = opts->rgid;
409 if (opts->have_egid)
410 egid = opts->egid;
411
412 /* Also copy effective to saved (for paranoia). */
413 if (setresgid(rgid, egid, egid) != 0)
414 err(SETPRIV_EXIT_PRIVERR, _("setresgid failed"));
415}
416
417static void bump_cap(unsigned int cap)
418{
419 if (capng_have_capability(CAPNG_PERMITTED, cap))
420 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap);
421}
422
423static void do_caps(capng_type_t type, const char *caps)
424{
425 char *my_caps = xstrdup(caps);
426 char *c;
427
428 while ((c = strsep(&my_caps, ","))) {
429 capng_act_t action;
430 if (*c == '+')
431 action = CAPNG_ADD;
432 else if (*c == '-')
433 action = CAPNG_DROP;
434 else
435 errx(EXIT_FAILURE, _("bad capability string"));
436
437 if (!strcmp(c + 1, "all")) {
438 int i;
439 /* It would be really bad if -all didn't drop all
440 * caps. It's better to just fail. */
441 if (real_cap_last_cap() > CAP_LAST_CAP)
442 errx(SETPRIV_EXIT_PRIVERR,
443 _("libcap-ng is too old for \"all\" caps"));
444 for (i = 0; i <= CAP_LAST_CAP; i++)
445 capng_update(action, type, i);
446 } else {
447 int cap = capng_name_to_capability(c + 1);
448 if (0 <= cap)
449 capng_update(action, type, cap);
450 else
451 errx(EXIT_FAILURE,
452 _("unknown capability \"%s\""), c + 1);
453 }
454 }
455
456 free(my_caps);
457}
458
459static void parse_securebits(struct privctx *opts, const char *arg)
460{
461 char *buf = xstrdup(arg);
462 char *c;
463
464 opts->have_securebits = 1;
465 opts->securebits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
466 if (opts->securebits < 0)
467 err(SETPRIV_EXIT_PRIVERR, _("getting process secure bits failed"));
468
469 if (opts->securebits & ~(int)(SECBIT_NOROOT |
470 SECBIT_NOROOT_LOCKED |
471 SECBIT_NO_SETUID_FIXUP |
472 SECBIT_NO_SETUID_FIXUP_LOCKED |
473 SECBIT_KEEP_CAPS |
474 SECBIT_KEEP_CAPS_LOCKED))
475 errx(SETPRIV_EXIT_PRIVERR,
476 _("unrecognized securebit set -- refusing to adjust"));
477
478 while ((c = strsep(&buf, ","))) {
479 if (*c != '+' && *c != '-')
480 errx(EXIT_FAILURE, _("bad securebits string"));
481
482 if (!strcmp(c + 1, "all")) {
483 if (*c == '-')
484 opts->securebits = 0;
485 else
486 errx(EXIT_FAILURE,
487 _("+all securebits is not allowed"));
488 } else {
489 int bit;
490 if (!strcmp(c + 1, "noroot"))
491 bit = SECBIT_NOROOT;
492 else if (!strcmp(c + 1, "noroot_locked"))
493 bit = SECBIT_NOROOT_LOCKED;
494 else if (!strcmp(c + 1, "no_setuid_fixup"))
495 bit = SECBIT_NO_SETUID_FIXUP;
496 else if (!strcmp(c + 1, "no_setuid_fixup_locked"))
497 bit = SECBIT_NO_SETUID_FIXUP_LOCKED;
498 else if (!strcmp(c + 1, "keep_caps"))
499 errx(EXIT_FAILURE,
500 _("adjusting keep_caps does not make sense"));
501 else if (!strcmp(c + 1, "keep_caps_locked"))
502 bit = SECBIT_KEEP_CAPS_LOCKED; /* sigh */
503 else
504 errx(EXIT_FAILURE, _("unrecognized securebit"));
505
506 if (*c == '+')
507 opts->securebits |= bit;
508 else
509 opts->securebits &= ~bit;
510 }
511 }
512
513 opts->securebits |= SECBIT_KEEP_CAPS; /* We need it, and it's reset on exec */
514
515 free(buf);
516}
517
518static void do_selinux_label(const char *label)
519{
520 int fd;
521 size_t len;
522
59c68b43 523 if (access(_PATH_SYS_SELINUX, F_OK) != 0)
5600c405
AL
524 errx(SETPRIV_EXIT_PRIVERR, _("SELinux is not running"));
525
59c68b43 526 fd = open(_PATH_PROC_ATTR_EXEC, O_RDWR);
5600c405
AL
527 if (fd == -1)
528 err(SETPRIV_EXIT_PRIVERR,
59c68b43 529 _("cannot open %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
530
531 len = strlen(label);
532 errno = 0;
533 if (write(fd, label, len) != (ssize_t) len)
534 err(SETPRIV_EXIT_PRIVERR,
59c68b43 535 _("write failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405 536
cc89383b 537 if (close(fd) != 0)
05cef8ea 538 err(SETPRIV_EXIT_PRIVERR,
cc89383b 539 _("close failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
540}
541
542static void do_apparmor_profile(const char *label)
543{
544 FILE *f;
545
59c68b43 546 if (access(_PATH_SYS_APPARMOR, F_OK) != 0)
5600c405
AL
547 errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running"));
548
d359c62c 549 f = fopen(_PATH_PROC_ATTR_EXEC, "r+");
5600c405
AL
550 if (!f)
551 err(SETPRIV_EXIT_PRIVERR,
59c68b43 552 _("cannot open %s"), _PATH_PROC_ATTR_EXEC);
5600c405 553
d359c62c 554 fprintf(f, "exec %s", label);
f99b58b3
SK
555
556 if (close_stream(f) != 0)
5600c405 557 err(SETPRIV_EXIT_PRIVERR,
59c68b43 558 _("write failed: %s"), _PATH_PROC_ATTR_EXEC);
5600c405
AL
559}
560
637fa4c6
SK
561static uid_t get_user(const char *s, const char *err)
562{
563 struct passwd *pw;
564 long tmp;
565 pw = getpwnam(s);
566 if (pw)
567 return pw->pw_uid;
568 tmp = strtol_or_err(s, err);
569 return tmp;
570}
571
572static gid_t get_group(const char *s, const char *err)
573{
574 struct group *gr;
575 long tmp;
576 gr = getgrnam(s);
577 if (gr)
578 return gr->gr_gid;
579 tmp = strtol_or_err(s, err);
580 return tmp;
581}
582
5600c405
AL
583int main(int argc, char **argv)
584{
585 enum {
586 NNP = CHAR_MAX + 1,
587 RUID,
588 EUID,
589 RGID,
590 EGID,
591 REUID,
592 REGID,
593 CLEAR_GROUPS,
594 KEEP_GROUPS,
595 GROUPS,
596 INHCAPS,
597 LISTCAPS,
598 CAPBSET,
599 SECUREBITS,
600 SELINUX_LABEL,
601 APPARMOR_PROFILE
602 };
603
604 static const struct option longopts[] = {
87918040
SK
605 { "dump", no_argument, NULL, 'd' },
606 { "nnp", no_argument, NULL, NNP },
607 { "no-new-privs", no_argument, NULL, NNP },
608 { "inh-caps", required_argument, NULL, INHCAPS },
609 { "list-caps", no_argument, NULL, LISTCAPS },
610 { "ruid", required_argument, NULL, RUID },
611 { "euid", required_argument, NULL, EUID },
612 { "rgid", required_argument, NULL, RGID },
613 { "egid", required_argument, NULL, EGID },
614 { "reuid", required_argument, NULL, REUID },
615 { "regid", required_argument, NULL, REGID },
616 { "clear-groups", no_argument, NULL, CLEAR_GROUPS },
617 { "keep-groups", no_argument, NULL, KEEP_GROUPS },
618 { "groups", required_argument, NULL, GROUPS },
619 { "bounding-set", required_argument, NULL, CAPBSET },
620 { "securebits", required_argument, NULL, SECUREBITS },
621 { "selinux-label", required_argument, NULL, SELINUX_LABEL },
622 { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
623 { "help", no_argument, NULL, 'h' },
624 { "version", no_argument, NULL, 'V' },
625 { NULL, 0, NULL, 0 }
5600c405
AL
626 };
627
628 static const ul_excl_t excl[] = {
629 /* keep in same order with enum definitions */
630 {CLEAR_GROUPS, KEEP_GROUPS, GROUPS},
631 {0}
632 };
633 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
634
635 int c;
636 struct privctx opts;
637 int dumplevel = 0;
638 int total_opts = 0;
639 int list_caps = 0;
640
a7a5c470 641 setlocale(LC_ALL, "");
5600c405
AL
642 bindtextdomain(PACKAGE, LOCALEDIR);
643 textdomain(PACKAGE);
644 atexit(close_stdout);
645
646 memset(&opts, 0, sizeof(opts));
647
648 while ((c = getopt_long(argc, argv, "+dhV", longopts, NULL)) != -1) {
649 err_exclusive_options(c, longopts, excl, excl_st);
650 total_opts++;
651 switch (c) {
652 case 'd':
653 dumplevel++;
654 break;
655 case NNP:
656 if (opts.nnp)
657 errx(EXIT_FAILURE,
658 _("duplicate --no-new-privs option"));
659 opts.nnp = 1;
660 break;
661 case RUID:
662 if (opts.have_ruid)
663 errx(EXIT_FAILURE, _("duplicate ruid"));
664 opts.have_ruid = 1;
637fa4c6 665 opts.ruid = get_user(optarg, _("failed to parse ruid"));
5600c405
AL
666 break;
667 case EUID:
668 if (opts.have_euid)
669 errx(EXIT_FAILURE, _("duplicate euid"));
670 opts.have_euid = 1;
637fa4c6 671 opts.euid = get_user(optarg, _("failed to parse euid"));
5600c405
AL
672 break;
673 case REUID:
674 if (opts.have_ruid || opts.have_euid)
675 errx(EXIT_FAILURE, _("duplicate ruid or euid"));
676 opts.have_ruid = opts.have_euid = 1;
637fa4c6 677 opts.ruid = opts.euid = get_user(optarg, _("failed to parse reuid"));
5600c405
AL
678 break;
679 case RGID:
680 if (opts.have_rgid)
681 errx(EXIT_FAILURE, _("duplicate rgid"));
682 opts.have_rgid = 1;
637fa4c6 683 opts.rgid = get_group(optarg, _("failed to parse rgid"));
5600c405
AL
684 break;
685 case EGID:
686 if (opts.have_egid)
687 errx(EXIT_FAILURE, _("duplicate egid"));
688 opts.have_egid = 1;
637fa4c6 689 opts.egid = get_group(optarg, _("failed to parse egid"));
5600c405
AL
690 break;
691 case REGID:
692 if (opts.have_rgid || opts.have_egid)
693 errx(EXIT_FAILURE, _("duplicate rgid or egid"));
694 opts.have_rgid = opts.have_egid = 1;
637fa4c6 695 opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid"));
5600c405
AL
696 break;
697 case CLEAR_GROUPS:
698 if (opts.clear_groups)
699 errx(EXIT_FAILURE,
700 _("duplicate --clear-groups option"));
701 opts.clear_groups = 1;
702 break;
703 case KEEP_GROUPS:
704 if (opts.keep_groups)
705 errx(EXIT_FAILURE,
706 _("duplicate --keep-groups option"));
707 opts.keep_groups = 1;
708 break;
709 case GROUPS:
710 if (opts.have_groups)
711 errx(EXIT_FAILURE,
712 _("duplicate --groups option"));
713 parse_groups(&opts, optarg);
714 break;
715 case LISTCAPS:
716 list_caps = 1;
717 break;
718 case INHCAPS:
719 if (opts.caps_to_inherit)
720 errx(EXIT_FAILURE,
db663995 721 _("duplicate --inh-caps option"));
5600c405
AL
722 opts.caps_to_inherit = optarg;
723 break;
724 case CAPBSET:
725 if (opts.bounding_set)
726 errx(EXIT_FAILURE,
727 _("duplicate --bounding-set option"));
728 opts.bounding_set = optarg;
729 break;
730 case SECUREBITS:
731 if (opts.have_securebits)
732 errx(EXIT_FAILURE,
733 _("duplicate --securebits option"));
734 parse_securebits(&opts, optarg);
735 break;
736 case SELINUX_LABEL:
737 if (opts.selinux_label)
738 errx(EXIT_FAILURE,
739 _("duplicate --selinux-label option"));
740 opts.selinux_label = optarg;
741 break;
742 case APPARMOR_PROFILE:
743 if (opts.apparmor_profile)
744 errx(EXIT_FAILURE,
745 _("duplicate --apparmor-profile option"));
746 opts.apparmor_profile = optarg;
747 break;
748 case 'h':
749 usage(stdout);
750 case 'V':
751 printf(UTIL_LINUX_VERSION);
752 return EXIT_SUCCESS;
5600c405 753 default:
677ec86c 754 errtryhelp(EXIT_FAILURE);
5600c405
AL
755 }
756 }
757
758 if (dumplevel) {
759 if (total_opts != dumplevel || optind < argc)
760 errx(EXIT_FAILURE,
761 _("--dump is incompatible with all other options"));
762 dump(dumplevel);
763 return EXIT_SUCCESS;
764 }
765
766 if (list_caps) {
767 if (total_opts != 1 || optind < argc)
768 errx(EXIT_FAILURE,
769 _("--list-caps must be specified alone"));
770 list_known_caps();
771 return EXIT_SUCCESS;
772 }
773
774 if (argc <= optind)
775 errx(EXIT_FAILURE, _("No program specified"));
776
777 if ((opts.have_rgid || opts.have_egid)
778 && !opts.keep_groups && !opts.clear_groups && !opts.have_groups)
779 errx(EXIT_FAILURE,
780 _("--[re]gid requires --keep-groups, --clear-groups, or --groups"));
781
74ce680a
SK
782 if (opts.nnp && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
783 err(EXIT_FAILURE, _("disallow granting new privileges failed"));
5600c405
AL
784
785 if (opts.selinux_label)
786 do_selinux_label(opts.selinux_label);
787 if (opts.apparmor_profile)
788 do_apparmor_profile(opts.apparmor_profile);
789
790 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
791 err(EXIT_FAILURE, _("keep process capabilities failed"));
792
793 /* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if
794 * possible. */
795 bump_cap(CAP_SETPCAP);
796 bump_cap(CAP_SETUID);
797 bump_cap(CAP_SETGID);
798 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
799 err(SETPRIV_EXIT_PRIVERR, _("activate capabilities"));
800
801 if (opts.have_ruid || opts.have_euid) {
802 do_setresuid(&opts);
803 /* KEEPCAPS doesn't work for the effective mask. */
804 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
805 err(SETPRIV_EXIT_PRIVERR, _("reactivate capabilities"));
806 }
807
808 if (opts.have_rgid || opts.have_egid)
809 do_setresgid(&opts);
810
811 if (opts.have_groups) {
812 if (setgroups(opts.num_groups, opts.groups) != 0)
813 err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
814 } else if (opts.clear_groups) {
815 gid_t x = 0;
816 if (setgroups(0, &x) != 0)
817 err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
818 }
819
74ce680a
SK
820 if (opts.have_securebits && prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0)
821 err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed"));
5600c405
AL
822
823 if (opts.bounding_set) {
824 do_caps(CAPNG_BOUNDING_SET, opts.bounding_set);
825 errno = EPERM; /* capng doesn't set errno if we're missing CAP_SETPCAP */
826 if (capng_apply(CAPNG_SELECT_BOUNDS) != 0)
827 err(SETPRIV_EXIT_PRIVERR, _("apply bounding set"));
828 }
829
830 if (opts.caps_to_inherit) {
831 do_caps(CAPNG_INHERITABLE, opts.caps_to_inherit);
832 if (capng_apply(CAPNG_SELECT_CAPS) != 0)
833 err(SETPRIV_EXIT_PRIVERR, _("apply capabilities"));
834 }
835
836 execvp(argv[optind], argv + optind);
837
838 err(EXIT_FAILURE, _("cannot execute: %s"), argv[optind]);
839}