[PATCH v3 0/6] setpriv: dumping and modification of capabilities

Patrick Steinhardt ps at pks.im
Thu Jul 6 13:21:42 UTC 2017


Hi,

this is version three of the patchset. I've rebased on top of the
already applied patches and tried to fix shortcomings pointed out
by Denys and Tito. I've now included output from `make
bloatcheck`, dropped static data which was not read-only and
fixed an issue with detecting the capability version provided by
the kernel, amongst other fixes.

The interdiff to v2 is attached below. Note that the diff also
includes differences for the already applied patches.

Regards
Patrick

Patrick Steinhardt (6):
  setpriv: dump no-new-privs info
  setpriv: dump inheritable capability set
  setpriv: dump capability bounding set
  setpriv: dump ambient capabilities
  setpriv: allow modifying inheritable caps
  setpriv: allow modifying ambient capabilities

 util-linux/setpriv.c | 363 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 352 insertions(+), 11 deletions(-)

-- 
2.13.2

diff --git a/util-linux/setpriv.c b/util-linux/setpriv.c
index 7b85af961..9ada33df4 100644
--- a/util-linux/setpriv.c
+++ b/util-linux/setpriv.c
@@ -21,28 +21,28 @@
 //config:	default y
 //config:	depends on SETPRIV
 //config:	help
-//config:		Enables the "--dump" switch to print out the current privilege
-//config:		state. This is helpful for diagnosing problems.
+//config:	  Enables the "--dump" switch to print out the current privilege
+//config:	  state. This is helpful for diagnosing problems.
 //config:
 //config:config FEATURE_SETPRIV_CAPABILITIES
 //config:	bool "Support capabilities"
 //config:	default y
 //config:	depends on SETPRIV
 //config:	help
-//config:		Capabilities can be used to grant processes additional rights
-//config:		without the necessity to always execute as the root user.
-//config:		Enabling this option enables "--dump" to show information on
-//config:		capabilities.
+//config:	  Capabilities can be used to grant processes additional rights
+//config:	  without the necessity to always execute as the root user.
+//config:	  Enabling this option enables "--dump" to show information on
+//config:	  capabilities.
 //config:
 //config:config FEATURE_SETPRIV_CAPABILITY_NAMES
 //config:	bool "Support capability names"
 //config:	default y
 //config:	depends on SETPRIV && FEATURE_SETPRIV_CAPABILITIES
 //config:	help
-//config:		Capabilities can be either referenced via a human-readble name,
-//config:		e.g. "net_admin", or using their index, e.g. "cap_12". Enabling
-//config:		this option allows using the human-readable names in addition to
-//config:		the index-based names.
+//config:	  Capabilities can be either referenced via a human-readble name,
+//config:	  e.g. "net_admin", or using their index, e.g. "cap_12". Enabling
+//config:	  this option allows using the human-readable names in addition to
+//config:	  the index-based names.
 
 //applet:IF_SETPRIV(APPLET(setpriv, BB_DIR_BIN, BB_SUID_DROP))
 
@@ -63,7 +63,7 @@
 //usage:     "\n--ambient-caps <caps,...>	Set ambient capabilities"
 //usage:	)
 
-//setpriv from util-linux e5c9736d214eeffd4a0dcaf1f78f64a6366bc8e7:
+//setpriv from util-linux 4fb515f90079739771b64dcb6beeb384e3be7cf5
 // -d, --dump               show current state (and do not exec anything)
 // --nnp, --no-new-privs    disallow granting new privileges
 // --ambient-caps <caps,...> set ambient capabilities
@@ -87,7 +87,6 @@
 #include <sys/capability.h>
 #endif
 #include <sys/prctl.h>
-#include <unistd.h>
 #include "libbb.h"
 
 #ifndef PR_CAPBSET_READ
@@ -109,11 +108,6 @@
 #define PR_CAP_AMBIENT_LOWER 3
 #endif
 
-#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-static cap_user_header_t cap_header;
-static int cap_u32s;
-#endif
-
 enum {
 	IF_FEATURE_SETPRIV_DUMP(OPTBIT_DUMP,)
 	IF_FEATURE_SETPRIV_CAPABILITIES(OPTBIT_INH,)
@@ -127,8 +121,11 @@ enum {
 };
 
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-static cap_user_header_t cap_header;
-static int cap_u32s;
+struct caps {
+	struct __user_cap_header_struct header;
+	cap_user_data_t data;
+	int u32s;
+};
 
 #if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
 static const char *capabilities[] = {
@@ -173,162 +170,164 @@ static const char *capabilities[] = {
 };
 #endif /* FEATURE_SETPRIV_CAPABILITY_NAMES */
 
-static void initcaps(void)
+#endif /* FEATURE_SETPRIV_CAPABILITIES */
+
+#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
+static void getcaps(struct caps *caps)
 {
-	cap_header = xmalloc(sizeof(*cap_header));
+	int versions[] = {
+		_LINUX_CAPABILITY_U32S_3,
+		_LINUX_CAPABILITY_U32S_2,
+		_LINUX_CAPABILITY_U32S_1,
+	};
+	int i;
 
-	cap_header->pid = 0;
-	cap_header->version = _LINUX_CAPABILITY_VERSION;
+	caps->header.pid = 0;
+	for (i = 0; i < ARRAY_SIZE(versions); i++) {
+		caps->header.version = versions[i];
+		if (capget(&caps->header, NULL) == 0)
+			break;
+	}
 
-	if ((capget(cap_header, NULL)) < 0)
-		bb_simple_perror_msg_and_die("capget");
+	if (i >= ARRAY_SIZE(versions))
+		bb_simple_perror_msg_and_die("");
 
-	switch (cap_header->version) {
+	switch (caps->header.version) {
 		case _LINUX_CAPABILITY_VERSION_1:
-			cap_u32s = _LINUX_CAPABILITY_U32S_1;
+			caps->u32s = _LINUX_CAPABILITY_U32S_1;
 			break;
 		case _LINUX_CAPABILITY_VERSION_2:
-			cap_u32s = _LINUX_CAPABILITY_U32S_2;
+			caps->u32s = _LINUX_CAPABILITY_U32S_2;
 			break;
 		case _LINUX_CAPABILITY_VERSION_3:
-			cap_u32s = _LINUX_CAPABILITY_U32S_3;
+			caps->u32s = _LINUX_CAPABILITY_U32S_3;
 			break;
 		default:
 			bb_error_msg_and_die("unsupported capability version");
 	}
-}
-#endif /* FEATURE_SETPRIV_CAPABILITIES */
 
-#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-static cap_user_data_t getcaps(void)
-{
-	cap_user_data_t result;
-
-	result = xmalloc(sizeof(*result) * cap_u32s);
-	if (capget(cap_header, result) < 0)
+	caps->data = xmalloc(sizeof(*caps->data) * caps->u32s);
+	if (capget(&caps->header, caps->data) < 0)
 		bb_simple_perror_msg_and_die("capget");
-
-	return result;
 }
 #endif
 
 #if ENABLE_FEATURE_SETPRIV_DUMP
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-static void printcap(unsigned long cap)
+static char *getcapname(char *buf, size_t nbuf, unsigned long cap)
 {
 #if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
 	if (cap < ARRAY_SIZE(capabilities))
-		printf(capabilities[cap]);
+		snprintf(buf, nbuf, capabilities[cap]);
 	else
 #endif
-		printf("cap_%lu", cap);
+		snprintf(buf, nbuf, "cap_%lu", cap);
+	return buf;
 }
 #endif /* FEATURE_SETPRIV_CAPABILITIES */
 
 static int dump(void)
 {
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-	cap_user_data_t caps;
+	struct caps caps;
+	char buf[32];
 	int n;
 #endif
+	const char *fmt;
 	uid_t ruid, euid, suid;
 	gid_t rgid, egid, sgid;
 	gid_t *gids;
 	int i, ngids, nnp;
 
-	if (getresuid(&ruid, &euid, &suid) < 0)
-		bb_simple_perror_msg_and_die("getresgid");
-
-	if (getresgid(&rgid, &egid, &sgid) < 0)
-		bb_simple_perror_msg_and_die("getresgid");
-
-	if ((ngids = getgroups(0, NULL)) < 0)
-		bb_simple_perror_msg_and_die("getgroups");
-	gids = xmalloc(ngids * sizeof(*gids));
-	if ((ngids = getgroups(ngids, gids)) < 0)
-		bb_simple_perror_msg_and_die("getgroups");
+	getresuid(&ruid, &euid, &suid); /* never fails in Linux */
+	getresgid(&rgid, &egid, &sgid); /* never fails in Linux */
+	ngids = 0;
+	gids = bb_getgroups(&ngids, NULL); /* never fails in Linux */
 
 	if ((nnp = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)) < 0)
 		bb_simple_perror_msg_and_die("prctl: GET_NO_NEW_PRIVS");
 
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-	caps = getcaps();
+	getcaps(&caps);
 #endif
 
-	printf("uid: %d\n", ruid);
-	printf("euid: %d\n", euid);
-	printf("gid: %d\n", rgid);
-	printf("egid: %d\n", egid);
+	printf("uid: %u\n", (unsigned)ruid);
+	printf("euid: %u\n", (unsigned)euid);
+	printf("gid: %u\n", (unsigned)rgid);
+	printf("egid: %u\n", (unsigned)egid);
 
 	printf("Supplementary groups: ");
 	if (ngids == 0) {
 		printf("[none]");
 	} else {
+		fmt = ",%u" + 1;
 		for (i = 0; i < ngids; i++) {
-			if (i)
-				putchar(',');
-			printf("%d", gids[i]);
+			printf(fmt, (unsigned)gids[i]);
+			fmt = ",%u";
 		}
 	}
-	putchar('\n');
-
-	printf("no_new_privs: %d\n", nnp);
+	printf("\nno_new_privs: %d\n", nnp);
 
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
 	printf("Inheritable capabilities: ");
+	fmt = ",%s" + 1;
 	for (n = 0, i = 0; cap_valid(i); i++) {
-		if (caps->inheritable & CAP_TO_MASK(i)) {
-			if (n)
-				putchar(',');
-			printcap(i);
+		if (CAP_TO_INDEX(i) >= caps.u32s) {
+			printf("\nindex: %u u32s: %u capability: %u\n", CAP_TO_INDEX(i), caps.u32s, i);
+			bb_error_msg_and_die("unsupported capability");
+		}
+		if (caps.data[CAP_TO_INDEX(i)].inheritable & CAP_TO_MASK(i)) {
+			printf(fmt, getcapname(buf, sizeof(buf), i));
 			n++;
+			fmt = ",%s";
 		}
 	}
 	if (!n)
 		printf("[none]");
-	putchar('\n');
 
-	printf("Ambient capabilities: ");
+	printf("\nAmbient capabilities: ");
+	fmt = ",%s" + 1;
 	for (n = 0, i = 0; cap_valid(i); i++) {
 		int ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, (unsigned long) i, 0UL, 0UL);
 		if (ret < 0)
 			bb_simple_perror_msg_and_die("prctl: CAP_AMBIENT_IS_SET");
 
 		if (ret) {
-			if (n)
-				putchar(',');
-			printcap(i);
+			printf(fmt, getcapname(buf, sizeof(buf), i));
 			n++;
+			fmt = ",%s";
 		}
 	}
 	if (!n && !i)
 		printf("[unsupported]");
 	else if (!n)
 		printf("[none]");
-	putchar('\n');
 
-	printf("Capability bounding set: ");
+	printf("\nCapability bounding set: ");
+	fmt = ",%s" + 1;
 	for (n = 0, i = 0; cap_valid(i); i++) {
 		int ret = prctl(PR_CAPBSET_READ, (unsigned long) i, 0UL, 0UL, 0UL);
 		if (ret < 0)
 			bb_simple_perror_msg_and_die("prctl: CAPBSET_READ");
 
 		if (ret) {
-			if (n)
-				putchar(',');
-			printcap(i);
+			printf(fmt, getcapname(buf, sizeof(buf), i));
 			n++;
+			fmt = ",%s";
 		}
 	}
 	if (!n)
 		printf("[none]");
-	putchar('\n');
-
-	free(caps);
+	bb_putchar('\n');
 #endif
 
-	free(gids);
-	return 0;
+	if (ENABLE_FEATURE_CLEAN_UP) {
+#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
+		free(caps.data);
+#endif
+		free(gids);
+	}
+	return EXIT_SUCCESS;
 }
 #endif /* FEATURE_SETPRIV_DUMP */
 
@@ -377,9 +376,9 @@ static void parse_cap(unsigned long *index, int *add, const char *cap)
 
 static void set_inh_caps(char *capstring)
 {
-	cap_user_data_t caps;
+	struct caps caps;
 
-	caps = getcaps();
+	getcaps(&caps);
 
 	capstring = strtok(capstring, ",");
 	while (capstring) {
@@ -388,21 +387,22 @@ static void set_inh_caps(char *capstring)
 
 		parse_cap(&cap, &add, capstring);
 
-		if (CAP_TO_INDEX(cap) >= cap_u32s)
+		if (CAP_TO_INDEX(cap) >= caps.u32s)
 			bb_error_msg_and_die("invalid capability cap");
 
 		if (add)
-			caps[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap);
+			caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap);
 		else
-			caps[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap);
+			caps.data[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap);
 
 		capstring = strtok(NULL, ",");
 	}
 
-	if ((capset(cap_header, caps)) < 0)
+	if ((capset(&caps.header, caps.data)) < 0)
 			bb_perror_msg_and_die("capset");
 
-	free(caps);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(caps.data);
 }
 
 static void set_ambient_caps(char *string)
@@ -433,42 +433,36 @@ int setpriv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int setpriv_main(int argc UNUSED_PARAM, char **argv)
 {
 	static const char setpriv_longopts[] ALIGN1 =
-		IF_FEATURE_SETPRIV_DUMP("dump\0"         No_argument	"d")
+		IF_FEATURE_SETPRIV_DUMP(
+		"dump\0"         No_argument		"d"
+		)
 		"nnp\0"          No_argument		"\xff"
 		"no-new-privs\0" No_argument		"\xff"
-		IF_FEATURE_SETPRIV_CAPABILITIES("inh-caps\0"     Required_argument	"\xfe")
-		IF_FEATURE_SETPRIV_CAPABILITIES("ambient-caps\0" Required_argument	"\xfd")
+		IF_FEATURE_SETPRIV_CAPABILITIES(
+		"inh-caps\0"     Required_argument	"\xfe"
+		)
+		IF_FEATURE_SETPRIV_CAPABILITIES(
+		"ambient-caps\0" Required_argument	"\xfd"
+		)
 		;
 #if ENABLE_FEATURE_SETPRIV_CAPABILITIES
 	char *inh_caps, *ambient_caps;
 #endif
 	int opts;
 
-	opt_complementary = "";
 	applet_long_options = setpriv_longopts;
-	opts = getopt32(argv, "+"
-			IF_FEATURE_SETPRIV_DUMP("d")
+	opts = getopt32(argv, "+"IF_FEATURE_SETPRIV_DUMP("d")
 			IF_FEATURE_SETPRIV_CAPABILITIES("\xfe:\xfd:" , &inh_caps, &ambient_caps));
 
-	argc -= optind;
 	argv += optind;
 
-#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
-	initcaps();
-#endif
-
 #if ENABLE_FEATURE_SETPRIV_DUMP
 	if (opts & OPT_DUMP) {
-		if ((opts & ~OPT_DUMP) || argc)
-			bb_error_msg_and_die("setpriv: --dump is incompatible with all other options");
-
+		if (argv[0] || (opts - OPT_DUMP) != 0)
+			bb_show_usage();
 		return dump();
 	}
 #endif
-
-	if (!argc)
-		bb_error_msg_and_die("no program specified");
-
 	if (opts & OPT_NNP) {
 		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
 			bb_simple_perror_msg_and_die("prctl: NO_NEW_PRIVS");
@@ -482,5 +476,7 @@ int setpriv_main(int argc UNUSED_PARAM, char **argv)
 		set_ambient_caps(ambient_caps);
 #endif
 
+	if (!argv[0])
+		bb_show_usage();
 	BB_EXECVP_or_die(argv);
 }


More information about the busybox mailing list