2 * setpriv(1) - set various kernel privilege bits and run something
4 * Copyright (C) 2012 Andy Lutomirski <luto@amacapital.net>
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
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.
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.
25 #include <linux/securebits.h>
30 #include <sys/prctl.h>
31 #include <sys/types.h>
35 #include "closestream.h"
40 #include "pathnames.h"
42 #ifndef PR_SET_NO_NEW_PRIVS
43 # define PR_SET_NO_NEW_PRIVS 38
45 #ifndef PR_GET_NO_NEW_PRIVS
46 # define PR_GET_NO_NEW_PRIVS 39
49 #define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
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.
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 */
74 /* supplementary groups */
79 const char *caps_to_inherit
;
80 const char *bounding_set
;
86 const char *selinux_label
;
87 const char *apparmor_profile
;
90 static void __attribute__((__noreturn__
)) usage(FILE *out
)
92 fputs(USAGE_HEADER
, out
);
93 fprintf(out
, _(" %s [options] <program> [args...]\n"), program_invocation_short_name
);
94 fputs(USAGE_OPTIONS
, out
);
95 fputs(_(" -d, --dump show current state (and do not exec anything)\n"), out
);
96 fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out
);
97 fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out
);
98 fputs(_(" --bounding-set <caps> set capability bounding set\n"), out
);
99 fputs(_(" --ruid <uid> set real uid\n"), out
);
100 fputs(_(" --euid <uid> set effective uid\n"), out
);
101 fputs(_(" --rgid <gid> set real gid\n"), out
);
102 fputs(_(" --egid <gid> set effective gid\n"), out
);
103 fputs(_(" --reuid <uid> set real and effective uid\n"), out
);
104 fputs(_(" --regid <gid> set real and effective gid\n"), out
);
105 fputs(_(" --clear-groups clear supplementary groups\n"), out
);
106 fputs(_(" --keep-groups keep supplementary groups\n"), out
);
107 fputs(_(" --groups <group,...> set supplementary groups\n"), out
);
108 fputs(_(" --securebits <bits> set securebits\n"), out
);
109 fputs(_(" --selinux-label <label> set SELinux label\n"), out
);
110 fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out
);
111 fputs(USAGE_SEPARATOR
, out
);
112 fputs(USAGE_HELP
, out
);
113 fputs(USAGE_VERSION
, out
);
114 fputs(USAGE_SEPARATOR
, out
);
115 fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out
);
116 fprintf(out
, USAGE_MAN_TAIL("setpriv(1)"));
118 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
121 static int real_cap_last_cap(void)
123 /* CAP_LAST_CAP is untrustworthy. */
131 f
= fopen(_PATH_PROC_CAPLASTCAP
, "r");
133 ret
= CAP_LAST_CAP
; /* guess */
137 matched
= fscanf(f
, "%d", &ret
);
141 ret
= CAP_LAST_CAP
; /* guess */
146 /* Returns the number of capabilities printed. */
147 static int print_caps(FILE *f
, capng_type_t which
)
149 int i
, n
= 0, max
= real_cap_last_cap();
151 for (i
= 0; i
<= max
; i
++) {
152 if (capng_have_capability(which
, i
)) {
153 const char *name
= capng_capability_to_name(i
);
159 /* cap-ng has very poor handling of
160 * CAP_LAST_CAP changes. This is the
169 static void dump_one_secbit(int *first
, int *bits
, int bit
, const char *name
)
181 static void dump_securebits(void)
184 int bits
= prctl(PR_GET_SECUREBITS
, 0, 0, 0, 0);
187 warnx(_("getting process secure bits failed"));
191 printf(_("Securebits: "));
193 dump_one_secbit(&first
, &bits
, SECBIT_NOROOT
, "noroot");
194 dump_one_secbit(&first
, &bits
, SECBIT_NOROOT_LOCKED
, "noroot_locked");
195 dump_one_secbit(&first
, &bits
, SECBIT_NO_SETUID_FIXUP
,
197 dump_one_secbit(&first
, &bits
, SECBIT_NO_SETUID_FIXUP_LOCKED
,
198 "no_setuid_fixup_locked");
199 bits
&= ~SECBIT_KEEP_CAPS
;
200 dump_one_secbit(&first
, &bits
, SECBIT_KEEP_CAPS_LOCKED
,
207 printf("0x%x", (unsigned)bits
);
211 printf(_("[none]\n"));
216 static void dump_label(const char *name
)
222 fd
= open(_PATH_PROC_ATTR_CURRENT
, O_RDONLY
);
224 warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT
);
228 len
= read(fd
, buf
, sizeof(buf
));
233 warn(_("cannot read %s"), name
);
236 if (sizeof(buf
) - 1 <= (size_t)len
) {
237 warnx(_("%s: too long"), name
);
242 if (0 < len
&& buf
[len
- 1] == '\n')
244 printf("%s: %s\n", name
, buf
);
247 static void dump_groups(void)
249 int n
= getgroups(0, 0);
253 warn("getgroups failed");
257 groups
= xmalloc(n
* sizeof(gid_t
));
258 n
= getgroups(n
, groups
);
261 warn("getgroups failed");
265 printf(_("Supplementary groups: "));
270 for (i
= 0; i
< n
; i
++) {
273 printf("%ld", (long)groups
[i
]);
280 static void dump(int dumplevel
)
286 if (getresuid(&ru
, &eu
, &su
) == 0) {
287 printf(_("uid: %u\n"), ru
);
288 printf(_("euid: %u\n"), eu
);
289 /* Saved and fs uids always equal euid. */
291 printf(_("suid: %u\n"), su
);
293 warn(_("getresuid failed"));
295 if (getresgid(&rg
, &eg
, &sg
) == 0) {
296 printf("gid: %ld\n", (long)rg
);
297 printf("egid: %ld\n", (long)eg
);
298 /* Saved and fs gids always equal egid. */
300 printf("sgid: %ld\n", (long)sg
);
302 warn(_("getresgid failed"));
306 x
= prctl(PR_GET_NO_NEW_PRIVS
, 0, 0, 0, 0);
308 printf("no_new_privs: %d\n", x
);
310 warn("setting no_new_privs failed");
312 if (2 <= dumplevel
) {
313 printf(_("Effective capabilities: "));
314 if (print_caps(stdout
, CAPNG_EFFECTIVE
) == 0)
318 printf(_("Permitted capabilities: "));
319 if (print_caps(stdout
, CAPNG_PERMITTED
) == 0)
324 printf(_("Inheritable capabilities: "));
325 if (print_caps(stdout
, CAPNG_INHERITABLE
) == 0)
329 printf(_("Capability bounding set: "));
330 if (print_caps(stdout
, CAPNG_BOUNDING_SET
) == 0)
336 if (access(_PATH_SYS_SELINUX
, F_OK
) == 0)
337 dump_label(_("SELinux label"));
339 if (access(_PATH_SYS_APPARMOR
, F_OK
) == 0) {
340 dump_label(_("AppArmor profile"));
344 static void list_known_caps(void)
346 int i
, max
= real_cap_last_cap();
348 for (i
= 0; i
<= max
; i
++) {
349 const char *name
= capng_capability_to_name(i
);
351 printf("%s\n", name
);
353 warnx(_("cap %d: libcap-ng is broken"), i
);
357 static void parse_groups(struct privctx
*opts
, const char *str
)
359 char *groups
= xstrdup(str
);
360 char *buf
= groups
; /* We'll reuse it */
364 opts
->have_groups
= 1;
365 opts
->num_groups
= 0;
366 while ((c
= strsep(&groups
, ",")))
370 strcpy(buf
, str
); /* It's exactly the right length */
373 opts
->groups
= xcalloc(opts
->num_groups
, sizeof(gid_t
));
374 while ((c
= strsep(&groups
, ",")))
375 opts
->groups
[i
++] = (gid_t
) strtol_or_err(c
,
376 _("Invalid supplementary group id"));
381 static void do_setresuid(const struct privctx
*opts
)
383 uid_t ruid
, euid
, suid
;
384 if (getresuid(&ruid
, &euid
, &suid
) != 0)
385 err(SETPRIV_EXIT_PRIVERR
, _("getresuid failed"));
391 /* Also copy effective to saved (for paranoia). */
392 if (setresuid(ruid
, euid
, euid
) != 0)
393 err(SETPRIV_EXIT_PRIVERR
, _("setresuid failed"));
396 static void do_setresgid(const struct privctx
*opts
)
398 gid_t rgid
, egid
, sgid
;
399 if (getresgid(&rgid
, &egid
, &sgid
) != 0)
400 err(SETPRIV_EXIT_PRIVERR
, _("getresgid failed"));
406 /* Also copy effective to saved (for paranoia). */
407 if (setresgid(rgid
, egid
, egid
) != 0)
408 err(SETPRIV_EXIT_PRIVERR
, _("setresgid failed"));
411 static void bump_cap(unsigned int cap
)
413 if (capng_have_capability(CAPNG_PERMITTED
, cap
))
414 capng_update(CAPNG_ADD
, CAPNG_EFFECTIVE
, cap
);
417 static void do_caps(capng_type_t type
, const char *caps
)
419 char *my_caps
= xstrdup(caps
);
422 while ((c
= strsep(&my_caps
, ","))) {
429 errx(EXIT_FAILURE
, _("bad capability string"));
431 if (!strcmp(c
+ 1, "all")) {
433 /* It would be really bad if -all didn't drop all
434 * caps. It's better to just fail. */
435 if (real_cap_last_cap() > CAP_LAST_CAP
)
436 errx(SETPRIV_EXIT_PRIVERR
,
437 _("libcap-ng is too old for \"all\" caps"));
438 for (i
= 0; i
<= CAP_LAST_CAP
; i
++)
439 capng_update(action
, type
, i
);
441 int cap
= capng_name_to_capability(c
+ 1);
443 capng_update(action
, type
, cap
);
446 _("unknown capability \"%s\""), c
+ 1);
453 static void parse_securebits(struct privctx
*opts
, const char *arg
)
455 char *buf
= xstrdup(arg
);
458 opts
->have_securebits
= 1;
459 opts
->securebits
= prctl(PR_GET_SECUREBITS
, 0, 0, 0, 0);
460 if (opts
->securebits
< 0)
461 err(SETPRIV_EXIT_PRIVERR
, _("getting process secure bits failed"));
463 if (opts
->securebits
& ~(int)(SECBIT_NOROOT
|
464 SECBIT_NOROOT_LOCKED
|
465 SECBIT_NO_SETUID_FIXUP
|
466 SECBIT_NO_SETUID_FIXUP_LOCKED
|
468 SECBIT_KEEP_CAPS_LOCKED
))
469 errx(SETPRIV_EXIT_PRIVERR
,
470 _("unrecognized securebit set -- refusing to adjust"));
472 while ((c
= strsep(&buf
, ","))) {
473 if (*c
!= '+' && *c
!= '-')
474 errx(EXIT_FAILURE
, _("bad securebits string"));
476 if (!strcmp(c
+ 1, "all")) {
478 opts
->securebits
= 0;
481 _("+all securebits is not allowed"));
484 if (!strcmp(c
+ 1, "noroot"))
486 else if (!strcmp(c
+ 1, "noroot_locked"))
487 bit
= SECBIT_NOROOT_LOCKED
;
488 else if (!strcmp(c
+ 1, "no_setuid_fixup"))
489 bit
= SECBIT_NO_SETUID_FIXUP
;
490 else if (!strcmp(c
+ 1, "no_setuid_fixup_locked"))
491 bit
= SECBIT_NO_SETUID_FIXUP_LOCKED
;
492 else if (!strcmp(c
+ 1, "keep_caps"))
494 _("adjusting keep_caps does not make sense"));
495 else if (!strcmp(c
+ 1, "keep_caps_locked"))
496 bit
= SECBIT_KEEP_CAPS_LOCKED
; /* sigh */
498 errx(EXIT_FAILURE
, _("unrecognized securebit"));
501 opts
->securebits
|= bit
;
503 opts
->securebits
&= ~bit
;
507 opts
->securebits
|= SECBIT_KEEP_CAPS
; /* We need it, and it's reset on exec */
512 static void do_selinux_label(const char *label
)
517 if (access(_PATH_SYS_SELINUX
, F_OK
) != 0)
518 errx(SETPRIV_EXIT_PRIVERR
, _("SELinux is not running"));
520 fd
= open(_PATH_PROC_ATTR_EXEC
, O_RDWR
);
522 err(SETPRIV_EXIT_PRIVERR
,
523 _("cannot open %s"), _PATH_PROC_ATTR_EXEC
);
527 if (write(fd
, label
, len
) != (ssize_t
) len
)
528 err(SETPRIV_EXIT_PRIVERR
,
529 _("write failed: %s"), _PATH_PROC_ATTR_EXEC
);
532 err(SETPRIV_EXIT_PRIVERR
,
533 _("close failed: %s"), _PATH_PROC_ATTR_EXEC
);
536 static void do_apparmor_profile(const char *label
)
540 if (access(_PATH_SYS_APPARMOR
, F_OK
) != 0)
541 errx(SETPRIV_EXIT_PRIVERR
, _("AppArmor is not running"));
543 f
= fopen(_PATH_PROC_ATTR_EXEC
, "r+");
545 err(SETPRIV_EXIT_PRIVERR
,
546 _("cannot open %s"), _PATH_PROC_ATTR_EXEC
);
548 fprintf(f
, "exec %s", label
);
550 if (close_stream(f
) != 0)
551 err(SETPRIV_EXIT_PRIVERR
,
552 _("write failed: %s"), _PATH_PROC_ATTR_EXEC
);
555 static uid_t
get_user(const char *s
, const char *err
)
562 tmp
= strtol_or_err(s
, err
);
566 static gid_t
get_group(const char *s
, const char *err
)
573 tmp
= strtol_or_err(s
, err
);
577 int main(int argc
, char **argv
)
598 static const struct option longopts
[] = {
599 {"dump", no_argument
, 0, 'd'},
600 {"nnp", no_argument
, 0, NNP
},
601 {"no-new-privs", no_argument
, 0, NNP
},
602 {"inh-caps", required_argument
, 0, INHCAPS
},
603 {"list-caps", no_argument
, 0, LISTCAPS
},
604 {"ruid", required_argument
, 0, RUID
},
605 {"euid", required_argument
, 0, EUID
},
606 {"rgid", required_argument
, 0, RGID
},
607 {"egid", required_argument
, 0, EGID
},
608 {"reuid", required_argument
, 0, REUID
},
609 {"regid", required_argument
, 0, REGID
},
610 {"clear-groups", no_argument
, 0, CLEAR_GROUPS
},
611 {"keep-groups", no_argument
, 0, KEEP_GROUPS
},
612 {"groups", required_argument
, 0, GROUPS
},
613 {"bounding-set", required_argument
, 0, CAPBSET
},
614 {"securebits", required_argument
, 0, SECUREBITS
},
615 {"selinux-label", required_argument
, 0, SELINUX_LABEL
},
616 {"apparmor-profile", required_argument
, 0, APPARMOR_PROFILE
},
617 {"help", no_argument
, 0, 'h'},
618 {"version", no_argument
, 0, 'V'},
622 static const ul_excl_t excl
[] = {
623 /* keep in same order with enum definitions */
624 {CLEAR_GROUPS
, KEEP_GROUPS
, GROUPS
},
627 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
635 setlocale(LC_ALL
, "");
636 bindtextdomain(PACKAGE
, LOCALEDIR
);
638 atexit(close_stdout
);
640 memset(&opts
, 0, sizeof(opts
));
642 while ((c
= getopt_long(argc
, argv
, "+dhV", longopts
, NULL
)) != -1) {
643 err_exclusive_options(c
, longopts
, excl
, excl_st
);
652 _("duplicate --no-new-privs option"));
657 errx(EXIT_FAILURE
, _("duplicate ruid"));
659 opts
.ruid
= get_user(optarg
, _("failed to parse ruid"));
663 errx(EXIT_FAILURE
, _("duplicate euid"));
665 opts
.euid
= get_user(optarg
, _("failed to parse euid"));
668 if (opts
.have_ruid
|| opts
.have_euid
)
669 errx(EXIT_FAILURE
, _("duplicate ruid or euid"));
670 opts
.have_ruid
= opts
.have_euid
= 1;
671 opts
.ruid
= opts
.euid
= get_user(optarg
, _("failed to parse reuid"));
675 errx(EXIT_FAILURE
, _("duplicate rgid"));
677 opts
.rgid
= get_group(optarg
, _("failed to parse rgid"));
681 errx(EXIT_FAILURE
, _("duplicate egid"));
683 opts
.egid
= get_group(optarg
, _("failed to parse egid"));
686 if (opts
.have_rgid
|| opts
.have_egid
)
687 errx(EXIT_FAILURE
, _("duplicate rgid or egid"));
688 opts
.have_rgid
= opts
.have_egid
= 1;
689 opts
.rgid
= opts
.egid
= get_group(optarg
, _("failed to parse regid"));
692 if (opts
.clear_groups
)
694 _("duplicate --clear-groups option"));
695 opts
.clear_groups
= 1;
698 if (opts
.keep_groups
)
700 _("duplicate --keep-groups option"));
701 opts
.keep_groups
= 1;
704 if (opts
.have_groups
)
706 _("duplicate --groups option"));
707 parse_groups(&opts
, optarg
);
713 if (opts
.caps_to_inherit
)
715 _("duplicate --inh-caps option"));
716 opts
.caps_to_inherit
= optarg
;
719 if (opts
.bounding_set
)
721 _("duplicate --bounding-set option"));
722 opts
.bounding_set
= optarg
;
725 if (opts
.have_securebits
)
727 _("duplicate --securebits option"));
728 parse_securebits(&opts
, optarg
);
731 if (opts
.selinux_label
)
733 _("duplicate --selinux-label option"));
734 opts
.selinux_label
= optarg
;
736 case APPARMOR_PROFILE
:
737 if (opts
.apparmor_profile
)
739 _("duplicate --apparmor-profile option"));
740 opts
.apparmor_profile
= optarg
;
745 printf(UTIL_LINUX_VERSION
);
750 errx(EXIT_FAILURE
, _("unrecognized option '%c'"), c
);
755 if (total_opts
!= dumplevel
|| optind
< argc
)
757 _("--dump is incompatible with all other options"));
763 if (total_opts
!= 1 || optind
< argc
)
765 _("--list-caps must be specified alone"));
771 errx(EXIT_FAILURE
, _("No program specified"));
773 if ((opts
.have_rgid
|| opts
.have_egid
)
774 && !opts
.keep_groups
&& !opts
.clear_groups
&& !opts
.have_groups
)
776 _("--[re]gid requires --keep-groups, --clear-groups, or --groups"));
779 if (prctl(PR_SET_NO_NEW_PRIVS
, 1, 0, 0, 0) == -1)
780 err(EXIT_FAILURE
, _("disallow granting new privileges failed"));
782 if (opts
.selinux_label
)
783 do_selinux_label(opts
.selinux_label
);
784 if (opts
.apparmor_profile
)
785 do_apparmor_profile(opts
.apparmor_profile
);
787 if (prctl(PR_SET_KEEPCAPS
, 1, 0, 0, 0) == -1)
788 err(EXIT_FAILURE
, _("keep process capabilities failed"));
790 /* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if
792 bump_cap(CAP_SETPCAP
);
793 bump_cap(CAP_SETUID
);
794 bump_cap(CAP_SETGID
);
795 if (capng_apply(CAPNG_SELECT_CAPS
) != 0)
796 err(SETPRIV_EXIT_PRIVERR
, _("activate capabilities"));
798 if (opts
.have_ruid
|| opts
.have_euid
) {
800 /* KEEPCAPS doesn't work for the effective mask. */
801 if (capng_apply(CAPNG_SELECT_CAPS
) != 0)
802 err(SETPRIV_EXIT_PRIVERR
, _("reactivate capabilities"));
805 if (opts
.have_rgid
|| opts
.have_egid
)
808 if (opts
.have_groups
) {
809 if (setgroups(opts
.num_groups
, opts
.groups
) != 0)
810 err(SETPRIV_EXIT_PRIVERR
, _("setgroups failed"));
811 } else if (opts
.clear_groups
) {
813 if (setgroups(0, &x
) != 0)
814 err(SETPRIV_EXIT_PRIVERR
, _("setgroups failed"));
817 if (opts
.have_securebits
)
818 if (prctl(PR_SET_SECUREBITS
, opts
.securebits
, 0, 0, 0) != 0)
819 err(SETPRIV_EXIT_PRIVERR
, _("set process securebits failed"));
821 if (opts
.bounding_set
) {
822 do_caps(CAPNG_BOUNDING_SET
, opts
.bounding_set
);
823 errno
= EPERM
; /* capng doesn't set errno if we're missing CAP_SETPCAP */
824 if (capng_apply(CAPNG_SELECT_BOUNDS
) != 0)
825 err(SETPRIV_EXIT_PRIVERR
, _("apply bounding set"));
828 if (opts
.caps_to_inherit
) {
829 do_caps(CAPNG_INHERITABLE
, opts
.caps_to_inherit
);
830 if (capng_apply(CAPNG_SELECT_CAPS
) != 0)
831 err(SETPRIV_EXIT_PRIVERR
, _("apply capabilities"));
834 execvp(argv
[optind
], argv
+ optind
);
836 err(EXIT_FAILURE
, _("cannot execute: %s"), argv
[optind
]);