Skip to content

Commit 80bc7ee

Browse files
jeffvanderstoepstephensmalley
authored andcommitted
Add support for ioctl command whitelisting
Adds support for new policy statements whitelisting individual ioctl commands. Ioctls provide many of the operations necessary for driver control. The typical driver supports a device specific set of operations accessible by the ioctl system call and specified by the command argument. SELinux provides per operation access control to many system operations e.g. chown, kill, setuid, ipc_lock, etc. Ioclts on the other hand are granted on a per file descriptor basis using the ioctl permission, meaning that the set of operations provided by the driver are granted on an all-or-nothing basis. In some cases this may be acceptable, but often the same driver provides a large and diverse set of operations such as benign and necessary functionality as well as dangerous capabilities or access to system information that should be restricted. Example policy: allow <source> <target>:<class> { 0x8900-0x8905 0x8910 } auditallow <source> <target>:<class> 0x8901 The ioctl permission is still required in order to make an ioctl call. If no individual ioctl commands are specified, only the ioctl permission is checked by the kernel - i.e. status quo. This allows ioctl whitelisting to done in a targeted manner, protecting desired drivers without requiring every ioctl command to be known and specified before use and otherwise allowing existing policy to be used as-is. This only implements ioctl whitelisting support for monolithic kernel policies built via checkpolicy. Support for modules and CIL remains to be done. Bug: 19419509 Change-Id: I198e8c9279b94d8ce4ae5625018daa99577ee970 Signed-off-by: Jeff Vander Stoep <[email protected]> Acked-by: Stephen Smalley <[email protected]>
1 parent de4f82b commit 80bc7ee

File tree

9 files changed

+886
-53
lines changed

9 files changed

+886
-53
lines changed

checkpolicy/policy_define.c

Lines changed: 615 additions & 0 deletions
Large diffs are not rendered by default.

checkpolicy/policy_define.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
* for NULL (ie 0) because that is a potentially valid return.
1010
*/
1111
#define COND_ERR ((avrule_t *)-1)
12-
1312
#define TRUE 1
1413
#define FALSE 0
1514

@@ -59,6 +58,7 @@ int define_roleattribute(void);
5958
int define_filename_trans(void);
6059
int define_sens(void);
6160
int define_te_avtab(int which);
61+
int define_te_avtab_operation(int which);
6262
int define_typealias(void);
6363
int define_typeattribute(void);
6464
int define_typebounds(void);

checkpolicy/policy_parse.y

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ te_avtab_def : allow_def
457457
| auditdeny_def
458458
| dontaudit_def
459459
| neverallow_def
460+
| operation_allow_def
461+
| operation_auditallow_def
462+
| operation_dontaudit_def
460463
;
461464
allow_def : ALLOW names names ':' names names ';'
462465
{if (define_te_avtab(AVRULE_ALLOWED)) return -1; }
@@ -473,6 +476,15 @@ dontaudit_def : DONTAUDIT names names ':' names names ';'
473476
neverallow_def : NEVERALLOW names names ':' names names ';'
474477
{if (define_te_avtab(AVRULE_NEVERALLOW)) return -1; }
475478
;
479+
operation_allow_def : ALLOW names names ':' names operations ';'
480+
{if (define_te_avtab_operation(AVRULE_OPNUM_ALLOWED)) return -1; }
481+
;
482+
operation_auditallow_def: AUDITALLOW names names ':' names operations ';'
483+
{if (define_te_avtab_operation(AVRULE_OPNUM_AUDITALLOW)) return -1; }
484+
;
485+
operation_dontaudit_def : DONTAUDIT names names ':' names operations ';'
486+
{if (define_te_avtab_operation(AVRULE_OPNUM_DONTAUDIT)) return -1; }
487+
;
476488
attribute_role_def : ATTRIBUTE_ROLE identifier ';'
477489
{if (define_attrib_role()) return -1; }
478490
;
@@ -737,6 +749,28 @@ genfs_context_def : GENFSCON filesystem path '-' identifier security_context_def
737749
ipv4_addr_def : IPV4_ADDR
738750
{ if (insert_id(yytext,0)) return -1; }
739751
;
752+
operations : operation
753+
{ if (insert_separator(0)) return -1; }
754+
| nested_operation_set
755+
{ if (insert_separator(0)) return -1; }
756+
| tilde operation
757+
{ if (insert_id("~", 0)) return -1; }
758+
| tilde nested_operation_set
759+
{ if (insert_id("~", 0)) return -1;
760+
if (insert_separator(0)) return -1; }
761+
;
762+
nested_operation_set : '{' nested_operation_list '}'
763+
;
764+
nested_operation_list : nested_operation_element
765+
| nested_operation_list nested_operation_element
766+
;
767+
nested_operation_element: operation '-' { if (insert_id("-", 0)) return -1; } operation
768+
| operation
769+
| nested_operation_set
770+
;
771+
operation : number
772+
{ if (insert_id(yytext,0)) return -1; }
773+
;
740774
security_context_def : identifier ':' identifier ':' identifier opt_mls_range_def
741775
;
742776
opt_mls_range_def : ':' mls_range_def

