Skip to content

Commit d1d991e

Browse files
tweksteenpcmoore
authored andcommitted
selinux: Add netlink xperm support
Reuse the existing extended permissions infrastructure to support policies based on the netlink message types. A new policy capability "netlink_xperm" is introduced. When disabled, the previous behaviour is preserved. That is, netlink_send will rely on the permission mappings defined in nlmsgtab.c (e.g, nlmsg_read for RTM_GETADDR on NETLINK_ROUTE). When enabled, the mappings are ignored and the generic "nlmsg" permission is used instead. The new "nlmsg" permission is an extended permission. The 16 bits of the extended permission are mapped to the nlmsg_type field. Example policy on Android, preventing regular apps from accessing the device's MAC address and ARP table, but allowing this access to privileged apps, looks as follows: allow netdomain self:netlink_route_socket { create read getattr write setattr lock append connect getopt setopt shutdown nlmsg }; allowxperm netdomain self:netlink_route_socket nlmsg ~{ RTM_GETLINK RTM_GETNEIGH RTM_GETNEIGHTBL }; allowxperm priv_app self:netlink_route_socket nlmsg { RTM_GETLINK RTM_GETNEIGH RTM_GETNEIGHTBL }; The constants in the example above (e.g., RTM_GETLINK) are explicitly defined in the policy. It is possible to generate policies to support kernels that may or may not have the capability enabled by generating a rule for each scenario. For instance: allow domain self:netlink_audit_socket nlmsg_read; allow domain self:netlink_audit_socket nlmsg; allowxperm domain self:netlink_audit_socket nlmsg { AUDIT_GET }; The approach of defining a new permission ("nlmsg") instead of relying on the existing permissions (e.g., "nlmsg_read", "nlmsg_readpriv" or "nlmsg_tty_audit") has been preferred because: 1. This is similar to the other extended permission ("ioctl"); 2. With the new extended permission, the coarse-grained mapping is not necessary anymore. It could eventually be removed, which would be impossible if the extended permission was defined below these. 3. Having a single extra extended permission considerably simplifies the implementation here and in libselinux. Signed-off-by: Thiébaud Weksteen <[email protected]> Signed-off-by: Bram Bonné <[email protected]> [PM: manual merge fixes for sock_skip_has_perm()] Signed-off-by: Paul Moore <[email protected]>
1 parent 3b70b66 commit d1d991e

File tree

8 files changed

+126
-51
lines changed

8 files changed

+126
-51
lines changed

security/selinux/hooks.c

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4590,14 +4590,10 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
45904590
secclass, NULL, socksid);
45914591
}
45924592

