diff options
author | Karel Zak <kzak@redhat.com> | 2023-05-17 13:43:40 +0200 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2023-05-17 13:43:40 +0200 |
commit | a82df27f5d900c5e905fa2d6bb55751cfdff7c9a (patch) | |
tree | 9e00444dc3fdab1d3eecee67f828e2e93f1293bc | |
parent | d3edf0f8de6de4ad3ff7e1d62bd6b271a576538e (diff) | |
parent | 5b175fa9f03062a11618983199f38578b6e11217 (diff) | |
download | util-linux-a82df27f5d900c5e905fa2d6bb55751cfdff7c9a.tar.gz |
Merge branch 'nsenter-keep-caps' of https://github.com/dgibson/util-linux
* 'nsenter-keep-caps' of https://github.com/dgibson/util-linux:
Add --keep-caps option to nsenter, similar to the one in unshare
unshare: Move implementation of --keep-caps option to library function
-rw-r--r-- | include/caputils.h | 2 | ||||
-rw-r--r-- | lib/caputils.c | 38 | ||||
-rw-r--r-- | sys-utils/Makemodule.am | 3 | ||||
-rw-r--r-- | sys-utils/nsenter.c | 13 | ||||
-rw-r--r-- | sys-utils/unshare.c | 38 |
5 files changed, 56 insertions, 38 deletions
diff --git a/include/caputils.h b/include/caputils.h index 852903a6e..8fc214e7f 100644 --- a/include/caputils.h +++ b/include/caputils.h @@ -31,4 +31,6 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data); extern int cap_last_cap(void); +extern void cap_permitted_to_ambient(void); + #endif /* CAPUTILS_H */ diff --git a/lib/caputils.c b/lib/caputils.c index 987533a34..3041c3078 100644 --- a/lib/caputils.c +++ b/lib/caputils.c @@ -24,6 +24,7 @@ #include "caputils.h" #include "pathnames.h" #include "procfs.h" +#include "nls.h" static int test_cap(unsigned int cap) { @@ -87,6 +88,43 @@ int cap_last_cap(void) return cap; } +void cap_permitted_to_ambient(void) +{ + /* We use capabilities system calls to propagate the permitted + * capabilities into the ambient set because we may have + * already forked so be in async-signal-safe context. */ + struct __user_cap_header_struct header = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0, + }; + struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }}; + uint64_t effective, cap; + + if (capget(&header, payload) < 0) + err(EXIT_FAILURE, _("capget failed")); + + /* In order the make capabilities ambient, we first need to ensure + * that they are all inheritable. */ + payload[0].inheritable = payload[0].permitted; + payload[1].inheritable = payload[1].permitted; + + if (capset(&header, payload) < 0) + err(EXIT_FAILURE, _("capset failed")); + + effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective; + + for (cap = 0; cap < (sizeof(effective) * 8); cap++) { + /* This is the same check as cap_valid(), but using + * the runtime value for the last valid cap. */ + if (cap > (uint64_t) cap_last_cap()) + continue; + + if ((effective & (1 << cap)) + && prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0) + err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed")); + } +} + #ifdef TEST_PROGRAM_CAPUTILS int main(int argc, char *argv[]) { diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index a0bf6cb7b..2584281fb 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -524,7 +524,8 @@ if BUILD_NSENTER usrbin_exec_PROGRAMS += nsenter MANPAGES += sys-utils/nsenter.1 dist_noinst_DATA += sys-utils/nsenter.1.adoc -nsenter_SOURCES = sys-utils/nsenter.c lib/exec_shell.c +nsenter_SOURCES = sys-utils/nsenter.c lib/exec_shell.c \ + lib/caputils.c nsenter_LDADD = $(LDADD) libcommon.la $(SELINUX_LIBS) if HAVE_STATIC_NSENTER diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 56dbf1775..7a4619d31 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -45,6 +45,7 @@ #include "xalloc.h" #include "all-io.h" #include "env.h" +#include "caputils.h" static struct namespace_file { int nstype; @@ -95,6 +96,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -S, --setuid[=<uid>] set uid in entered namespace\n"), out); fputs(_(" -G, --setgid[=<gid>] set gid in entered namespace\n"), out); fputs(_(" --preserve-credentials do not touch uids or gids\n"), out); + fputs(_(" --keep-caps retain capabilities granted in user namespaces\n"), out); fputs(_(" -r, --root[=<dir>] set the root directory\n"), out); fputs(_(" -w, --wd[=<dir>] set the working directory\n"), out); fputs(_(" -W, --wdns <dir> set the working directory in namespace\n"), out); @@ -232,7 +234,8 @@ static void continue_as_child(void) int main(int argc, char *argv[]) { enum { - OPT_PRESERVE_CRED = CHAR_MAX + 1 + OPT_PRESERVE_CRED = CHAR_MAX + 1, + OPT_KEEPCAPS, }; static const struct option longopts[] = { { "all", no_argument, NULL, 'a' }, @@ -255,6 +258,7 @@ int main(int argc, char *argv[]) { "env", no_argument, NULL, 'e' }, { "no-fork", no_argument, NULL, 'F' }, { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, + { "keep-caps", no_argument, NULL, OPT_KEEPCAPS }, #ifdef HAVE_LIBSELINUX { "follow-context", no_argument, NULL, 'Z' }, #endif @@ -274,6 +278,7 @@ int main(int argc, char *argv[]) char *wdns = NULL; uid_t uid = 0; gid_t gid = 0; + int keepcaps = 0; struct ul_env_list *envls; #ifdef HAVE_LIBSELINUX bool selinux = 0; @@ -384,6 +389,9 @@ int main(int argc, char *argv[]) case OPT_PRESERVE_CRED: preserve_cred = 1; break; + case OPT_KEEPCAPS: + keepcaps = 1; + break; #ifdef HAVE_LIBSELINUX case 'Z': selinux = 1; @@ -569,6 +577,9 @@ int main(int argc, char *argv[]) err(EXIT_FAILURE, _("setuid failed")); } + if (keepcaps && (namespaces & CLONE_NEWUSER)) + cap_permitted_to_ambient(); + if (optind < argc) { execvp(argv[optind], argv + optind); errexec(argv[optind]); diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 2aa239eff..13aefa96c 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -1089,42 +1089,8 @@ int main(int argc, char *argv[]) if (force_uid && setuid(uid) < 0) /* change UID */ err(EXIT_FAILURE, _("setuid failed")); - /* We use capabilities system calls to propagate the permitted - * capabilities into the ambient set because we have already - * forked so are in async-signal-safe context. */ - if (keepcaps && (unshare_flags & CLONE_NEWUSER)) { - struct __user_cap_header_struct header = { - .version = _LINUX_CAPABILITY_VERSION_3, - .pid = 0, - }; - - struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }}; - uint64_t effective, cap; - - if (capget(&header, payload) < 0) - err(EXIT_FAILURE, _("capget failed")); - - /* In order the make capabilities ambient, we first need to ensure - * that they are all inheritable. */ - payload[0].inheritable = payload[0].permitted; - payload[1].inheritable = payload[1].permitted; - - if (capset(&header, payload) < 0) - err(EXIT_FAILURE, _("capset failed")); - - effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective; - - for (cap = 0; cap < (sizeof(effective) * 8); cap++) { - /* This is the same check as cap_valid(), but using - * the runtime value for the last valid cap. */ - if (cap > (uint64_t) cap_last_cap()) - continue; - - if ((effective & (1 << cap)) - && prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0) - err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed")); - } - } + if (keepcaps && (unshare_flags & CLONE_NEWUSER)) + cap_permitted_to_ambient(); if (optind < argc) { execvp(argv[optind], argv + optind); |