ANDROID: selinux: modify RTM_GETNEIGH{TBL}

Map the permission gating RTM_GETNEIGH/RTM_GETNEIGHTBL messages to a
new permission so that it can be distinguished from the other netlink
route permissions in selinux policy. The new permission is triggered by
a flag set in system images T and up.

This change is intended to be backported to all kernels that a T system
image can run on top of.

Bug: 171572148
Test: atest NetworkInterfaceTest
Test: atest CtsSelinuxTargetSdkCurrentTestCases
Test: atest bionic-unit-tests-static
Test: On Cuttlefish, run combinations of:
    - Policy bit set or omitted (see https://r.android.com/1701847)
    - This patch applied or omitted
    - App having nlmsg_readneigh permission or not
  Verify that only the combination of this patch + the policy bit being
  set + the app not having the nlmsg_readneigh permission prevents the
  app from sending RTM_GETNEIGH messages.

Change-Id: I4bcfce4decb34ea9388eeedfc4be67403de8a980
Signed-off-by: Bram Bonné <brambonne@google.com>
(cherry picked from commit fac07550bdac9adea0dbe3edbdbec7a9a690a178)
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index f3ad0a7..955e8c8 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -117,7 +117,8 @@
 	  { COMMON_IPC_PERMS, NULL } },
 	{ "netlink_route_socket",
 	  { COMMON_SOCK_PERMS,
-	    "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", NULL } },
+	    "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", "nlmsg_getneigh",
+	    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 0f7e1ae..a2c0cf6 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -98,6 +98,7 @@
 	bool initialized;
 	bool policycap[__POLICYDB_CAPABILITY_MAX];
 	bool android_netlink_route;
+	bool android_netlink_getneigh;
 
 	struct page *status_page;
 	struct mutex status_lock;
@@ -227,6 +228,13 @@
 	return state->android_netlink_route;
 }
 
+static inline bool selinux_android_nlroute_getneigh(void)
+{
+	struct selinux_state *state = &selinux_state;
+
+	return state->android_netlink_getneigh;
+}
+
 struct selinux_policy_convert_data;
 
 struct selinux_load_state {
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 7968c2e..9ad6f9b 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -212,12 +212,12 @@
 	return err;
 }
 
-static void nlmsg_set_getlink_perm(u32 perm)
+static void nlmsg_set_perm_for_type(u32 perm, u16 type)
 {
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(nlmsg_route_perms); i++) {
-		if (nlmsg_route_perms[i].nlmsg_type == RTM_GETLINK) {
+		if (nlmsg_route_perms[i].nlmsg_type == type) {
 			nlmsg_route_perms[i].perm = perm;
 			break;
 		}
@@ -227,11 +227,27 @@
 /**
  * Use nlmsg_readpriv as the permission for RTM_GETLINK messages if the
  * netlink_route_getlink policy capability is set. Otherwise use nlmsg_read.
+ * Similarly, use nlmsg_getneigh for RTM_GETNEIGH and RTM_GETNEIGHTBL if the
+ * netlink_route_getneigh policy capability is set. Otherwise use nlmsg_read.
  */
 void selinux_nlmsg_init(void)
 {
 	if (selinux_android_nlroute_getlink())
-		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV);
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV,
+					RTM_GETLINK);
 	else
-		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READ);
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+					RTM_GETLINK);
+
+	if (selinux_android_nlroute_getneigh()) {
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+					RTM_GETNEIGH);
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_GETNEIGH,
+					RTM_GETNEIGHTBL);
+	} else {
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+					RTM_GETNEIGH);
+		nlmsg_set_perm_for_type(NETLINK_ROUTE_SOCKET__NLMSG_READ,
+					RTM_GETNEIGHTBL);
+	}
 }
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ff5cc4b..572370c 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2497,6 +2497,10 @@
 		p->android_netlink_route = 1;
 	}
 
+	if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH)) {
+		p->android_netlink_getneigh = 1;
+	}
+
 	if (p->policyvers >= POLICYDB_VERSION_POLCAP) {
 		rc = ebitmap_read(&p->policycaps, fp);
 		if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 6bedec4..be9988e 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -239,6 +239,7 @@
 struct policydb {
 	int mls_enabled;
 	int android_netlink_route;
+	int android_netlink_getneigh;
 
 	/* symbol tables */
 	struct symtab symtab[SYM_NUM];
@@ -336,6 +337,7 @@
 
 #define POLICYDB_CONFIG_MLS    1
 #define POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE    (1 << 31)
+#define POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH (1 << 30)
 
 /* the config flags related to unknown classes/perms are bits 2 and 3 */
 #define REJECT_UNKNOWN	0x00000002
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 5d9b9d5..739c361 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2160,6 +2160,7 @@
 	}
 
 	state->android_netlink_route = p->android_netlink_route;
+	state->android_netlink_getneigh = p->android_netlink_getneigh;
 	selinux_nlmsg_init();
 }