Skip to content

Commit fa1aa14

Browse files
jeffvanderstoeppcmoore
authored andcommitted
selinux: extended permissions for ioctls
Add extended permissions logic to selinux. Extended permissions provides additional permissions in 256 bit increments. Extend the generic ioctl permission check to use the extended permissions for per-command filtering. Source/target/class sets including the ioctl permission may additionally include a set of commands. Example: allowxperm <source> <target>:<class> ioctl unpriv_app_socket_cmds auditallowxperm <source> <target>:<class> ioctl priv_gpu_cmds Where unpriv_app_socket_cmds and priv_gpu_cmds are macros representing commonly granted sets of ioctl commands. When ioctl commands are omitted only the permissions are checked. This feature is intended to provide finer granularity for the ioctl permission that may be too imprecise. For example, the same driver may use ioctls to provide important and benign functionality such as driver version or socket type as well as dangerous capabilities such as debugging features, read/write/execute to physical memory or access to sensitive data. Per-command filtering provides a mechanism to reduce the attack surface of the kernel, and limit applications to the subset of commands required. The format of the policy binary has been modified to include ioctl commands, and the policy version number has been incremented to POLICYDB_VERSION_XPERMS_IOCTL=30 to account for the format change. The extended permissions logic is deliberately generic to allow components to be reused e.g. netlink filters Signed-off-by: Jeff Vander Stoep <[email protected]> Acked-by: Nick Kralevich <[email protected]> Signed-off-by: Paul Moore <[email protected]>
1 parent 671a278 commit fa1aa14

File tree

11 files changed

+834
-60
lines changed

11 files changed

+834
-60
lines changed

security/selinux/avc.c

Lines changed: 400 additions & 15 deletions
Large diffs are not rendered by default.

security/selinux/hooks.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3216,6 +3216,46 @@ static void selinux_file_free_security(struct file *file)
32163216
file_free_security(file);
32173217
}
32183218

3219+
/*
3220+
* Check whether a task has the ioctl permission and cmd
3221+
* operation to an inode.
3222+
*/
3223+
int ioctl_has_perm(const struct cred *cred, struct file *file,
3224+
u32 requested, u16 cmd)
3225+
{
3226+
struct common_audit_data ad;
3227+
struct file_security_struct *fsec = file->f_security;
3228+
struct inode *inode = file_inode(file);
3229+
struct inode_security_struct *isec = inode->i_security;
3230+
struct lsm_ioctlop_audit ioctl;
3231+
u32 ssid = cred_sid(cred);
3232+
int rc;
3233+
u8 driver = cmd >> 8;
3234+
u8 xperm = cmd & 0xff;
3235+
3236+
ad.type = LSM_AUDIT_DATA_IOCTL_OP;
3237+
ad.u.op = &ioctl;
3238+
ad.u.op->cmd = cmd;
3239+
ad.u.op->path = file->f_path;
3240+
3241+
if (ssid != fsec->sid) {
3242+
rc = avc_has_perm(ssid, fsec->sid,
3243+
SECCLASS_FD,
3244+
FD__USE,
3245+
&ad);
3246+
if (rc)
3247+
goto out;
3248+
}
3249+
3250+
if (unlikely(IS_PRIVATE(inode)))
3251+
return 0;
3252+
3253+
rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
3254+
requested, driver, xperm, &ad);
3255+
out:
3256+
return rc;
3257+
}
3258+
32193259
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
32203260
unsigned long arg)
32213261
{
@@ -3258,7 +3298,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
32583298
* to the file's ioctl() function.
32593299
*/
32603300
default:
3261-
error = file_has_perm(cred, file, FILE__IOCTL);
3301+
error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
32623302
}
32633303
return error;
32643304
}

security/selinux/include/avc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
142142
}
143143

144144
#define AVC_STRICT 1 /* Ignore permissive mode. */
145+
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
145146
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
146147
u16 tclass, u32 requested,
147148
unsigned flags,
@@ -151,6 +152,10 @@ int avc_has_perm(u32 ssid, u32 tsid,
151152
u16 tclass, u32 requested,
152153
struct common_audit_data *auditdata);
153154

