security/selinux: Add support for new key permissions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a new 'key_perms' policy capability and support for the additional
key permissions: inval, revoke, join, clear

Also fixes JOIN -> LINK permission translation when policy
capability 'keys_perms' = 0;

The current "setattr" perm name remains and is used for KEY_NEED_SETSEC.
This gives the following permissions for the 'key' class:

create	Create a key or keyring.
view	View attributes.
read	Read contents.
write	Update or modify.
search	Search (keyring) or find (key).
link	Link a key into the keyring.
setattr	kernel < 5.X Change permissions on a keyring.
	kernel >= 5.X Set owner, group, ACL.
inval	Invalidate key.
revoke	Revoke key.
join	Join keyring as session.
clear	Clear a keyring.

Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---
 include/linux/key.h                 |  3 +-
 security/keys/keyctl.c              |  3 +-
 security/selinux/hooks.c            | 73 ++++++++++++++++++++++++++++++-------
 security/selinux/include/classmap.h |  2 +-
 security/selinux/include/security.h |  8 ++++
 security/selinux/ss/services.c      |  3 +-
 6 files changed, 74 insertions(+), 18 deletions(-)

diff --git a/include/linux/key.h b/include/linux/key.h
index 24c69457783f..ddfc0709569b 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -435,7 +435,8 @@ extern void key_free_user_ns(struct user_namespace *);
 #define	KEY_NEED_REVOKE	0x080	/* Require permission to revoke key */
 #define	KEY_NEED_JOIN	0x100	/* Require permission to join keyring as session */
 #define	KEY_NEED_CLEAR	0x200	/* Require permission to clear a keyring */
-#define KEY_NEED_ALL	0x3ff
+#define KEY_NEED_PARENT_JOIN 0x400 /* Require permission to impose keyring on parent */
+#define KEY_NEED_ALL	0x7ff
 
 #define OLD_KEY_NEED_SETATTR 0x20 /* Used to be Require permission to change attributes */
 
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index a0c97d4d8251..65e1c0c3feb1 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1592,7 +1592,8 @@ long keyctl_session_to_parent(void)
 	struct cred *cred;
 	int ret;
 
