aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-12-26 21:42:15 -0800
committerAndrew G. Morgan <morgan@kernel.org>2020-12-26 21:42:15 -0800
commitf552b8f7403bf535832e703641e8fcee6adf6630 (patch)
tree81e72e28a0d178f63230a0be600bd4c7c67ebcbf
parent0f0c1fe489ec0ca69891a7999f5bda1c91e02f92 (diff)
downloadlibcap-f552b8f7403bf535832e703641e8fcee6adf6630.tar.gz
Augment NOPRIV libcap mode with the sticky NO_NEW_PRIVS prctl bit.
Since I last visited securebits no privs mode, a new prctl bit has been added (it isn't a securebit, but a parallel implementation of something similar). So, layer that bit on top of NOPRIV mode. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--cap/convenience.go4
-rw-r--r--go/try-launching.go10
-rw-r--r--libcap/cap_proc.c14
-rw-r--r--progs/capsh.c17
-rw-r--r--tests/libcap_launch_test.c3
5 files changed, 44 insertions, 4 deletions
diff --git a/cap/convenience.go b/cap/convenience.go
index f094e52..9580903 100644
--- a/cap/convenience.go
+++ b/cap/convenience.go
@@ -36,6 +36,7 @@ const (
prSetKeepCaps = 8
prGetSecureBits = 27
prSetSecureBits = 28
+ prSetNoNewPrivs = 38
)
// GetSecbits returns the current setting of the process' Secbits.
@@ -163,6 +164,9 @@ func (sc *syscaller) setMode(m Mode) error {
}
w.ClearFlag(Permitted)
+ // For good measure.
+ sc.prctlwcall6(prSetNoNewPrivs, 1, 0, 0, 0, 0)
+
return nil
}
diff --git a/go/try-launching.go b/go/try-launching.go
index 272fd0a..9f20e6b 100644
--- a/go/try-launching.go
+++ b/go/try-launching.go
@@ -28,6 +28,7 @@ func tryLaunching() {
iab string
uid int
gid int
+ mode cap.Mode
groups []int
}{
{args: []string{root + "/go/ok"}},
@@ -44,6 +45,11 @@ func tryLaunching() {
chroot: root + "/go",
fail: syscall.Getuid() != 0,
},
+ {
+ args: []string{root + "/progs/tcapsh-static", "--inmode=NOPRIV", "--has-no-new-privs"},
+ mode: cap.ModeNoPriv,
+ fail: syscall.Getuid() != 0,
+ },
}
ps := make([]int, len(vs))
@@ -61,6 +67,9 @@ func tryLaunching() {
if v.gid != 0 {
e.SetGroups(v.gid, v.groups)
}
+ if v.mode != 0 {
+ e.SetMode(v.mode)
+ }
if v.iab != "" {
if iab, err := cap.IABFromText(v.iab); err != nil {
log.Fatalf("failed to parse iab=%q: %v", v.iab, err)
@@ -68,6 +77,7 @@ func tryLaunching() {
e.SetIAB(iab)
}
}
+ log.Printf("[%d] trying: %q\n", i, v.args)
if ps[i], err = e.Launch(nil); err != nil {
if v.fail {
continue
diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c
index 3929f66..1329f94 100644
--- a/libcap/cap_proc.c
+++ b/libcap/cap_proc.c
@@ -390,7 +390,7 @@ static int _cap_set_secbits(struct syscaller_s *sc, unsigned bits)
}
/*
- * Set the security mode of the current process.
+ * Set the secbits of the current process.
*/
int cap_set_secbits(unsigned bits)
{
@@ -398,6 +398,14 @@ int cap_set_secbits(unsigned bits)
}
/*
+ * Attempt to raise the no new privs prctl value.
+ */
+static void _cap_set_no_new_privs(struct syscaller_s *sc)
+{
+ (void) _libcap_wprctl6(sc, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0);
+}
+
+/*
* Some predefined constants
*/
#define CAP_SECURED_BITS_BASIC \
@@ -448,7 +456,11 @@ static int _cap_set_mode(struct syscaller_s *sc, cap_mode_t flavor)
(void) _cap_drop_bound(sc, c);
}
(void) cap_clear_flag(working, CAP_PERMITTED);
+
+ /* for good measure */
+ _cap_set_no_new_privs(sc);
break;
+
default:
errno = EINVAL;
ret = -1;
diff --git a/progs/capsh.c b/progs/capsh.c
index dfe420f..a39ceeb 100644
--- a/progs/capsh.c
+++ b/progs/capsh.c
@@ -108,8 +108,9 @@ static void arg_print(void)
set = cap_get_secbits();
if (set >= 0) {
const char *b = binary(set); /* verilog convention for binary string */
- printf("Securebits: 0%lo/0x%lx/%u'b%s\n", set, set,
- (unsigned) strlen(b), b);
+ printf("Securebits: 0%lo/0x%lx/%u'b%s (no-new-privs=%d)\n", set, set,
+ (unsigned) strlen(b), b,
+ prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0));
printf(" secure-noroot: %s (%s)\n",
(set & SECBIT_NOROOT) ? "yes":"no",
(set & SECBIT_NOROOT_LOCKED) ? "locked":"unlocked");
@@ -910,6 +911,16 @@ int main(int argc, char *argv[], char *envp[])
exit(1);
}
cap_free(iab);
+ } else if (!strcmp("--no-new-privs", argv[i])) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0, 0) != 0) {
+ perror("unable to set no-new-privs");
+ exit(1);
+ }
+ } else if (!strcmp("--has-no-new-privs", argv[i])) {
+ if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0, 0) != 1) {
+ fprintf(stderr, "no-new-privs not set\n");
+ exit(1);
+ }
} else if (!strcmp("--license", argv[i])) {
printf(
"%s has a you choose license: BSD 3-clause or GPL2\n"
@@ -932,6 +943,7 @@ int main(int argc, char *argv[], char *envp[])
" --groups=g,... set the supplemental groups\n"
" --has-p=xxx exit 1 if capability xxx not permitted\n"
" --has-i=xxx exit 1 if capability xxx not inheritable\n"
+ " --has-no-new-privs exit 1 if privs not limited\n"
" --help, -h this message (or try 'man capsh')\n"
" --iab=... use cap_iab_from_text() to set iab\n"
" --inh=xxx set xxx,.. inheritable set\n"
@@ -943,6 +955,7 @@ int main(int argc, char *argv[], char *envp[])
" --license display license info\n"
" --modes list libcap named capability modes\n"
" --mode=<xxx> set capability mode to <xxx>\n"
+ " --no-new-privs set sticky process privilege limiter\n"
" --noamb reset (drop) all ambient capabilities\n"
" --print display capability relevant state\n"
" --secbits=<n> write a new value for securebits\n"
diff --git a/tests/libcap_launch_test.c b/tests/libcap_launch_test.c
index c9ef205..bba38c6 100644
--- a/tests/libcap_launch_test.c
+++ b/tests/libcap_launch_test.c
@@ -70,7 +70,8 @@ int main(int argc, char **argv) {
.iab = "!^cap_chown"
},
{
- .args = { "../progs/tcapsh-static", "--inmode=NOPRIV" },
+ .args = { "../progs/tcapsh-static", "--inmode=NOPRIV",
+ "--has-no-new-privs" },
.result = 0,
.mode = CAP_MODE_NOPRIV
},