diff options
author | Patrick Steinhardt | 2018-04-10 13:08:21 +0200 |
---|---|---|
committer | Karel Zak | 2018-04-10 13:52:21 +0200 |
commit | 23f54ce77773aa77f578084f2212b9173827fdc1 (patch) | |
tree | c2fc07ddba0da2098ac14d734dad5af72c3e08f5 /sys-utils/setpriv.c | |
parent | docs: update TODO (libblkid) (diff) | |
download | kernel-qcow2-util-linux-23f54ce77773aa77f578084f2212b9173827fdc1.tar.gz kernel-qcow2-util-linux-23f54ce77773aa77f578084f2212b9173827fdc1.tar.xz kernel-qcow2-util-linux-23f54ce77773aa77f578084f2212b9173827fdc1.zip |
setpriv: implement option to set parent death signal
When a process uses the syscall `prctl(PR_SET_PDEATHSIG, ...)`, it will
get notified with a process-defined signal as soon as its parent process
dies. This is for example being used by unshare(1)'s recently added
"--kill-child" option, causing the forked child to be killed as soon as
unshare itself dies.
Unfortunately, some LSMs will cause the parent death signal to be reset
when a process changes credentials, with the most important ones being
SELinux and AppArmor. The following command will thus not work as
expected:
unshare --fork --kill-child setpriv --reuid user <executable>
As soon as setpriv changes UID, the parent death signal is cleared and
the child will never get signalled when unshare gets killed.
Add a new option "--pdeathsig keep|clear|<signal>". Setting this flag
will cause us to either
- restore the previously active parent death signal as soon as the
setpriv has applied all credential changes
- clear the parent death signal
- set the parent death signal to "<signal>"
Furthermore, print out the currently set signal when dumping process
state.
[kzak@redhat.com: - small changes in codding style]
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/setpriv.c')
-rw-r--r-- | sys-utils/setpriv.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/sys-utils/setpriv.c b/sys-utils/setpriv.c index 4147978cc..0d3a3b3c9 100644 --- a/sys-utils/setpriv.c +++ b/sys-utils/setpriv.c @@ -38,6 +38,7 @@ #include "strutils.h" #include "xalloc.h" #include "pathnames.h" +#include "signames.h" #ifndef PR_SET_NO_NEW_PRIVS # define PR_SET_NO_NEW_PRIVS 38 @@ -102,6 +103,8 @@ struct privctx { /* securebits */ int securebits; + /* parent death signal (<0 clear, 0 nothing, >0 signal) */ + int pdeathsig; /* LSMs */ const char *selinux_label; @@ -135,6 +138,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --init-groups initialize supplementary groups\n"), out); fputs(_(" --groups <group,...> set supplementary groups\n"), out); fputs(_(" --securebits <bits> set securebits\n"), out); + fputs(_(" --pdeathsig keep|clear|<signame>\n" + " set or clear parent death signal\n"), out); fputs(_(" --selinux-label <label> set SELinux label\n"), out); fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out); @@ -329,6 +334,24 @@ static void dump_groups(void) free(groups); } +static void dump_pdeathsig(void) +{ + int pdeathsig; + + if (prctl(PR_GET_PDEATHSIG, &pdeathsig) != 0) { + warn(_("get pdeathsig failed")); + return; + } + + printf("Parent death signal: "); + if (pdeathsig && signum_to_signame(pdeathsig) != NULL) + printf("%s\n", signum_to_signame(pdeathsig)); + else if (pdeathsig) + printf("%d\n", pdeathsig); + else + printf("[none]\n"); +} + static void dump(int dumplevel) { int x; @@ -392,6 +415,7 @@ static void dump(int dumplevel) printf("\n"); dump_securebits(); + dump_pdeathsig(); if (access(_PATH_SYS_SELINUX, F_OK) == 0) dump_label(_("SELinux label")); @@ -438,6 +462,19 @@ static void parse_groups(struct privctx *opts, const char *str) free(groups); } +static void parse_pdeathsig(struct privctx *opts, const char *str) +{ + if (!strcmp(str, "keep")) { + if (prctl(PR_GET_PDEATHSIG, &opts->pdeathsig) != 0) + errx(SETPRIV_EXIT_PRIVERR, + _("failed to get parent death signal")); + } else if (!strcmp(str, "clear")) { + opts->pdeathsig = -1; + } else if ((opts->pdeathsig = signame_to_signum(str)) < 0) { + errx(EXIT_FAILURE, _("unknown signal: %s"), str); + } +} + static void do_setresuid(const struct privctx *opts) { uid_t ruid, euid, suid; @@ -711,6 +748,7 @@ int main(int argc, char **argv) LISTCAPS, CAPBSET, SECUREBITS, + PDEATHSIG, SELINUX_LABEL, APPARMOR_PROFILE }; @@ -734,6 +772,7 @@ int main(int argc, char **argv) { "groups", required_argument, NULL, GROUPS }, { "bounding-set", required_argument, NULL, CAPBSET }, { "securebits", required_argument, NULL, SECUREBITS }, + { "pdeathsig", required_argument, NULL, PDEATHSIG, }, { "selinux-label", required_argument, NULL, SELINUX_LABEL }, { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE }, { "help", no_argument, NULL, 'h' }, @@ -844,6 +883,12 @@ int main(int argc, char **argv) _("duplicate --groups option")); parse_groups(&opts, optarg); break; + case PDEATHSIG: + if (opts.pdeathsig) + errx(EXIT_FAILURE, + _("duplicate --keep-pdeathsig option")); + parse_pdeathsig(&opts, optarg); + break; case LISTCAPS: list_caps = 1; break; @@ -989,6 +1034,10 @@ int main(int argc, char **argv) do_caps(CAP_TYPE_AMBIENT, opts.ambient_caps); } + /* Clear or set parent death signal */ + if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0) + err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed")); + execvp(argv[optind], argv + optind); errexec(argv[optind]); } |