|
|
e377cc |
From 0b421290e05862e1abbb5a82654bd2de9829dd58 Mon Sep 17 00:00:00 2001
|
|
|
e377cc |
From: Patrick Steinhardt <ps@pks.im>
|
|
|
e377cc |
Date: Tue, 10 Apr 2018 12:08:21 +0100
|
|
|
e377cc |
Subject: [PATCH 69/74] setpriv: implement option to set parent death signal
|
|
|
e377cc |
|
|
|
e377cc |
When a process uses the syscall `prctl(PR_SET_PDEATHSIG, ...)`, it will
|
|
|
e377cc |
get notified with a process-defined signal as soon as its parent process
|
|
|
e377cc |
dies. This is for example being used by unshare(1)'s recently added
|
|
|
e377cc |
"--kill-child" option, causing the forked child to be killed as soon as
|
|
|
e377cc |
unshare itself dies.
|
|
|
e377cc |
|
|
|
e377cc |
Unfortunately, some LSMs will cause the parent death signal to be reset
|
|
|
e377cc |
when a process changes credentials, with the most important ones being
|
|
|
e377cc |
SELinux and AppArmor. The following command will thus not work as
|
|
|
e377cc |
expected:
|
|
|
e377cc |
|
|
|
e377cc |
unshare --fork --kill-child setpriv --reuid user <executable>
|
|
|
e377cc |
|
|
|
e377cc |
As soon as setpriv changes UID, the parent death signal is cleared and
|
|
|
e377cc |
the child will never get signalled when unshare gets killed.
|
|
|
e377cc |
|
|
|
e377cc |
Add a new option "--pdeathsig keep|clear|<signal>". Setting this flag
|
|
|
e377cc |
will cause us to either
|
|
|
e377cc |
|
|
|
e377cc |
- restore the previously active parent death signal as soon as the
|
|
|
e377cc |
setpriv has applied all credential changes
|
|
|
e377cc |
- clear the parent death signal
|
|
|
e377cc |
- set the parent death signal to "<signal>"
|
|
|
e377cc |
|
|
|
e377cc |
Furthermore, print out the currently set signal when dumping process
|
|
|
e377cc |
state.
|
|
|
e377cc |
|
|
|
e377cc |
[kzak@redhat.com: - small changes in codding style]
|
|
|
e377cc |
|
|
|
e377cc |
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1894192
|
|
|
e377cc |
Signed-off-by: Patrick Steinhardt <ps@pks.im>
|
|
|
e377cc |
Signed-off-by: Karel Zak <kzak@redhat.com>
|
|
|
e377cc |
---
|
|
|
e377cc |
sys-utils/setpriv.1 | 6 ++++++
|
|
|
e377cc |
sys-utils/setpriv.c | 49 +++++++++++++++++++++++++++++++++++++++++++++
|
|
|
e377cc |
2 files changed, 55 insertions(+)
|
|
|
e377cc |
|
|
|
e377cc |
diff --git a/sys-utils/setpriv.1 b/sys-utils/setpriv.1
|
|
|
e377cc |
index b900f6e08..f989bf33c 100644
|
|
|
e377cc |
--- a/sys-utils/setpriv.1
|
|
|
e377cc |
+++ b/sys-utils/setpriv.1
|
|
|
e377cc |
@@ -139,6 +139,12 @@ is cleared by
|
|
|
e377cc |
.BR execve (2)
|
|
|
e377cc |
and is therefore not allowed.
|
|
|
e377cc |
.TP
|
|
|
e377cc |
+.BR "\-\-pdeathsig keep" | clear | <signal>
|
|
|
e377cc |
+Keep, clear or set the parent death signal. Some LSMs, most notably SELinux and
|
|
|
e377cc |
+AppArmor, clear the signal when the process' credentials change. Using
|
|
|
e377cc |
+\fB--pdeathsig keep\fR will restore the parent death signal after changing
|
|
|
e377cc |
+credentials to remedy that situation.
|
|
|
e377cc |
+.TP
|
|
|
e377cc |
.BI \-\-selinux\-label " label"
|
|
|
e377cc |
Request a particular SELinux transition (using a transition on exec, not
|
|
|
e377cc |
dyntrans). This will fail and cause
|
|
|
e377cc |
diff --git a/sys-utils/setpriv.c b/sys-utils/setpriv.c
|
|
|
e377cc |
index 4147978cc..0d3a3b3c9 100644
|
|
|
e377cc |
--- a/sys-utils/setpriv.c
|
|
|
e377cc |
+++ b/sys-utils/setpriv.c
|
|
|
e377cc |
@@ -38,6 +38,7 @@
|
|
|
e377cc |
#include "strutils.h"
|
|
|
e377cc |
#include "xalloc.h"
|
|
|
e377cc |
#include "pathnames.h"
|
|
|
e377cc |
+#include "signames.h"
|
|
|
e377cc |
|
|
|
e377cc |
#ifndef PR_SET_NO_NEW_PRIVS
|
|
|
e377cc |
# define PR_SET_NO_NEW_PRIVS 38
|
|
|
e377cc |
@@ -102,6 +103,8 @@ struct privctx {
|
|
|
e377cc |
|
|
|
e377cc |
/* securebits */
|
|
|
e377cc |
int securebits;
|
|
|
e377cc |
+ /* parent death signal (<0 clear, 0 nothing, >0 signal) */
|
|
|
e377cc |
+ int pdeathsig;
|
|
|
e377cc |
|
|
|
e377cc |
/* LSMs */
|
|
|
e377cc |
const char *selinux_label;
|
|
|
e377cc |
@@ -135,6 +138,8 @@ static void __attribute__((__noreturn__)) usage(void)
|
|
|
e377cc |
fputs(_(" --init-groups initialize supplementary groups\n"), out);
|
|
|
e377cc |
fputs(_(" --groups <group,...> set supplementary groups\n"), out);
|
|
|
e377cc |
fputs(_(" --securebits <bits> set securebits\n"), out);
|
|
|
e377cc |
+ fputs(_(" --pdeathsig keep|clear|<signame>\n"
|
|
|
e377cc |
+ " set or clear parent death signal\n"), out);
|
|
|
e377cc |
fputs(_(" --selinux-label <label> set SELinux label\n"), out);
|
|
|
e377cc |
fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
|
|
|
e377cc |
|
|
|
e377cc |
@@ -329,6 +334,24 @@ static void dump_groups(void)
|
|
|
e377cc |
free(groups);
|
|
|
e377cc |
}
|
|
|
e377cc |
|
|
|
e377cc |
+static void dump_pdeathsig(void)
|
|
|
e377cc |
+{
|
|
|
e377cc |
+ int pdeathsig;
|
|
|
e377cc |
+
|
|
|
e377cc |
+ if (prctl(PR_GET_PDEATHSIG, &pdeathsig) != 0) {
|
|
|
e377cc |
+ warn(_("get pdeathsig failed"));
|
|
|
e377cc |
+ return;
|
|
|
e377cc |
+ }
|
|
|
e377cc |
+
|
|
|
e377cc |
+ printf("Parent death signal: ");
|
|
|
e377cc |
+ if (pdeathsig && signum_to_signame(pdeathsig) != NULL)
|
|
|
e377cc |
+ printf("%s\n", signum_to_signame(pdeathsig));
|
|
|
e377cc |
+ else if (pdeathsig)
|
|
|
e377cc |
+ printf("%d\n", pdeathsig);
|
|
|
e377cc |
+ else
|
|
|
e377cc |
+ printf("[none]\n");
|
|
|
e377cc |
+}
|
|
|
e377cc |
+
|
|
|
e377cc |
static void dump(int dumplevel)
|
|
|
e377cc |
{
|
|
|
e377cc |
int x;
|
|
|
e377cc |
@@ -392,6 +415,7 @@ static void dump(int dumplevel)
|
|
|
e377cc |
printf("\n");
|
|
|
e377cc |
|
|
|
e377cc |
dump_securebits();
|
|
|
e377cc |
+ dump_pdeathsig();
|
|
|
e377cc |
|
|
|
e377cc |
if (access(_PATH_SYS_SELINUX, F_OK) == 0)
|
|
|
e377cc |
dump_label(_("SELinux label"));
|
|
|
e377cc |
@@ -438,6 +462,19 @@ static void parse_groups(struct privctx *opts, const char *str)
|
|
|
e377cc |
free(groups);
|
|
|
e377cc |
}
|
|
|
e377cc |
|
|
|
e377cc |
+static void parse_pdeathsig(struct privctx *opts, const char *str)
|
|
|
e377cc |
+{
|
|
|
e377cc |
+ if (!strcmp(str, "keep")) {
|
|
|
e377cc |
+ if (prctl(PR_GET_PDEATHSIG, &opts->pdeathsig) != 0)
|
|
|
e377cc |
+ errx(SETPRIV_EXIT_PRIVERR,
|
|
|
e377cc |
+ _("failed to get parent death signal"));
|
|
|
e377cc |
+ } else if (!strcmp(str, "clear")) {
|
|
|
e377cc |
+ opts->pdeathsig = -1;
|
|
|
e377cc |
+ } else if ((opts->pdeathsig = signame_to_signum(str)) < 0) {
|
|
|
e377cc |
+ errx(EXIT_FAILURE, _("unknown signal: %s"), str);
|
|
|
e377cc |
+ }
|
|
|
e377cc |
+}
|
|
|
e377cc |
+
|
|
|
e377cc |
static void do_setresuid(const struct privctx *opts)
|
|
|
e377cc |
{
|
|
|
e377cc |
uid_t ruid, euid, suid;
|
|
|
e377cc |
@@ -711,6 +748,7 @@ int main(int argc, char **argv)
|
|
|
e377cc |
LISTCAPS,
|
|
|
e377cc |
CAPBSET,
|
|
|
e377cc |
SECUREBITS,
|
|
|
e377cc |
+ PDEATHSIG,
|
|
|
e377cc |
SELINUX_LABEL,
|
|
|
e377cc |
APPARMOR_PROFILE
|
|
|
e377cc |
};
|
|
|
e377cc |
@@ -734,6 +772,7 @@ int main(int argc, char **argv)
|
|
|
e377cc |
{ "groups", required_argument, NULL, GROUPS },
|
|
|
e377cc |
{ "bounding-set", required_argument, NULL, CAPBSET },
|
|
|
e377cc |
{ "securebits", required_argument, NULL, SECUREBITS },
|
|
|
e377cc |
+ { "pdeathsig", required_argument, NULL, PDEATHSIG, },
|
|
|
e377cc |
{ "selinux-label", required_argument, NULL, SELINUX_LABEL },
|
|
|
e377cc |
{ "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
|
|
|
e377cc |
{ "help", no_argument, NULL, 'h' },
|
|
|
e377cc |
@@ -844,6 +883,12 @@ int main(int argc, char **argv)
|
|
|
e377cc |
_("duplicate --groups option"));
|
|
|
e377cc |
parse_groups(&opts, optarg);
|
|
|
e377cc |
break;
|
|
|
e377cc |
+ case PDEATHSIG:
|
|
|
e377cc |
+ if (opts.pdeathsig)
|
|
|
e377cc |
+ errx(EXIT_FAILURE,
|
|
|
e377cc |
+ _("duplicate --keep-pdeathsig option"));
|
|
|
e377cc |
+ parse_pdeathsig(&opts, optarg);
|
|
|
e377cc |
+ break;
|
|
|
e377cc |
case LISTCAPS:
|
|
|
e377cc |
list_caps = 1;
|
|
|
e377cc |
break;
|
|
|
e377cc |
@@ -989,6 +1034,10 @@ int main(int argc, char **argv)
|
|
|
e377cc |
do_caps(CAP_TYPE_AMBIENT, opts.ambient_caps);
|
|
|
e377cc |
}
|
|
|
e377cc |
|
|
|
e377cc |
+ /* Clear or set parent death signal */
|
|
|
e377cc |
+ if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0)
|
|
|
e377cc |
+ err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed"));
|
|
|
e377cc |
+
|
|
|
e377cc |
execvp(argv[optind], argv + optind);
|
|
|
e377cc |
errexec(argv[optind]);
|
|
|
e377cc |
}
|
|
|
e377cc |
--
|
|
|
e377cc |
2.31.1
|
|
|
e377cc |
|