libsepol/include/sepol/policydb/avtab.h

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,37 @@ typedef struct avtab_key {
5050
uint16_t source_type;
5151
uint16_t target_type;
5252
uint16_t target_class;
53-
#define AVTAB_ALLOWED 1
54-
#define AVTAB_AUDITALLOW 2
55-
#define AVTAB_AUDITDENY 4
56-
#define AVTAB_NEVERALLOW 128
57-
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
58-
#define AVTAB_TRANSITION 16
59-
#define AVTAB_MEMBER 32
60-
#define AVTAB_CHANGE 64
61-
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
62-
#define AVTAB_ENABLED_OLD 0x80000000
63-
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
53+
#define AVTAB_ALLOWED 0x0001
54+
#define AVTAB_AUDITALLOW 0x0002
55+
#define AVTAB_AUDITDENY 0x0004
56+
#define AVTAB_NEVERALLOW 0x0080
57+
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
58+
#define AVTAB_TRANSITION 0x0010
59+
#define AVTAB_MEMBER 0x0020
60+
#define AVTAB_CHANGE 0x0040
61+
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
62+
#define AVTAB_OPNUM_ALLOWED 0x0100
63+
#define AVTAB_OPNUM_AUDITALLOW 0x0200
64+
#define AVTAB_OPNUM_DONTAUDIT 0x0400
65+
#define AVTAB_OPNUM (AVTAB_OPNUM_ALLOWED | AVTAB_OPNUM_AUDITALLOW | AVTAB_OPNUM_DONTAUDIT)
66+
#define AVTAB_OPTYPE_ALLOWED 0x1000
67+
#define AVTAB_OPTYPE_AUDITALLOW 0x2000
68+
#define AVTAB_OPTYPE_DONTAUDIT 0x4000
69+
#define AVTAB_OPTYPE (AVTAB_OPTYPE_ALLOWED | AVTAB_OPTYPE_AUDITALLOW | AVTAB_OPTYPE_DONTAUDIT)
70+
#define AVTAB_OP (AVTAB_OPNUM | AVTAB_OPTYPE)
71+
#define AVTAB_ENABLED_OLD 0x80000000
72+
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
6473
uint16_t specified; /* what fields are specified */
6574
} avtab_key_t;
6675

76+
typedef struct avtab_operations {
77+
uint8_t type;
78+
uint32_t perms[8];
79+
} avtab_operations_t;
80+
6781
typedef struct avtab_datum {
6882
uint32_t data; /* access vector or type */
83+
avtab_operations_t *ops;
6984
} avtab_datum_t;
7085

7186
typedef struct avtab_node *avtab_ptr_t;

libsepol/include/sepol/policydb/policydb.h

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -241,25 +241,43 @@ typedef struct class_perm_node {
241241
struct class_perm_node *next;
242242
} class_perm_node_t;
243243

