diff mbox series

[v3] selinux: map RTM_GETLINK to a privileged permission

Message ID 20200117090802.36613-1-jeffv@google.com (mailing list archive)
State Changes Requested
Headers show
Series [v3] selinux: map RTM_GETLINK to a privileged permission | expand

Commit Message

Jeffrey Vander Stoep Jan. 17, 2020, 9:08 a.m. UTC
Persistent device identifiers like MAC addresses are sensitive
because they are (usually) unique and can be used to
identify/track a device or user [1]. The MAC address is
accessible via the RTM_GETLINK request message type of a netlink
route socket[2] which returns the RTM_NEWLINK message.
Mapping RTM_GETLINK to a separate permission enables restricting
access to the MAC address without changing the behavior for
other RTM_GET* message types.

[1] https://adamdrake.com/mac-addresses-udids-and-privacy.html
[2] Other access vectors like ioctl(SIOCGIFHWADDR) are already covered
by existing LSM hooks.

v3:
-Add security_policycaps_init() to handle loading of policycaps,
selinux_nlmsg_init(), and future initializations resulting from
policy capabilities.

v2:
-Fix comment.
-Use ARRAY_SIZE instead of sizeof/sizeof.
-Call selinux_nlmsg_init() after both instances of
selinux_load_policycaps().
Signed-off-by: Jeff Vander Stoep <jeffv@google.com>
---
 security/selinux/include/classmap.h |  2 +-
 security/selinux/include/security.h |  9 +++++++++
 security/selinux/nlmsgtab.c         | 26 +++++++++++++++++++++++++-
 security/selinux/ss/services.c      | 13 ++++++++++---
 4 files changed, 45 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 986f3ac14282..77ccd558890a 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -116,7 +116,7 @@  struct security_class_mapping secclass_map[] = {
 	  { COMMON_IPC_PERMS, NULL } },
 	{ "netlink_route_socket",
 	  { COMMON_SOCK_PERMS,
-	    "nlmsg_read", "nlmsg_write", NULL } },
+	    "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", NULL } },
 	{ "netlink_tcpdiag_socket",
 	  { COMMON_SOCK_PERMS,
 	    "nlmsg_read", "nlmsg_write", NULL } },
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index a39f9565d80b..1671b418ddcb 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_NETLINK_ROUTE_GETLINK,
 	__POLICYDB_CAPABILITY_MAX
 };
 #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
@@ -209,6 +210,13 @@  static inline bool selinux_policycap_nnp_nosuid_transition(void)
 	return state->policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION];
 }
 
+static inline bool selinux_policycap_nlroute_getlink(void)
+{
+	struct selinux_state *state = &selinux_state;
+
+	return state->policycap[POLICYDB_CAPABILITY_NETLINK_ROUTE_GETLINK];
+}
+
 int security_mls_enabled(struct selinux_state *state);
 int security_load_policy(struct selinux_state *state,
 			 void *data, size_t len);
@@ -422,6 +430,7 @@  extern struct vfsmount *selinuxfs_mount;
 extern void selnl_notify_setenforce(int val);
 extern void selnl_notify_policyload(u32 seqno);
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern void selinux_nlmsg_init(void);
 
 extern void avtab_cache_init(void);
 extern void ebitmap_cache_init(void);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index c97fdae8f71b..3992f4894cf7 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -25,7 +25,7 @@  struct nlmsg_perm {
 	u32	perm;
 };
 
-static const struct nlmsg_perm nlmsg_route_perms[] =
+static struct nlmsg_perm nlmsg_route_perms[] =
 {
 	{ RTM_NEWLINK,		NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
 	{ RTM_DELLINK,		NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
@@ -208,3 +208,27 @@  int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 
 	return err;
 }
+
+static void nlmsg_set_getlink_perm(u32 perm)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nlmsg_route_perms); i++) {
+		if (nlmsg_route_perms[i].nlmsg_type == RTM_GETLINK) {
+			nlmsg_route_perms[i].perm = perm;
+			break;
+		}
+	}
+}
+
+/**
+ * Use nlmsg_readpriv as the permission for RTM_GETLINK messages if the
+ * netlink_route_getlink policy capability is set. Otherwise use nlmsg_read.
+ */
+void selinux_nlmsg_init(void)
+{
+	if (selinux_policycap_nlroute_getlink())
+		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV);
+	else
+		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READ);
+}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 0e8b94e8e156..091eeafd650d 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",
+	"netlink_route_getlink"
 };
 
 static struct selinux_ss selinux_ss;
@@ -2157,6 +2158,12 @@  static void security_load_policycaps(struct selinux_state *state)
 	}
 }
 
+static void security_policycaps_init(struct selinux_state *state)
+{
+	security_load_policycaps(state);
+	selinux_nlmsg_init();
+}
+
 static int security_preserve_bools(struct selinux_state *state,
 				   struct policydb *newpolicydb);
 
@@ -2222,7 +2229,7 @@  int security_load_policy(struct selinux_state *state, void *data, size_t len)
 		}
 
 		state->ss->sidtab = newsidtab;
-		security_load_policycaps(state);
+		security_policycaps_init(state);
 		selinux_mark_initialized(state);
 		seqno = ++state->ss->latest_granting;
 		selinux_complete_init();
@@ -2294,7 +2301,7 @@  int security_load_policy(struct selinux_state *state, void *data, size_t len)
 	write_lock_irq(&state->ss->policy_rwlock);
 	memcpy(policydb, newpolicydb, sizeof(*policydb));
 	state->ss->sidtab = newsidtab;
-	security_load_policycaps(state);
+	security_policycaps_init(state);
 	oldmapping = state->ss->map.mapping;
 	state->ss->map.mapping = newmap.mapping;
 	state->ss->map.size = newmap.size;