155+
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
156+
u8 driver, u8 perm, struct common_audit_data *ad);
157+
158+
154159
u32 avc_policy_seqno(void);
155160

156161
#define AVC_CALLBACK_GRANT 1
@@ -161,6 +166,7 @@ u32 avc_policy_seqno(void);
161166
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
162167
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
163168
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
169+
#define AVC_CALLBACK_ADD_XPERMS 256
164170

165171
int avc_add_callback(int (*callback)(u32 event), u32 events);
166172

security/selinux/include/security.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@
3535
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
3636
#define POLICYDB_VERSION_DEFAULT_TYPE 28
3737
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
38+
#define POLICYDB_VERSION_XPERMS_IOCTL 30
3839

3940
/* Range of policy versions we understand*/
4041
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
4142
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
4243
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
4344
#else
44-
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES
45+
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL
4546
#endif
4647

4748
/* Mask for just the mount related flags */
@@ -109,11 +110,38 @@ struct av_decision {
109110
u32 flags;
110111
};
111112

113+
#define XPERMS_ALLOWED 1
114+
#define XPERMS_AUDITALLOW 2
115+
#define XPERMS_DONTAUDIT 4
116+
117+
#define security_xperm_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f))
118+
#define security_xperm_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f)))
119+
struct extended_perms_data {
120+
u32 p[8];
121+
};
122+
123+
struct extended_perms_decision {
124+
u8 used;
125+
u8 driver;
126+
struct extended_perms_data *allowed;
127+
struct extended_perms_data *auditallow;
128+
struct extended_perms_data *dontaudit;
129+
};
130+
131+
struct extended_perms {
132+
u16 len; /* length associated decision chain */
133+
struct extended_perms_data drivers; /* flag drivers that are used */
134+
};
135+
112136
/* definitions of av_decision.flags */
113137
#define AVD_FLAGS_PERMISSIVE 0x0001
114138

115139
void security_compute_av(u32 ssid, u32 tsid,
116-
u16 tclass, struct av_decision *avd);
140+
u16 tclass, struct av_decision *avd,
141+
struct extended_perms *xperms);
142+
143+
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass,
144+
u8 driver, struct extended_perms_decision *xpermd);
117145

118146
void security_compute_av_user(u32 ssid, u32 tsid,
119147
u16 tclass, struct av_decision *avd);

security/selinux/ss/avtab.c

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "policydb.h"
2525

2626
static struct kmem_cache *avtab_node_cachep;
27+
static struct kmem_cache *avtab_xperms_cachep;
2728

2829
/* Based on MurmurHash3, written by Austin Appleby and placed in the
2930
* public domain.
@@ -70,11 +71,24 @@ avtab_insert_node(struct avtab *h, int hvalue,
7071
struct avtab_key *key, struct avtab_datum *datum)
7172
{
7273
struct avtab_node *newnode;
74+
struct avtab_extended_perms *xperms;
7375
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
7476
if (newnode == NULL)
7577
return NULL;
7678
newnode->key = *key;
77-
newnode->datum = *datum;
79+
80+
if (key->specified & AVTAB_XPERMS) {
81+
xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL);
82+
if (xperms == NULL) {
83+
kmem_cache_free(avtab_node_cachep, newnode);
84+
return NULL;
85+
}
86+
*xperms = *(datum->u.xperms);
87+
newnode->datum.u.xperms = xperms;
88+
} else {
89+
newnode->datum.u.data = datum->u.data;
90+
}
91+
7892
if (prev) {
7993
newnode->next = prev->next;
8094
prev->next = newnode;
@@ -107,8 +121,12 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
107121
if (key->source_type == cur->key.source_type &&
108122
key->target_type == cur->key.target_type &&
109123
key->target_class == cur->key.target_class &&
110-
(specified & cur->key.specified))
124+
(specified & cur->key.specified)) {
125+
/* extended perms may not be unique */
126+
if (specified & AVTAB_XPERMS)
127+
break;
111128
return -EEXIST;
129+
}
112130
if (key->source_type < cur->key.source_type)
113131
break;
114132
if (key->source_type == cur->key.source_type &&
@@ -271,6 +289,9 @@ void avtab_destroy(struct avtab *h)
271289
while (cur) {
272290
temp = cur;
273291
cur = cur->next;
292+
if (temp->key.specified & AVTAB_XPERMS)
293+
kmem_cache_free(avtab_xperms_cachep,
294+
temp->datum.u.xperms);
274295
kmem_cache_free(avtab_node_cachep, temp);
275296
}
276297
}
@@ -359,7 +380,10 @@ static uint16_t spec_order[] = {
359380
AVTAB_AUDITALLOW,
360381
AVTAB_TRANSITION,
361382
AVTAB_CHANGE,
362-
AVTAB_MEMBER
383+
AVTAB_MEMBER,
384+
AVTAB_XPERMS_ALLOWED,
385+
AVTAB_XPERMS_AUDITALLOW,
386+
AVTAB_XPERMS_DONTAUDIT
363387
};
364388