4593-
static int sock_has_perm(struct sock *sk, u32 perms)
4593+
static bool sock_skip_has_perm(u32 sid)
45944594
{
4595-
struct sk_security_struct *sksec = selinux_sock(sk);
4596-
struct common_audit_data ad;
4597-
struct lsm_network_audit net;
4598-
4599-
if (sksec->sid == SECINITSID_KERNEL)
4600-
return 0;
4595+
if (sid == SECINITSID_KERNEL)
4596+
return true;
46014597

46024598
/*
46034599
* Before POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT, sockets that
@@ -4611,7 +4607,19 @@ static int sock_has_perm(struct sock *sk, u32 perms)
46114607
* setting.
46124608
*/
46134609
if (!selinux_policycap_userspace_initial_context() &&
4614-
sksec->sid == SECINITSID_INIT)
4610+
sid == SECINITSID_INIT)
4611+
return true;
4612+
return false;
4613+
}
4614+
4615+
4616+
static int sock_has_perm(struct sock *sk, u32 perms)
4617+
{
4618+
struct sk_security_struct *sksec = sk->sk_security;
4619+
struct common_audit_data ad;
4620+
struct lsm_network_audit net;
4621+
4622+
if (sock_skip_has_perm(sksec->sid))
46154623
return 0;
46164624

46174625
ad_net_init_from_sk(&ad, &net, sk);
@@ -5920,6 +5928,26 @@ static unsigned int selinux_ip_postroute(void *priv,
59205928
}
59215929
#endif /* CONFIG_NETFILTER */
59225930

5931+
static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_type)
5932+
{
5933+
struct sk_security_struct *sksec = sk->sk_security;
5934+
struct common_audit_data ad;
5935+
struct lsm_network_audit net;
5936+
u8 driver;
5937+
u8 xperm;
5938+
5939+
if (sock_skip_has_perm(sksec->sid))
5940+
return 0;
5941+
5942+
ad_net_init_from_sk(&ad, &net, sk);
5943+
5944+
driver = nlmsg_type >> 8;
5945+
xperm = nlmsg_type & 0xff;
5946+
5947+
return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass,
5948+
perms, driver, xperm, &ad);
5949+
}
5950+
59235951
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
59245952
{
59255953
int rc = 0;
@@ -5945,7 +5973,12 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
59455973

59465974
rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm);
59475975
if (rc == 0) {
5948-
rc = sock_has_perm(sk, perm);
5976+
if (selinux_policycap_netlink_xperm()) {
5977+
rc = nlmsg_sock_has_extended_perms(
5978+
sk, perm, nlh->nlmsg_type);
5979+
} else {
5980+
rc = sock_has_perm(sk, perm);
5981+
}
59495982
if (rc)
59505983
return rc;
59515984
} else if (rc == -EINVAL) {

security/selinux/include/classmap.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,17 @@ const struct security_class_mapping secclass_map[] = {
9797
{ "shm", { COMMON_IPC_PERMS, "lock", NULL } },
9898
{ "ipc", { COMMON_IPC_PERMS, NULL } },
9999
{ "netlink_route_socket",
100-
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
100+
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
101101
{ "netlink_tcpdiag_socket",
102-
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
102+
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
103103
{ "netlink_nflog_socket", { COMMON_SOCK_PERMS, NULL } },
104104
{ "netlink_xfrm_socket",
105-
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
105+
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
106106
{ "netlink_selinux_socket", { COMMON_SOCK_PERMS, NULL } },
107107
{ "netlink_iscsi_socket", { COMMON_SOCK_PERMS, NULL } },
108108
{ "netlink_audit_socket",
109109
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg_relay",
110-
"nlmsg_readpriv", "nlmsg_tty_audit", NULL } },
110+
"nlmsg_readpriv", "nlmsg_tty_audit", "nlmsg", NULL } },
111111
{ "netlink_fib_lookup_socket", { COMMON_SOCK_PERMS, NULL } },
112112
{ "netlink_connector_socket", { COMMON_SOCK_PERMS, NULL } },
113113
{ "netlink_netfilter_socket", { COMMON_SOCK_PERMS, NULL } },

security/selinux/include/policycap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ enum {
1414
POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS,
1515
POLICYDB_CAP_IOCTL_SKIP_CLOEXEC,
1616
POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT,
17+
POLICYDB_CAP_NETLINK_XPERM,
1718
__POLICYDB_CAP_MAX
1819
};
1920
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)

security/selinux/include/policycap_names.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
1717
"genfs_seclabel_symlinks",
1818
"ioctl_skip_cloexec",
1919
"userspace_initial_context",
20+
"netlink_xperm",
2021
};
2122
/* clang-format on */
2223

security/selinux/include/security.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ static inline bool selinux_policycap_userspace_initial_context(void)
195195
selinux_state.policycap[POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT]);
196196
}
197197

198+
static inline bool selinux_policycap_netlink_xperm(void)
199+
{
200+
return READ_ONCE(
201+
selinux_state.policycap[POLICYDB_CAP_NETLINK_XPERM]);
202+
}
203+
198204
struct selinux_policy_convert_data;
199205