244+
typedef struct av_operations {
245+
uint8_t type;
246+
/* 256 bits of ioctl number permissions */
247+
uint32_t perms[8];
248+
} av_operations_t;
249+
244250
typedef struct avrule {
245251
/* these typedefs are almost exactly the same as those in avtab.h - they are
246252
* here because of the need to include neverallow and dontaudit messages */
247-
#define AVRULE_ALLOWED 1
248-
#define AVRULE_AUDITALLOW 2
249-
#define AVRULE_AUDITDENY 4
250-
#define AVRULE_DONTAUDIT 8
251-
#define AVRULE_NEVERALLOW 128
253+
#define AVRULE_ALLOWED 0x0001
254+
#define AVRULE_AUDITALLOW 0x0002
255+
#define AVRULE_AUDITDENY 0x0004
256+
#define AVRULE_DONTAUDIT 0x0008
257+
#define AVRULE_NEVERALLOW 0x0080
252258
#define AVRULE_AV (AVRULE_ALLOWED | AVRULE_AUDITALLOW | AVRULE_AUDITDENY | AVRULE_DONTAUDIT | AVRULE_NEVERALLOW)
253-
#define AVRULE_TRANSITION 16
254-
#define AVRULE_MEMBER 32
255-
#define AVRULE_CHANGE 64
259+
#define AVRULE_TRANSITION 0x0010
260+
#define AVRULE_MEMBER 0x0020
261+
#define AVRULE_CHANGE 0x0040
256262
#define AVRULE_TYPE (AVRULE_TRANSITION | AVRULE_MEMBER | AVRULE_CHANGE)
263+
#define AVRULE_OPNUM_ALLOWED 0x0100
264+
#define AVRULE_OPNUM_AUDITALLOW 0x0200
265+
#define AVRULE_OPNUM_DONTAUDIT 0x0400
266+
#define AVRULE_OPNUM (AVRULE_OPNUM_ALLOWED | AVRULE_OPNUM_AUDITALLOW | \
267+
AVRULE_OPNUM_DONTAUDIT)
268+
#define AVRULE_OPTYPE_ALLOWED 0x1000
269+
#define AVRULE_OPTYPE_AUDITALLOW 0x2000
270+
#define AVRULE_OPTYPE_DONTAUDIT 0x4000
271+
#define AVRULE_OPTYPE (AVRULE_OPTYPE_ALLOWED | AVRULE_OPTYPE_AUDITALLOW | \
272+
AVRULE_OPTYPE_DONTAUDIT)
273+
#define AVRULE_OP (AVRULE_OPNUM | AVRULE_OPTYPE)
257274
uint32_t specified;
258275
#define RULE_SELF 1
259276
uint32_t flags;
260277
type_set_t stypes;
261278
type_set_t ttypes;
262279
class_perm_node_t *perms;
280+
av_operations_t * ops;
263281
unsigned long line; /* line number from policy.conf where
264282
* this rule originated */
265283
/* source file name and line number (e.g. .te file) */
@@ -690,11 +708,12 @@ extern int policydb_set_target_platform(policydb_t *p, int platform);
690708
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
691709
#define POLICYDB_VERSION_DEFAULT_TYPE 28
692710
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
693-
#define POLICYDB_VERSION_XEN_DEVICETREE 30
711+
#define POLICYDB_VERSION_XEN_DEVICETREE 30 /* Xen-specific */
712+
#define POLICYDB_VERSION_IOCTL_OPERATIONS 30 /* Linux-specific */
694713

695714
/* Range of policy versions we understand*/
696715
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
697-
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XEN_DEVICETREE
716+
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_IOCTL_OPERATIONS
698717

699718
/* Module versions and specific changes*/
700719
#define MOD_POLICYDB_VERSION_BASE 4

