[RFC][PATCH] keyutils: Add key/keyring ACL support

David Howells dhowells at redhat.com
Wed Sep 27 16:10:04 UTC 2017


Here's a patch to make keyutils support ACLs on keys.

[root at andromeda ~]# LD_LIBRARY_PATH=~/keyutils ~/keyutils/keyctl setacl $k all:all u.4:r pos:0x37f root:I net:c
[root at andromeda ~]# LD_LIBRARY_PATH=~/keyutils ~/keyutils/keyctl getacl $k
IDENTITY  ID         PERMS
========= ========== ===================
all       -          0fffffff cjSRIlswrv
uid                4 00000002 --------r-
possessor -          0000037f cj-RIlswrv
root      -          00000020 ----I-----
net       -          00000200 c---------


David
---
diff --git a/keyctl.c b/keyctl.c
index 61990b4..c8b6f6f 100644
--- a/keyctl.c
+++ b/keyctl.c
@@ -71,6 +71,9 @@ static nr void act_keyctl_dh_compute(int argc, char *argv[]);
 static nr void act_keyctl_dh_compute_kdf(int argc, char *argv[]);
 static nr void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[]);
 static nr void act_keyctl_restrict_keyring(int argc, char *argv[]);
+static nr void act_keyctl_getacl(int argc, char *argv[]);
+static nr void act_keyctl_rgetacl(int argc, char *argv[]);
+static nr void act_keyctl_setacl(int argc, char *argv[]);
 
 const struct command commands[] = {
 	{ act_keyctl___version,	"--version",	"" },
@@ -82,9 +85,10 @@ const struct command commands[] = {
 	{ act_keyctl_dh_compute, "dh_compute",	"<private> <prime> <base>" },
 	{ act_keyctl_dh_compute_kdf, "dh_compute_kdf", "<private> <prime> <base> <len> <hash_name>" },
 	{ act_keyctl_dh_compute_kdf_oi, "dh_compute_kdf_oi", "<private> <prime> <base> <len> <hash_name>" },
+	{ act_keyctl_getacl,	"getacl",	"<key>" },
+	{ act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
 	{ act_keyctl_instantiate, "instantiate","<key> <data> <keyring>" },
 	{ act_keyctl_invalidate,"invalidate",	"<key>" },
-	{ act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
 	{ act_keyctl_link,	"link",		"<key> <keyring>" },
 	{ act_keyctl_list,	"list",		"<keyring>" },
 	{ act_keyctl_negate,	"negate",	"<key> <timeout> <keyring>" },
@@ -107,12 +111,14 @@ const struct command commands[] = {
 	{ act_keyctl_request2,	"request2",	"<type> <desc> <info> [<dest_keyring>]" },
 	{ act_keyctl_restrict_keyring, "restrict_keyring", "<keyring> [<type> [<restriction>]]" },
 	{ act_keyctl_revoke,	"revoke",	"<key>" },
+	{ act_keyctl_rgetacl,	"rgetacl",	"<key>" },
 	{ act_keyctl_rlist,	"rlist",	"<keyring>" },
 	{ act_keyctl_search,	"search",	"<keyring> <type> <desc> [<dest_keyring>]" },
 	{ act_keyctl_security,	"security",	"<key>" },
 	{ act_keyctl_session,	"session",	"" },
 	{ NULL,			"session",	"- [<prog> <arg1> <arg2> ...]" },
 	{ NULL,			"session",	"<name> [<prog> <arg1> <arg2> ...]" },
+	{ act_keyctl_setacl,	"setacl",	"<key> [<ace> <ace> ...]" },
 	{ act_keyctl_setperm,	"setperm",	"<key> <mask>" },
 	{ act_keyctl_show,	"show",		"[-x] [<keyring>]" },
 	{ act_keyctl_timeout,	"timeout",	"<key> <timeout>" },
@@ -1841,6 +1847,226 @@ static void act_keyctl_restrict_keyring(int argc, char *argv[])
 	exit(0);
 }
 
+static const char *special_ace_ids[] = {
+	[0]			= "?0",
+	[KEY_ACE_EVERYONE]	= "all",
+	[KEY_ACE_GROUP]		= "group",
+	[KEY_ACE_OWNER]		= "owner",
+	[KEY_ACE_POSSESSOR]	= "possessor",
+	[KEY_ACE_ROOT]		= "root",
+	[KEY_ACE_SYS_ADMIN]	= "sys",
+	[KEY_ACE_NET_ADMIN]	= "net",
+};
+
+/*
+ * Display ACL.
+ */
+static void display_acl(const struct key_ace *acl, int nr_ace)
+{
+	int i;
+
+	printf("IDENTITY  ID         PERMS\n");
+	printf("========= ========== ===================\n");
+
+	for (i = 0; i < nr_ace; i++) {
+		const struct key_ace *ace = &acl[i];
+
+		switch (ace->mask & KEY_ACE__IDENTITY) {
+		case KEY_ACE_SPECIAL:
+			if (ace->special_id <= KEY_ACE_NET_ADMIN)
+				printf("%-9.9s -         ", special_ace_ids[ace->special_id]);
+			else
+				printf("?special  %10u", ace->special_id);
+			break;
+		case KEY_ACE_UID:
+			printf("uid       %10u", ace->uid);
+			break;
+		case KEY_ACE_GID:
+			printf("gid       %10u", ace->gid);
+			break;
+		default:
+			printf("?%x        %10u",
+			       (ace->mask & KEY_ACE__IDENTITY) >> 28,
+			       ace->special_id);
+			break;
+		}
+
+		printf(" %08x ", ace->mask & KEY_ACE__PERMS);
+		printf("%c%c%c%c%c%c%c%c%c%c\n",
+		       ace->mask & KEY_ACE_CLEAR	? 'c' : '-',
+		       ace->mask & KEY_ACE_JOIN		? 'j' : '-',
+		       ace->mask & KEY_ACE_SET_SECURITY	? 'S' : '-',
+		       ace->mask & KEY_ACE_REVOKE	? 'R' : '-',
+		       ace->mask & KEY_ACE_INVAL	? 'I' : '-',
+		       ace->mask & KEY_ACE_LINK		? 'l' : '-',
+		       ace->mask & KEY_ACE_SEARCH	? 's' : '-',
+		       ace->mask & KEY_ACE_WRITE	? 'w' : '-',
+		       ace->mask & KEY_ACE_READ		? 'r' : '-',
+		       ace->mask & KEY_ACE_VIEW		? 'v' : '-');
+	}
+}
+
+/*
+ * Get a key's ACL.
+ */
+static void act_keyctl_getacl(int argc, char *argv[])
+{
+	struct key_ace *acl;
+	key_serial_t key;
+	int ret, nr_ace;
+
+	if (argc != 2)
+		format();
+
+	key = get_key_id(argv[1]);
+
+	ret = keyctl_get_acl_alloc(key, &acl);
+	if (ret < 0)
+		error("keyctl_get_acl_alloc");
+	nr_ace = ret / sizeof(*acl);
+
+	display_acl(acl, nr_ace);
+	exit(0);
+}
+
+/*
+ * Get a key's ACL in raw hex form.
+ */
+static void act_keyctl_rgetacl(int argc, char *argv[])
+{
+	struct key_ace *acl;
+	key_serial_t key;
+	int ret, nr_ace, i;
+
+	if (argc != 2)
+		format();
+
+	key = get_key_id(argv[1]);
+
+	ret = keyctl_get_acl_alloc(key, &acl);
+	if (ret < 0)
+		error("keyctl_get_acl_alloc");
+	nr_ace = ret / sizeof(*acl);
+
+	for (i = 0; i < nr_ace; i++) {
+		if (i != 0)
+			putchar(';');
+		printf("%x:%x", acl[i].mask, acl[i].special_id);
+	}
+
+	putchar('\n');
+	exit(0);
+}
+
+/*
+ * Set a key's ACL.
+ */
+static void act_keyctl_setacl(int argc, char *argv[])
+{
+	struct key_ace *acl = NULL, *ace;
+	key_serial_t key;
+	unsigned ltmp;
+	unsigned tmp, len;
+	char *id, *colon, *perm, *p;
+	int nr_ace, i;
+
+	if (argc < 2)
+		format();
+
+	key = get_key_id(argv[1]);
+	nr_ace = argc - 2;
+
+	if (nr_ace > 0) {
+		acl = alloca(sizeof(*acl) * nr_ace);
+		memset(acl, 0, sizeof(*acl) * nr_ace);
+
+		for (i = 0; i < nr_ace; i++) {
+			ace = &acl[i];
+			id = argv[i + 2];
+			colon = strchr(id, ':');
+			if (!colon || colon == id)
+				goto invalid_ace;
+
+			*colon = 0;
+			if (strcmp(id, "all") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_EVERYONE;
+			} else if (strcmp(id, "grp") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_GROUP;
+			} else if (strcmp(id, "own") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_OWNER;
+			} else if (strcmp(id, "pos") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_POSSESSOR;
+			} else if (strcmp(id, "root") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_ROOT;
+			} else if (strcmp(id, "sys") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_SYS_ADMIN;
+			} else if (strcmp(id, "net") == 0) {
+				ace->mask = KEY_ACE_SPECIAL;
+				ace->special_id = KEY_ACE_NET_ADMIN;
+			} else if (strncmp(id, "u.", 2) == 0) {
+				ace->mask = KEY_ACE_UID;
+				if (sscanf(id + 2, "%u%n", &tmp, &len) != 1)
+					goto invalid_ace_colon;
+				if (id + 2 + len != colon)
+					goto invalid_ace_colon;
+				ace->uid = tmp;
+			} else if (strncmp(id, "g.", 2) == 0) {
+				ace->mask = KEY_ACE_GID;
+				if (sscanf(id + 2, "%u%n", &tmp, &len) != 1)
+					goto invalid_ace_colon;
+				if (id + 2 + len != colon)
+					goto invalid_ace_colon;
+				ace->gid = tmp;
+			} else {
+				goto invalid_ace_colon;
+			}
+
+			perm = colon + 1;
+			if (isdigit(*perm)) {
+				ltmp = strtoul(perm, &p, 0);
+				if (ltmp & ~(unsigned long)KEY_ACE__PERMS)
+					goto invalid_ace_colon;
+				ace->mask |= ltmp;
+			} else if (strcmp(perm, "all") == 0) {
+				ace->mask |= KEY_ACE__PERMS;
+			} else {
+				for (; *perm; perm++) {
+					switch (*perm) {
+					case 'v': ace->mask |= KEY_ACE_VIEW;	break;
+					case 'r': ace->mask |= KEY_ACE_READ;	break;
+					case 'w': ace->mask |= KEY_ACE_WRITE;	break;
+					case 's': ace->mask |= KEY_ACE_SEARCH;	break;
+					case 'l': ace->mask |= KEY_ACE_LINK;	break;
+					case 'I': ace->mask |= KEY_ACE_INVAL;	break;
+					case 'R': ace->mask |= KEY_ACE_REVOKE;	break;
+					case 'S': ace->mask |= KEY_ACE_SET_SECURITY; break;
+					case 'j': ace->mask |= KEY_ACE_JOIN;	break;
+					case 'c': ace->mask |= KEY_ACE_CLEAR;	break;
+					default: goto invalid_ace_colon;
+					}
+				}
+			}
+		}
+	}
+
+	if (keyctl_set_acl(key, acl, nr_ace * sizeof(*acl)) < 0)
+		error("keyctl_set_acl");
+
+	exit(0);
+
+invalid_ace_colon:
+	*colon = ':';
+invalid_ace:
+	fprintf(stderr, "Invalid ACE: '%s'\n", id);
+	exit(2);
+}
+
 /*****************************************************************************/
 /*
  * parse a key identifier
diff --git a/keyutils.c b/keyutils.c
index d2bb34c..a37acbc 100644
--- a/keyutils.c
+++ b/keyutils.c
@@ -264,6 +264,16 @@ long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
 	return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
 }
 
+long keyctl_get_acl(key_serial_t id, struct key_ace *acl, size_t max_acl_size)
+{
+	return keyctl(KEYCTL_GET_ACL, id, acl, max_acl_size);
+}
+
+long keyctl_set_acl(key_serial_t id, const struct key_ace *acl, size_t acl_size)
+{
+	return keyctl(KEYCTL_SET_ACL, id, acl, acl_size);
+}
+
 /*****************************************************************************/
 /*
  * fetch key description into an allocated buffer
@@ -405,6 +415,41 @@ int keyctl_dh_compute_alloc(key_serial_t priv, key_serial_t prime,
 	return ret;
 }
 
+/*****************************************************************************/
+/*
+ * Fetch key ACL into an allocated buffer
+ * - Returns size of ACL in bytes.
+ */
+int keyctl_get_acl_alloc(key_serial_t id, struct key_ace **_acl)
+{
+	struct key_ace *acl;
+	long acl_size, ret;
+
+	ret = keyctl_get_acl(id, NULL, 0);
+	if (ret < 0)
+		return -1;
+
+	for (;;) {
+		acl_size = ret;
+		acl = malloc(acl_size);
+		if (!acl)
+			return -1;
+
+		ret = keyctl_get_acl(id, acl, acl_size);
+		if (ret < 0) {
+			free(acl);
+			return -1;
+		}
+
+		if (acl_size >= ret)
+			break;
+		free(acl);
+	}
+
+	*_acl = acl;
+	return ret;
+}
+
 /*
  * Depth-first recursively apply a function over a keyring tree
  */
diff --git a/keyutils.h b/keyutils.h
index 89c5b08..de1b0b4 100644
--- a/keyutils.h
+++ b/keyutils.h
@@ -101,6 +101,8 @@ typedef uint32_t key_perm_t;
 #define KEYCTL_GET_PERSISTENT		22	/* get a user's persistent keyring */
 #define KEYCTL_DH_COMPUTE		23	/* Compute Diffie-Hellman values */
 #define KEYCTL_RESTRICT_KEYRING		29	/* Restrict keys allowed to link to a keyring */
+#define KEYCTL_GET_ACL			30	/* Get a key's ACL */
+#define KEYCTL_SET_ACL			31	/* Set a key's ACL */
 
 /* keyctl structures */
 struct keyctl_dh_params {
@@ -117,6 +119,42 @@ struct keyctl_kdf_params {
 };
 
 /*
+ * Key ACL definitions.
+ */
+struct key_ace {
+	unsigned int		mask;
+#define KEY_ACE_VIEW		0x00000001 /* Can describe the key */
+#define KEY_ACE_READ		0x00000002 /* Can read the key content */
+#define KEY_ACE_WRITE		0x00000004 /* Can update/modify the key content */
+#define KEY_ACE_SEARCH		0x00000008 /* Can find the key by search */
+#define KEY_ACE_LINK		0x00000010 /* Can make a link to the key */
+#define KEY_ACE_INVAL		0x00000020 /* Can invalidate the key */
+#define KEY_ACE_REVOKE		0x00000040 /* Can revoke the key */
+#define KEY_ACE_SET_SECURITY	0x00000080 /* Can set owner, group, ACL */
+#define KEY_ACE_JOIN		0x00000100 /* Can join keyring */
+#define KEY_ACE_CLEAR		0x00000200 /* Can clear keyring */
+#define KEY_ACE__ORDINARY	0x0000001f /* Ordinary permissions */
+#define KEY_ACE__PERMS		0x0fffffff
+#define KEY_ACE_SPECIAL		0x10000000 /* A specific subject */
+#define KEY_ACE_UID		0x20000000 /* A nominated UID */
+#define KEY_ACE_GID		0x30000000 /* A nominated GID */
+#define KEY_ACE__IDENTITY	0xf0000000
+
+	union {
+		uid_t		uid;
+		gid_t		gid;
+		unsigned int	special_id;
+#define KEY_ACE_EVERYONE	1	/* Everyone, including owner and group */
+#define KEY_ACE_GROUP		2	/* The key's group */
+#define KEY_ACE_OWNER		3	/* The owner of the key */
+#define KEY_ACE_POSSESSOR	4	/* Any process that possesses of the key */
+#define KEY_ACE_ROOT		5	/* The user namespace root user */
+#define KEY_ACE_SYS_ADMIN	6	/* Anyone with CAP_SYS_ADMIN */
+#define KEY_ACE_NET_ADMIN	7	/* Anyone with CAP_NET_ADMIN */
+	};
+};
+
+/*
  * syscall wrappers
  */
 extern key_serial_t add_key(const char *type,
@@ -177,6 +215,8 @@ extern long keyctl_dh_compute_kdf(key_serial_t private, key_serial_t prime,
 				  char *buffer, size_t buflen);
 extern long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
 				    const char *restriction);
+extern long keyctl_get_acl(key_serial_t id, struct key_ace *acl, size_t max_acl_size);
+extern long keyctl_set_acl(key_serial_t id, const struct key_ace *acl, size_t max_acl_size);
 
 /*
  * utilities
@@ -186,6 +226,7 @@ extern int keyctl_read_alloc(key_serial_t id, void **_buffer);
 extern int keyctl_get_security_alloc(key_serial_t id, char **_buffer);
 extern int keyctl_dh_compute_alloc(key_serial_t priv, key_serial_t prime,
 				   key_serial_t base, void **_buffer);
+extern int keyctl_get_acl_alloc(key_serial_t id, struct key_ace **_acl);
 
 typedef int (*recursive_key_scanner_t)(key_serial_t parent, key_serial_t key,
 				       char *desc, int desc_len, void *data);
diff --git a/tests/keyctl/dh_compute/bad-args/runtest.sh b/tests/keyctl/dh_compute/bad-args/runtest.sh
index 7e8828b..f58a44c 100644
--- a/tests/keyctl/dh_compute/bad-args/runtest.sh
+++ b/tests/keyctl/dh_compute/bad-args/runtest.sh
@@ -72,7 +72,7 @@ expect_keyid logonid
 
 marker "CHECK WRONG KEY TYPE"
 dh_compute --fail $privateid $primeid $logonid
-expect_error ENOKEY
+expect_error EOPNOTSUPP
 dh_compute --fail $privateid $primeid @s
 expect_error EOPNOTSUPP
 
diff --git a/tests/keyctl/permitting/valid/runtest.sh b/tests/keyctl/permitting/valid/runtest.sh
index 70600e7..0b3d55f 100644
--- a/tests/keyctl/permitting/valid/runtest.sh
+++ b/tests/keyctl/permitting/valid/runtest.sh
@@ -72,12 +72,11 @@ set_key_perm $keyid 0x00201f00
 describe_key --fail $keyid
 expect_error EACCES
 
-# check that we can't use other perms instead of user perms to view the key
-# (our UID matches that of the key)
-marker "VIEW OTHER PERMISSIONS"
+# check that permissions for everyone allow the user to view the key,
+# even if our user perms don't.
+marker "VIEW EVERYONE PERMISSIONS"
 set_key_perm $keyid 0x0020001f
-describe_key --fail $keyid
-expect_error EACCES
+describe_key $keyid
 
 # check that taking away setattr permission renders the key immune to setperm
 marker "REMOVE SETATTR"
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the Linux-security-module-archive mailing list