200206
struct selinux_load_state {

security/selinux/nlmsgtab.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,33 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
170170
{
171171
int err = 0;
172172

173+
if (selinux_policycap_netlink_xperm()) {
174+
switch (sclass) {
175+
case SECCLASS_NETLINK_ROUTE_SOCKET:
176+
*perm = NETLINK_ROUTE_SOCKET__NLMSG;
177+
break;
178+
case SECCLASS_NETLINK_TCPDIAG_SOCKET:
179+
*perm = NETLINK_TCPDIAG_SOCKET__NLMSG;
180+
break;
181+
case SECCLASS_NETLINK_XFRM_SOCKET:
182+
*perm = NETLINK_XFRM_SOCKET__NLMSG;
183+
break;
184+
case SECCLASS_NETLINK_AUDIT_SOCKET:
185+
*perm = NETLINK_AUDIT_SOCKET__NLMSG;
186+
break;
187+
/* While it is possible to add a similar permission to other
188+
* netlink classes, note that the extended permission value is
189+
* matched against the nlmsg_type field. Notably,
190+
* SECCLASS_NETLINK_GENERIC_SOCKET uses dynamic values for this
191+
* field, which means that it cannot be added as-is.
192+
*/
193+
default:
194+
err = -ENOENT;
195+
break;
196+
}
197+
return err;
198+
}
199+
173200
switch (sclass) {
174201
case SECCLASS_NETLINK_ROUTE_SOCKET:
175202
/* RTM_MAX always points to RTM_SETxxxx, ie RTM_NEWxxx + 3.

security/selinux/ss/avtab.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ struct avtab_key {
5353
*/
5454
struct avtab_extended_perms {
5555
/* These are not flags. All 256 values may be used */
56-
#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
57-
#define AVTAB_XPERMS_IOCTLDRIVER 0x02
56+
#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
57+
#define AVTAB_XPERMS_IOCTLDRIVER 0x02
58+
#define AVTAB_XPERMS_NLMSG 0x03
5859
/* extension of the avtab_key specified */
5960
u8 specified; /* ioctl, netfilter, ... */
6061
/*

security/selinux/ss/services.c

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -582,23 +582,26 @@ static void type_attribute_bounds_av(struct policydb *policydb,
582582
}
583583

584584
/*
585-
* flag which drivers have permissions
586-
* only looking for ioctl based extended permissions
585+
* Flag which drivers have permissions.
587586
*/
588587
void services_compute_xperms_drivers(
589588
struct extended_perms *xperms,
590589
struct avtab_node *node)
591590
{
592591
unsigned int i;
593592

594-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
593+
switch (node->datum.u.xperms->specified) {
594+
case AVTAB_XPERMS_IOCTLDRIVER:
595595
/* if one or more driver has all permissions allowed */
596596
for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
597597
xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
598-
} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
598+
break;
599+
case AVTAB_XPERMS_IOCTLFUNCTION:
600+
case AVTAB_XPERMS_NLMSG:
599601
/* if allowing permissions within a driver */
600602
security_xperm_set(xperms->drivers.p,
601603
node->datum.u.xperms->driver);
604+
break;
602605
}
603606

604607
xperms->len = 1;
@@ -942,55 +945,58 @@ static void avd_init(struct selinux_policy *policy, struct av_decision *avd)
942945
avd->flags = 0;
943946
}
944947

945-
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
946-
struct avtab_node *node)
948+
static void update_xperms_extended_data(u8 specified,
949+
struct extended_perms_data *from,
950+
struct extended_perms_data *xp_data)
947951
{
948952
unsigned int i;
949953

950-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
954+
switch (specified) {
955+
case AVTAB_XPERMS_IOCTLDRIVER:
956+
memset(xp_data->p, 0xff, sizeof(xp_data->p));
957+
break;
958+
case AVTAB_XPERMS_IOCTLFUNCTION:
959+
case AVTAB_XPERMS_NLMSG:
960+
for (i = 0; i < ARRAY_SIZE(xp_data->p); i++)
961+
xp_data->p[i] |= from->p[i];
962+
break;
963+
}
964+
965+
}
966+
967+
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
968+
struct avtab_node *node)
969+
{
970+
switch (node->datum.u.xperms->specified) {
971+
case AVTAB_XPERMS_IOCTLFUNCTION:
972+
case AVTAB_XPERMS_NLMSG:
951973
if (xpermd->driver != node->datum.u.xperms->driver)
952974
return;
953-
} else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
975+
break;
976+
case AVTAB_XPERMS_IOCTLDRIVER:
954977
if (!security_xperm_test(node->datum.u.xperms->perms.p,
955978
xpermd->driver))
956979
return;
957-
} else {
980+
break;
981+
default:
958982
BUG();
959983
}
960984

961985
if (node->key.specified == AVTAB_XPERMS_ALLOWED) {
962986
xpermd->used |= XPERMS_ALLOWED;
963-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
964-
memset(xpermd->allowed->p, 0xff,
965-
sizeof(xpermd->allowed->p));
966-
}
967-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
968-
for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++)
969-
xpermd->allowed->p[i] |=
970-
node->datum.u.xperms->perms.p[i];
971-
}
987+
update_xperms_extended_data(node->datum.u.xperms->specified,
988+
&node->datum.u.xperms->perms,
989+
xpermd->allowed);
972990
} else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
973991
xpermd->used |= XPERMS_AUDITALLOW;
974-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
975-
memset(xpermd->auditallow->p, 0xff,
976-
sizeof(xpermd->auditallow->p));
977-
}
978-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
979-
for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++)
980-
xpermd->auditallow->p[i] |=
981-
node->datum.u.xperms->perms.p[i];
982-
}
992+
update_xperms_extended_data(node->datum.u.xperms->specified,
993+
&node->datum.u.xperms->perms,
994+
xpermd->auditallow);
983995
} else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
984996
xpermd->used |= XPERMS_DONTAUDIT;
985-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
986-
memset(xpermd->dontaudit->p, 0xff,
987-
sizeof(xpermd->dontaudit->p));
988-
}
989-
if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
990-
for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++)
991-
xpermd->dontaudit->p[i] |=
992-
node->datum.u.xperms->perms.p[i];
993-
}
997+
update_xperms_extended_data(node->datum.u.xperms->specified,
998+
&node->datum.u.xperms->perms,
999+
xpermd->dontaudit);
9941000
} else {
9951001
BUG();
9961002
}

0 commit comments

Comments
 (0)