365389
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
@@ -369,10 +393,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
369393
{
370394
__le16 buf16[4];
371395
u16 enabled;
372-
__le32 buf32[7];
373396
u32 items, items2, val, vers = pol->policyvers;
374397
struct avtab_key key;
375398
struct avtab_datum datum;
399+
struct avtab_extended_perms xperms;
400+
__le32 buf32[ARRAY_SIZE(xperms.perms.p)];
376401
int i, rc;
377402
unsigned set;
378403

@@ -429,11 +454,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
429454
printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
430455
return -EINVAL;
431456
}
457+
if (val & AVTAB_XPERMS) {
458+
printk(KERN_ERR "SELinux: avtab: entry has extended permissions\n");
459+
return -EINVAL;
460+
}
432461

433462
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
434463
if (val & spec_order[i]) {
435464
key.specified = spec_order[i] | enabled;
436-
datum.data = le32_to_cpu(buf32[items++]);
465+
datum.u.data = le32_to_cpu(buf32[items++]);
437466
rc = insertf(a, &key, &datum, p);
438467
if (rc)
439468
return rc;
@@ -476,14 +505,42 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
476505
return -EINVAL;
477506
}
478507

479-
rc = next_entry(buf32, fp, sizeof(u32));
480-
if (rc) {
481-
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
482-
return rc;
508+
if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
509+
(key.specified & AVTAB_XPERMS)) {
510+
printk(KERN_ERR "SELinux: avtab: policy version %u does not "
511+
"support extended permissions rules and one "
512+
"was specified\n", vers);
513+
return -EINVAL;
514+
} else if (key.specified & AVTAB_XPERMS) {
515+
memset(&xperms, 0, sizeof(struct avtab_extended_perms));
516+
rc = next_entry(&xperms.specified, fp, sizeof(u8));
517+
if (rc) {
518+
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
519+
return rc;
520+
}
521+
rc = next_entry(&xperms.driver, fp, sizeof(u8));
522+
if (rc) {
523+
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
524+
return rc;
525+
}
526+
rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
527+
if (rc) {
528+
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
529+
return rc;
530+
}
531+
for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
532+
xperms.perms.p[i] = le32_to_cpu(buf32[i]);
533+
datum.u.xperms = &xperms;
534+
} else {
535+
rc = next_entry(buf32, fp, sizeof(u32));
536+
if (rc) {
537+
printk(KERN_ERR "SELinux: avtab: truncated entry\n");
538+
return rc;
539+
}
540+
datum.u.data = le32_to_cpu(*buf32);
483541
}
484-
datum.data = le32_to_cpu(*buf32);
485542
if ((key.specified & AVTAB_TYPE) &&
486-
!policydb_type_isvalid(pol, datum.data)) {
543+
!policydb_type_isvalid(pol, datum.u.data)) {
487544
printk(KERN_ERR "SELinux: avtab: invalid type\n");
488545
return -EINVAL;
489546
}
@@ -543,8 +600,9 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
543600
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
544601
{
545602
__le16 buf16[4];
546-
__le32 buf32[1];
603+
__le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
547604
int rc;
605+
unsigned int i;
548606

549607
buf16[0] = cpu_to_le16(cur->key.source_type);
550608
buf16[1] = cpu_to_le16(cur->key.target_type);
@@ -553,8 +611,22 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
553611
rc = put_entry(buf16, sizeof(u16), 4, fp);
554612
if (rc)
555613
return rc;
556-
buf32[0] = cpu_to_le32(cur->datum.data);
557-
rc = put_entry(buf32, sizeof(u32), 1, fp);
614+
615+
if (cur->key.specified & AVTAB_XPERMS) {
616+
rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp);
617+
if (rc)
618+
return rc;
619+
rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
620+
if (rc)
621+
return rc;
622+
for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
623+
buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
624+
rc = put_entry(buf32, sizeof(u32),
625+
ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
626+
} else {
627+
buf32[0] = cpu_to_le32(cur->datum.u.data);
628+
rc = put_entry(buf32, sizeof(u32), 1, fp);
629+
}
558630
if (rc)
559631
return rc;
560632
return 0;
@@ -588,9 +660,13 @@ void avtab_cache_init(void)
588660
avtab_node_cachep = kmem_cache_create("avtab_node",
589661
sizeof(struct avtab_node),
590662
0, SLAB_PANIC, NULL);
663+
avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
664+
sizeof(struct avtab_extended_perms),
665+
0, SLAB_PANIC, NULL);
591666
}
592667

