diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2020-12-26 21:42:15 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2020-12-26 21:42:15 -0800 |
commit | f552b8f7403bf535832e703641e8fcee6adf6630 (patch) | |
tree | 81e72e28a0d178f63230a0be600bd4c7c67ebcbf | |
parent | 0f0c1fe489ec0ca69891a7999f5bda1c91e02f92 (diff) | |
download | libcap-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.go | 4 | ||||
-rw-r--r-- | go/try-launching.go | 10 | ||||
-rw-r--r-- | libcap/cap_proc.c | 14 | ||||
-rw-r--r-- | progs/capsh.c | 17 | ||||
-rw-r--r-- | tests/libcap_launch_test.c | 3 |
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 }, |