libsepol/src/avtab.c

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,28 @@ avtab_insert_node(avtab_t * h, int hvalue, avtab_ptr_t prev, avtab_key_t * key,
9393
avtab_datum_t * datum)
9494
{
9595
avtab_ptr_t newnode;
96+
avtab_operations_t *ops;
97+
9698
newnode = (avtab_ptr_t) malloc(sizeof(struct avtab_node));
9799
if (newnode == NULL)
98100
return NULL;
99101
memset(newnode, 0, sizeof(struct avtab_node));
100102
newnode->key = *key;
101-
newnode->datum = *datum;
103+
104+
if (key->specified & AVTAB_OP) {
105+
ops = calloc(1, sizeof(avtab_operations_t));
106+
if (ops == NULL) {
107+
free(newnode);
108+
return NULL;
109+
}
110+
if (datum->ops) /* else caller populates ops*/
111+
*ops = *(datum->ops);
112+
113+
newnode->datum.ops = ops;
114+
} else {
115+
newnode->datum = *datum;
116+
}
117+
102118
if (prev) {
103119
newnode->next = prev->next;
104120
prev->next = newnode;
@@ -127,8 +143,11 @@ int avtab_insert(avtab_t * h, avtab_key_t * key, avtab_datum_t * datum)
127143
if (key->source_type == cur->key.source_type &&
128144
key->target_type == cur->key.target_type &&
129145
key->target_class == cur->key.target_class &&
130-
(specified & cur->key.specified))
146+
(specified & cur->key.specified)) {
147+
if (specified & AVTAB_OPNUM)
148+
break;
131149
return SEPOL_EEXIST;
150+
}
132151
if (key->source_type < cur->key.source_type)
133152
break;
134153
if (key->source_type == cur->key.source_type &&
@@ -396,23 +415,32 @@ static uint16_t spec_order[] = {
396415
AVTAB_AUDITALLOW,
397416
AVTAB_TRANSITION,
398417
AVTAB_CHANGE,
399-
AVTAB_MEMBER
418+
AVTAB_MEMBER,
419+
AVTAB_OPNUM_ALLOWED,
420+
AVTAB_OPNUM_AUDITALLOW,
421+
AVTAB_OPNUM_DONTAUDIT,
422+
AVTAB_OPTYPE_ALLOWED,
423+
AVTAB_OPTYPE_AUDITALLOW,
424+
AVTAB_OPTYPE_DONTAUDIT
400425
};
401426

402427
int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
403428
int (*insertf) (avtab_t * a, avtab_key_t * k,
404429
avtab_datum_t * d, void *p), void *p)
405430
{
431+
uint8_t buf8;
406432
uint16_t buf16[4], enabled;
407-
uint32_t buf32[7], items, items2, val;
433+
uint32_t buf32[8], items, items2, val;
408434
avtab_key_t key;
409435
avtab_datum_t datum;
436+
avtab_operations_t ops;
410437
unsigned set;
411438
unsigned int i;
412439
int rc;
413440

414441
memset(&key, 0, sizeof(avtab_key_t));
415442
memset(&datum, 0, sizeof(avtab_datum_t));
443+
memset(&ops, 0, sizeof(avtab_operations_t));
416444

417445
if (vers < POLICYDB_VERSION_AVTAB) {
418446
rc = next_entry(buf32, fp, sizeof(uint32_t));
@@ -505,12 +533,34 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
505533
return -1;
506534
}
507535

508-
rc = next_entry(buf32, fp, sizeof(uint32_t));
509-
if (rc < 0) {
510-
ERR(fp->handle, "truncated entry");
536+
if ((vers < POLICYDB_VERSION_IOCTL_OPERATIONS) &&
537+
(key.specified & AVTAB_OP)) {
538+
ERR(fp->handle, "policy version %u does not support ioctl "
539+
"operation rules and one was specified\n", vers);
511540
return -1;
541+
} else if (key.specified & AVTAB_OP) {
542+
rc = next_entry(&buf8, fp, sizeof(uint8_t));
543+
if (rc < 0) {
544+
ERR(fp->handle, "truncated entry");
545+
return -1;
546+
}
547+
ops.type = buf8;
548+
rc = next_entry(buf32, fp, sizeof(uint32_t)*8);
549+
if (rc < 0) {
550+
ERR(fp->handle, "truncated entry");
551+
return -1;
552+
}
553+
for (i = 0; i < ARRAY_SIZE(ops.perms); i++)
554+
ops.perms[i] = le32_to_cpu(buf32[i]);
555+
datum.ops = &ops;
556+
} else {
557+
rc = next_entry(buf32, fp, sizeof(uint32_t));
558+
if (rc < 0) {
559+
ERR(fp->handle, "truncated entry");
560+
return -1;
561+
}
562+
datum.data = le32_to_cpu(*buf32);
512563
}
513-
datum.data = le32_to_cpu(*buf32);
514564
return insertf(a, &key, &datum, p);
515565
}
516566

0 commit comments

Comments
 (0)