593668
void avtab_cache_destroy(void)
594669
{
595670
kmem_cache_destroy(avtab_node_cachep);
671+
kmem_cache_destroy(avtab_xperms_cachep);
596672
}

security/selinux/ss/avtab.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#ifndef _SS_AVTAB_H_
2424
#define _SS_AVTAB_H_
2525

26+
#include "security.h"
2627
#include <linux/flex_array.h>
2728

2829
struct avtab_key {
@@ -37,13 +38,43 @@ struct avtab_key {
3738
#define AVTAB_MEMBER 0x0020
3839
#define AVTAB_CHANGE 0x0040
3940
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
41+
/* extended permissions */
42+
#define AVTAB_XPERMS_ALLOWED 0x0100
43+
#define AVTAB_XPERMS_AUDITALLOW 0x0200
44+
#define AVTAB_XPERMS_DONTAUDIT 0x0400
45+
#define AVTAB_XPERMS (AVTAB_XPERMS_ALLOWED | \
46+
AVTAB_XPERMS_AUDITALLOW | \
47+
AVTAB_XPERMS_DONTAUDIT)
4048
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
4149
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
4250
u16 specified; /* what field is specified */
4351
};
4452

53+
/*
54+
* For operations that require more than the 32 permissions provided by the avc
55+
* extended permissions may be used to provide 256 bits of permissions.
56+
*/
57+
struct avtab_extended_perms {
58+
/* These are not flags. All 256 values may be used */
59+
#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
60+
#define AVTAB_XPERMS_IOCTLDRIVER 0x02
61+
/* extension of the avtab_key specified */
62+
u8 specified; /* ioctl, netfilter, ... */
63+
/*
64+
* if 256 bits is not adequate as is often the case with ioctls, then
65+
* multiple extended perms may be used and the driver field
66+
* specifies which permissions are included.
67+
*/
68+
u8 driver;
69+
/* 256 bits of permissions */
70+
struct extended_perms_data perms;
71+
};
72+
4573
struct avtab_datum {
46-
u32 data; /* access vector or type value */
74+
union {
75+
u32 data; /* access vector or type value */
76+
struct avtab_extended_perms *xperms;
77+
} u;
4778
};
4879

4980
struct avtab_node {

0 commit comments

Comments
 (0)