From acb72212eb41ca862b8bc29b0106a46a83297fcb Mon Sep 17 00:00:00 2001
From: David Gibson <david@gibson.dropbear.id.au>
Date: Wed, 29 Mar 2023 13:36:15 +1100
Subject: [PATCH] unshare: Move implementation of --keep-caps option to library
 function

unshare.c open codes some logic to copy the permitted capability set to the
ambient set in order to implement the --keep-caps option.  Move this logic
to lib/caputils.c so that we can reuse it in nsenter.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 include/caputils.h  |  2 ++
 lib/caputils.c      | 38 ++++++++++++++++++++++++++++++++++++++
 sys-utils/unshare.c | 38 ++------------------------------------
 3 files changed, 42 insertions(+), 36 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/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);
-- 
2.33.0

