summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2023-05-17 13:43:40 +0200
committerKarel Zak <kzak@redhat.com>2023-05-17 13:43:40 +0200
commita82df27f5d900c5e905fa2d6bb55751cfdff7c9a (patch)
tree9e00444dc3fdab1d3eecee67f828e2e93f1293bc
parentd3edf0f8de6de4ad3ff7e1d62bd6b271a576538e (diff)
parent5b175fa9f03062a11618983199f38578b6e11217 (diff)
downloadutil-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.h2
-rw-r--r--lib/caputils.c38
-rw-r--r--sys-utils/Makemodule.am3
-rw-r--r--sys-utils/nsenter.c13
-rw-r--r--sys-utils/unshare.c38
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);