-	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_JOIN);
+	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0,
+				    KEY_NEED_PARENT_JOIN);
 	if (IS_ERR(keyring_r))
 		return PTR_ERR(keyring_r);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c8db5235b01f..a499bd7d9777 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6556,7 +6556,8 @@ static int selinux_key_permission(key_ref_t key_ref,
 {
 	struct key *key;
 	struct key_security_struct *ksec;
-	unsigned oldstyle_perm;
+	unsigned int key_perm = 0, switch_perm = 0;
+	int bit = 1, count = KEY_NEED_ALL;
 	u32 sid;
 
 	/* if no specific permissions are requested, we skip the
@@ -6565,18 +6566,62 @@ static int selinux_key_permission(key_ref_t key_ref,
 	if (perm == 0)
 		return 0;
 
-	oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | KEY_NEED_WRITE |
-				KEY_NEED_SEARCH | KEY_NEED_LINK);
-	if (perm & KEY_NEED_SETSEC)
-		oldstyle_perm |= OLD_KEY_NEED_SETATTR;
-	if (perm & KEY_NEED_INVAL)
-		oldstyle_perm |= KEY_NEED_SEARCH;
-	if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR))
-		oldstyle_perm |= KEY_NEED_WRITE;
-	if (perm & KEY_NEED_JOIN)
-		oldstyle_perm |= KEY_NEED_SEARCH;
-	if (perm & KEY_NEED_CLEAR)
-		oldstyle_perm |= KEY_NEED_WRITE;
+	if (selinux_policycap_key_perms()) {
+		while (count) {
+			switch_perm = bit & perm;
+			switch (switch_perm) {
+			case KEY_NEED_VIEW:
+				key_perm |= KEY__VIEW;
+				break;
+			case KEY_NEED_READ:
+				key_perm |= KEY__READ;
+				break;
+			case KEY_NEED_WRITE:
+				key_perm |= KEY__WRITE;
+				break;
+			case KEY_NEED_SEARCH:
+				key_perm |= KEY__SEARCH;
+				break;
+			case KEY_NEED_LINK:
+				key_perm |= KEY__LINK;
+				break;
+			case KEY_NEED_SETSEC:
+				key_perm |= KEY__SETATTR;
+				break;
+			case KEY_NEED_INVAL:
+				key_perm |= KEY__INVAL;
+				break;
+			case KEY_NEED_REVOKE:
+				key_perm |= KEY__REVOKE;
+				break;
+			case KEY_NEED_JOIN:
+			case KEY_NEED_PARENT_JOIN:
+				key_perm |= KEY__JOIN;
+				break;
+			case KEY_NEED_CLEAR:
+				key_perm |= KEY__CLEAR;
+				break;
+			}
+			bit <<= 1;
+			count >>= 1;
+		}
+	} else {
+		key_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ |
+				   KEY_NEED_WRITE | KEY_NEED_SEARCH |
+				   KEY_NEED_LINK);
+		if (perm & KEY_NEED_PARENT_JOIN)
+			key_perm |= KEY_NEED_LINK;
+		if (perm & KEY_NEED_SETSEC)
+			key_perm |= OLD_KEY_NEED_SETATTR;
+		if (perm & KEY_NEED_INVAL)
+			key_perm |= KEY_NEED_SEARCH;
+		if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR))
+			key_perm |= KEY_NEED_WRITE;
+		if (perm & KEY_NEED_JOIN)
+			key_perm |= KEY_NEED_SEARCH;
+		if (perm & KEY_NEED_CLEAR)
+			key_perm |= KEY_NEED_WRITE;
+	}
 
 	sid = cred_sid(cred);
 
@@ -6584,7 +6629,7 @@ static int selinux_key_permission(key_ref_t key_ref,
 	ksec = key->security;
 
 	return avc_has_perm(&selinux_state,
-			    sid, ksec->sid, SECCLASS_KEY, oldstyle_perm, NULL);
+			    sid, ksec->sid, SECCLASS_KEY, key_perm, NULL);
 }
 
 static int selinux_key_getsecurity(struct key *key, char **_buffer)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 7db24855e12d..038374bef526 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -159,7 +159,7 @@ struct security_class_mapping secclass_map[] = {
 	  { "send", "recv", "relabelto", "forward_in", "forward_out", NULL } },
 	{ "key",
 	  { "view", "read", "write", "search", "link", "setattr", "create",
-	    NULL } },
+	    "inval", "revoke", "join", "clear", NULL } },
 	{ "dccp_socket",
 	  { COMMON_SOCK_PERMS,
 	    "node_bind", "name_connect", NULL } },
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index ae840634e3c7..6b264b6d9d31 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -79,6 +79,7 @@ enum {
 	POLICYDB_CAPABILITY_ALWAYSNETWORK,
 	POLICYDB_CAPABILITY_CGROUPSECLABEL,
 	POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION,
+	POLICYDB_CAPABILITY_KEYPERMS,
 	__POLICYDB_CAPABILITY_MAX
 };
 #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
@@ -178,6 +179,13 @@ static inline bool selinux_policycap_nnp_nosuid_transition(void)
 	return state->policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION];
 }
 
+static inline bool selinux_policycap_key_perms(void)
+{
+	struct selinux_state *state = &selinux_state;
+
+	return state->policycap[POLICYDB_CAPABILITY_KEYPERMS];
+}
+
 int security_mls_enabled(struct selinux_state *state);
 int security_load_policy(struct selinux_state *state,
 			 void *data, size_t len);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index a5813c7629c1..b31dcd54cde9 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -73,7 +73,8 @@ const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
 	"extended_socket_class",
 	"always_check_network",
 	"cgroup_seclabel",
-	"nnp_nosuid_transition"
+	"nnp_nosuid_transition",
+	"key_perms"
 };
 
 static struct selinux_ss selinux_ss;
-- 
cgit 1.2-0.3.lf.el7



[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux