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