diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/avc.c | 2 | ||||
-rw-r--r-- | security/selinux/hooks.c | 15 | ||||
-rw-r--r-- | security/selinux/include/avc.h | 4 | ||||
-rw-r--r-- | security/selinux/include/security.h | 15 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 205 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 5 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 172 |
7 files changed, 398 insertions, 20 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 114b4b4c97b2..cb30c7e350b3 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
@@ -136,7 +136,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) | |||
136 | * @tclass: target security class | 136 | * @tclass: target security class |
137 | * @av: access vector | 137 | * @av: access vector |
138 | */ | 138 | */ |
139 | static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) | 139 | void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) |
140 | { | 140 | { |
141 | const char **common_pts = NULL; | 141 | const char **common_pts = NULL; |
142 | u32 common_base = 0; | 142 | u32 common_base = 0; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6b5790bba8f9..89f446d86054 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -5226,8 +5226,12 @@ static int selinux_setprocattr(struct task_struct *p, | |||
5226 | 5226 | ||
5227 | if (sid == 0) | 5227 | if (sid == 0) |
5228 | return -EINVAL; | 5228 | return -EINVAL; |
5229 | 5229 | /* | |
5230 | /* Only allow single threaded processes to change context */ | 5230 | * SELinux allows to change context in the following case only. |
5231 | * - Single threaded processes. | ||
5232 | * - Multi threaded processes intend to change its context into | ||
5233 | * more restricted domain (defined by TYPEBOUNDS statement). | ||
5234 | */ | ||
5231 | if (atomic_read(&p->mm->mm_users) != 1) { | 5235 | if (atomic_read(&p->mm->mm_users) != 1) { |
5232 | struct task_struct *g, *t; | 5236 | struct task_struct *g, *t; |
5233 | struct mm_struct *mm = p->mm; | 5237 | struct mm_struct *mm = p->mm; |
@@ -5235,11 +5239,16 @@ static int selinux_setprocattr(struct task_struct *p, | |||
5235 | do_each_thread(g, t) { | 5239 | do_each_thread(g, t) { |
5236 | if (t->mm == mm && t != p) { | 5240 | if (t->mm == mm && t != p) { |
5237 | read_unlock(&tasklist_lock); | 5241 | read_unlock(&tasklist_lock); |
5238 | return -EPERM; | 5242 | error = security_bounded_transition(tsec->sid, sid); |
5243 | if (!error) | ||
5244 | goto boundary_ok; | ||
5245 | |||
5246 | return error; | ||
5239 | } | 5247 | } |
5240 | } while_each_thread(g, t); | 5248 | } while_each_thread(g, t); |
5241 | read_unlock(&tasklist_lock); | 5249 | read_unlock(&tasklist_lock); |
5242 | } | 5250 | } |
5251 | boundary_ok: | ||
5243 | 5252 | ||
5244 | /* Check permissions for the transition. */ | 5253 | /* Check permissions for the transition. */ |
5245 | error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, | 5254 | error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, |
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 7b9769f5e775..d12ff1a9c0aa 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/kdev_t.h> | 12 | #include <linux/kdev_t.h> |
13 | #include <linux/spinlock.h> | 13 | #include <linux/spinlock.h> |
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/audit.h> | ||
15 | #include <linux/in6.h> | 16 | #include <linux/in6.h> |
16 | #include <linux/path.h> | 17 | #include <linux/path.h> |
17 | #include <asm/system.h> | 18 | #include <asm/system.h> |
@@ -126,6 +127,9 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, | |||
126 | u32 events, u32 ssid, u32 tsid, | 127 | u32 events, u32 ssid, u32 tsid, |
127 | u16 tclass, u32 perms); | 128 | u16 tclass, u32 perms); |
128 | 129 | ||
130 | /* Shows permission in human readable form */ | ||
131 | void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av); | ||
132 | |||
129 | /* Exported to selinuxfs */ | 133 | /* Exported to selinuxfs */ |
130 | int avc_get_hash_stats(char *page); | 134 | int avc_get_hash_stats(char *page); |
131 | extern unsigned int avc_cache_threshold; | 135 | extern unsigned int avc_cache_threshold; |
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 7c543003d653..72447370bc95 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
@@ -27,13 +27,14 @@ | |||
27 | #define POLICYDB_VERSION_RANGETRANS 21 | 27 | #define POLICYDB_VERSION_RANGETRANS 21 |
28 | #define POLICYDB_VERSION_POLCAP 22 | 28 | #define POLICYDB_VERSION_POLCAP 22 |
29 | #define POLICYDB_VERSION_PERMISSIVE 23 | 29 | #define POLICYDB_VERSION_PERMISSIVE 23 |
30 | #define POLICYDB_VERSION_BOUNDARY 24 | ||
30 | 31 | ||
31 | /* Range of policy versions we understand*/ | 32 | /* Range of policy versions we understand*/ |
32 | #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE | 33 | #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE |
33 | #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX | 34 | #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX |
34 | #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE | 35 | #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE |
35 | #else | 36 | #else |
36 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_PERMISSIVE | 37 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY |
37 | #endif | 38 | #endif |
38 | 39 | ||
39 | #define CONTEXT_MNT 0x01 | 40 | #define CONTEXT_MNT 0x01 |
@@ -62,6 +63,16 @@ enum { | |||
62 | extern int selinux_policycap_netpeer; | 63 | extern int selinux_policycap_netpeer; |
63 | extern int selinux_policycap_openperm; | 64 | extern int selinux_policycap_openperm; |
64 | 65 | ||
66 | /* | ||
67 | * type_datum properties | ||
68 | * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY | ||
69 | */ | ||
70 | #define TYPEDATUM_PROPERTY_PRIMARY 0x0001 | ||
71 | #define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002 | ||
72 | |||
73 | /* limitation of boundary depth */ | ||
74 | #define POLICYDB_BOUNDS_MAXDEPTH 4 | ||
75 | |||
65 | int security_load_policy(void *data, size_t len); | 76 | int security_load_policy(void *data, size_t len); |
66 | 77 | ||
67 | int security_policycap_supported(unsigned int req_cap); | 78 | int security_policycap_supported(unsigned int req_cap); |
@@ -117,6 +128,8 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen, | |||
117 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, | 128 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, |
118 | u16 tclass); | 129 | u16 tclass); |
119 | 130 | ||
131 | int security_bounded_transition(u32 oldsid, u32 newsid); | ||
132 | |||
120 | int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); | 133 | int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid); |
121 | 134 | ||
122 | int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, | 135 | int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type, |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 26646305dc0e..72e4a54973aa 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
31 | #include <linux/string.h> | 31 | #include <linux/string.h> |
32 | #include <linux/errno.h> | 32 | #include <linux/errno.h> |
33 | #include <linux/audit.h> | ||
33 | #include "security.h" | 34 | #include "security.h" |
34 | 35 | ||
35 | #include "policydb.h" | 36 | #include "policydb.h" |
@@ -116,7 +117,12 @@ static struct policydb_compat_info policydb_compat[] = { | |||
116 | .version = POLICYDB_VERSION_PERMISSIVE, | 117 | .version = POLICYDB_VERSION_PERMISSIVE, |
117 | .sym_num = SYM_NUM, | 118 | .sym_num = SYM_NUM, |
118 | .ocon_num = OCON_NUM, | 119 | .ocon_num = OCON_NUM, |
119 | } | 120 | }, |
121 | { | ||
122 | .version = POLICYDB_VERSION_BOUNDARY, | ||
123 | .sym_num = SYM_NUM, | ||
124 | .ocon_num = OCON_NUM, | ||
125 | }, | ||
120 | }; | 126 | }; |
121 | 127 | ||
122 | static struct policydb_compat_info *policydb_lookup_compat(int version) | 128 | static struct policydb_compat_info *policydb_lookup_compat(int version) |
@@ -254,7 +260,9 @@ static int role_index(void *key, void *datum, void *datap) | |||
254 | 260 | ||
255 | role = datum; | 261 | role = datum; |
256 | p = datap; | 262 | p = datap; |
257 | if (!role->value || role->value > p->p_roles.nprim) | 263 | if (!role->value |
264 | || role->value > p->p_roles.nprim | ||
265 | || role->bounds > p->p_roles.nprim) | ||
258 | return -EINVAL; | 266 | return -EINVAL; |
259 | p->p_role_val_to_name[role->value - 1] = key; | 267 | p->p_role_val_to_name[role->value - 1] = key; |
260 | p->role_val_to_struct[role->value - 1] = role; | 268 | p->role_val_to_struct[role->value - 1] = role; |
@@ -270,9 +278,12 @@ static int type_index(void *key, void *datum, void *datap) | |||
270 | p = datap; | 278 | p = datap; |
271 | 279 | ||
272 | if (typdatum->primary) { | 280 | if (typdatum->primary) { |
273 | if (!typdatum->value || typdatum->value > p->p_types.nprim) | 281 | if (!typdatum->value |
282 | || typdatum->value > p->p_types.nprim | ||
283 | || typdatum->bounds > p->p_types.nprim) | ||
274 | return -EINVAL; | 284 | return -EINVAL; |
275 | p->p_type_val_to_name[typdatum->value - 1] = key; | 285 | p->p_type_val_to_name[typdatum->value - 1] = key; |
286 | p->type_val_to_struct[typdatum->value - 1] = typdatum; | ||
276 | } | 287 | } |
277 | 288 | ||
278 | return 0; | 289 | return 0; |
@@ -285,7 +296,9 @@ static int user_index(void *key, void *datum, void *datap) | |||
285 | 296 | ||
286 | usrdatum = datum; | 297 | usrdatum = datum; |
287 | p = datap; | 298 | p = datap; |
288 | if (!usrdatum->value || usrdatum->value > p->p_users.nprim) | 299 | if (!usrdatum->value |
300 | || usrdatum->value > p->p_users.nprim | ||
301 | || usrdatum->bounds > p->p_users.nprim) | ||
289 | return -EINVAL; | 302 | return -EINVAL; |
290 | p->p_user_val_to_name[usrdatum->value - 1] = key; | 303 | p->p_user_val_to_name[usrdatum->value - 1] = key; |
291 | p->user_val_to_struct[usrdatum->value - 1] = usrdatum; | 304 | p->user_val_to_struct[usrdatum->value - 1] = usrdatum; |
@@ -438,6 +451,14 @@ static int policydb_index_others(struct policydb *p) | |||
438 | goto out; | 451 | goto out; |
439 | } | 452 | } |
440 | 453 | ||
454 | p->type_val_to_struct = | ||
455 | kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)), | ||
456 | GFP_KERNEL); | ||
457 | if (!p->type_val_to_struct) { | ||
458 | rc = -ENOMEM; | ||
459 | goto out; | ||
460 | } | ||
461 | |||
441 | if (cond_init_bool_indexes(p)) { | 462 | if (cond_init_bool_indexes(p)) { |
442 | rc = -ENOMEM; | 463 | rc = -ENOMEM; |
443 | goto out; | 464 | goto out; |
@@ -625,6 +646,7 @@ void policydb_destroy(struct policydb *p) | |||
625 | kfree(p->class_val_to_struct); | 646 | kfree(p->class_val_to_struct); |
626 | kfree(p->role_val_to_struct); | 647 | kfree(p->role_val_to_struct); |
627 | kfree(p->user_val_to_struct); | 648 | kfree(p->user_val_to_struct); |
649 | kfree(p->type_val_to_struct); | ||
628 | 650 | ||
629 | avtab_destroy(&p->te_avtab); | 651 | avtab_destroy(&p->te_avtab); |
630 | 652 | ||
@@ -1176,8 +1198,8 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1176 | { | 1198 | { |
1177 | char *key = NULL; | 1199 | char *key = NULL; |
1178 | struct role_datum *role; | 1200 | struct role_datum *role; |
1179 | int rc; | 1201 | int rc, to_read = 2; |
1180 | __le32 buf[2]; | 1202 | __le32 buf[3]; |
1181 | u32 len; | 1203 | u32 len; |
1182 | 1204 | ||
1183 | role = kzalloc(sizeof(*role), GFP_KERNEL); | 1205 | role = kzalloc(sizeof(*role), GFP_KERNEL); |
@@ -1186,12 +1208,17 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1186 | goto out; | 1208 | goto out; |
1187 | } | 1209 | } |
1188 | 1210 | ||
1189 | rc = next_entry(buf, fp, sizeof buf); | 1211 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) |
1212 | to_read = 3; | ||
1213 | |||
1214 | rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); | ||
1190 | if (rc < 0) | 1215 | if (rc < 0) |
1191 | goto bad; | 1216 | goto bad; |
1192 | 1217 | ||
1193 | len = le32_to_cpu(buf[0]); | 1218 | len = le32_to_cpu(buf[0]); |
1194 | role->value = le32_to_cpu(buf[1]); | 1219 | role->value = le32_to_cpu(buf[1]); |
1220 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) | ||
1221 | role->bounds = le32_to_cpu(buf[2]); | ||
1195 | 1222 | ||
1196 | key = kmalloc(len + 1, GFP_KERNEL); | 1223 | key = kmalloc(len + 1, GFP_KERNEL); |
1197 | if (!key) { | 1224 | if (!key) { |
@@ -1236,8 +1263,8 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1236 | { | 1263 | { |
1237 | char *key = NULL; | 1264 | char *key = NULL; |
1238 | struct type_datum *typdatum; | 1265 | struct type_datum *typdatum; |
1239 | int rc; | 1266 | int rc, to_read = 3; |
1240 | __le32 buf[3]; | 1267 | __le32 buf[4]; |
1241 | u32 len; | 1268 | u32 len; |
1242 | 1269 | ||
1243 | typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); | 1270 | typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); |
@@ -1246,13 +1273,27 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1246 | return rc; | 1273 | return rc; |
1247 | } | 1274 | } |
1248 | 1275 | ||
1249 | rc = next_entry(buf, fp, sizeof buf); | 1276 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) |
1277 | to_read = 4; | ||
1278 | |||
1279 | rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); | ||
1250 | if (rc < 0) | 1280 | if (rc < 0) |
1251 | goto bad; | 1281 | goto bad; |
1252 | 1282 | ||
1253 | len = le32_to_cpu(buf[0]); | 1283 | len = le32_to_cpu(buf[0]); |
1254 | typdatum->value = le32_to_cpu(buf[1]); | 1284 | typdatum->value = le32_to_cpu(buf[1]); |
1255 | typdatum->primary = le32_to_cpu(buf[2]); | 1285 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) { |
1286 | u32 prop = le32_to_cpu(buf[2]); | ||
1287 | |||
1288 | if (prop & TYPEDATUM_PROPERTY_PRIMARY) | ||
1289 | typdatum->primary = 1; | ||
1290 | if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE) | ||
1291 | typdatum->attribute = 1; | ||
1292 | |||
1293 | typdatum->bounds = le32_to_cpu(buf[3]); | ||
1294 | } else { | ||
1295 | typdatum->primary = le32_to_cpu(buf[2]); | ||
1296 | } | ||
1256 | 1297 | ||
1257 | key = kmalloc(len + 1, GFP_KERNEL); | 1298 | key = kmalloc(len + 1, GFP_KERNEL); |
1258 | if (!key) { | 1299 | if (!key) { |
@@ -1309,8 +1350,8 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1309 | { | 1350 | { |
1310 | char *key = NULL; | 1351 | char *key = NULL; |
1311 | struct user_datum *usrdatum; | 1352 | struct user_datum *usrdatum; |
1312 | int rc; | 1353 | int rc, to_read = 2; |
1313 | __le32 buf[2]; | 1354 | __le32 buf[3]; |
1314 | u32 len; | 1355 | u32 len; |
1315 | 1356 | ||
1316 | usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); | 1357 | usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); |
@@ -1319,12 +1360,17 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) | |||
1319 | goto out; | 1360 | goto out; |
1320 | } | 1361 | } |
1321 | 1362 | ||
1322 | rc = next_entry(buf, fp, sizeof buf); | 1363 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) |
1364 | to_read = 3; | ||
1365 | |||
1366 | rc = next_entry(buf, fp, sizeof(buf[0]) * to_read); | ||
1323 | if (rc < 0) | 1367 | if (rc < 0) |
1324 | goto bad; | 1368 | goto bad; |
1325 | 1369 | ||
1326 | len = le32_to_cpu(buf[0]); | 1370 | len = le32_to_cpu(buf[0]); |
1327 | usrdatum->value = le32_to_cpu(buf[1]); | 1371 | usrdatum->value = le32_to_cpu(buf[1]); |
1372 | if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) | ||
1373 | usrdatum->bounds = le32_to_cpu(buf[2]); | ||
1328 | 1374 | ||
1329 | key = kmalloc(len + 1, GFP_KERNEL); | 1375 | key = kmalloc(len + 1, GFP_KERNEL); |
1330 | if (!key) { | 1376 | if (!key) { |
@@ -1465,6 +1511,133 @@ static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) | |||
1465 | cat_read, | 1511 | cat_read, |
1466 | }; | 1512 | }; |
1467 | 1513 | ||
1514 | static int user_bounds_sanity_check(void *key, void *datum, void *datap) | ||
1515 | { | ||
1516 | struct user_datum *upper, *user; | ||
1517 | struct policydb *p = datap; | ||
1518 | int depth = 0; | ||
1519 | |||
1520 | upper = user = datum; | ||
1521 | while (upper->bounds) { | ||
1522 | struct ebitmap_node *node; | ||
1523 | unsigned long bit; | ||
1524 | |||
1525 | if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { | ||
1526 | printk(KERN_ERR "SELinux: user %s: " | ||
1527 | "too deep or looped boundary", | ||
1528 | (char *) key); | ||
1529 | return -EINVAL; | ||
1530 | } | ||
1531 | |||
1532 | upper = p->user_val_to_struct[upper->bounds - 1]; | ||
1533 | ebitmap_for_each_positive_bit(&user->roles, node, bit) { | ||
1534 | if (ebitmap_get_bit(&upper->roles, bit)) | ||
1535 | continue; | ||
1536 | |||
1537 | printk(KERN_ERR | ||
1538 | "SELinux: boundary violated policy: " | ||
1539 | "user=%s role=%s bounds=%s\n", | ||
1540 | p->p_user_val_to_name[user->value - 1], | ||
1541 | p->p_role_val_to_name[bit], | ||
1542 | p->p_user_val_to_name[upper->value - 1]); | ||
1543 | |||
1544 | return -EINVAL; | ||
1545 | } | ||
1546 | } | ||
1547 | |||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | static int role_bounds_sanity_check(void *key, void *datum, void *datap) | ||
1552 | { | ||
1553 | struct role_datum *upper, *role; | ||
1554 | struct policydb *p = datap; | ||
1555 | int depth = 0; | ||
1556 | |||
1557 | upper = role = datum; | ||
1558 | while (upper->bounds) { | ||
1559 | struct ebitmap_node *node; | ||
1560 | unsigned long bit; | ||
1561 | |||
1562 | if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { | ||
1563 | printk(KERN_ERR "SELinux: role %s: " | ||
1564 | "too deep or looped bounds\n", | ||
1565 | (char *) key); | ||
1566 | return -EINVAL; | ||
1567 | } | ||
1568 | |||
1569 | upper = p->role_val_to_struct[upper->bounds - 1]; | ||
1570 | ebitmap_for_each_positive_bit(&role->types, node, bit) { | ||
1571 | if (ebitmap_get_bit(&upper->types, bit)) | ||
1572 | continue; | ||
1573 | |||
1574 | printk(KERN_ERR | ||
1575 | "SELinux: boundary violated policy: " | ||
1576 | "role=%s type=%s bounds=%s\n", | ||
1577 | p->p_role_val_to_name[role->value - 1], | ||
1578 | p->p_type_val_to_name[bit], | ||
1579 | p->p_role_val_to_name[upper->value - 1]); | ||
1580 | |||
1581 | return -EINVAL; | ||
1582 | } | ||
1583 | } | ||
1584 | |||
1585 | return 0; | ||
1586 | } | ||
1587 | |||
1588 | static int type_bounds_sanity_check(void *key, void *datum, void *datap) | ||
1589 | { | ||
1590 | struct type_datum *upper, *type; | ||
1591 | struct policydb *p = datap; | ||
1592 | int depth = 0; | ||
1593 | |||
1594 | upper = type = datum; | ||
1595 | while (upper->bounds) { | ||
1596 | if (++depth == POLICYDB_BOUNDS_MAXDEPTH) { | ||
1597 | printk(KERN_ERR "SELinux: type %s: " | ||
1598 | "too deep or looped boundary\n", | ||
1599 | (char *) key); | ||
1600 | return -EINVAL; | ||
1601 | } | ||
1602 | |||
1603 | upper = p->type_val_to_struct[upper->bounds - 1]; | ||
1604 | if (upper->attribute) { | ||
1605 | printk(KERN_ERR "SELinux: type %s: " | ||
1606 | "bounded by attribute %s", | ||
1607 | (char *) key, | ||
1608 | p->p_type_val_to_name[upper->value - 1]); | ||
1609 | return -EINVAL; | ||
1610 | } | ||
1611 | } | ||
1612 | |||
1613 | return 0; | ||
1614 | } | ||
1615 | |||
1616 | static int policydb_bounds_sanity_check(struct policydb *p) | ||
1617 | { | ||
1618 | int rc; | ||
1619 | |||
1620 | if (p->policyvers < POLICYDB_VERSION_BOUNDARY) | ||
1621 | return 0; | ||
1622 | |||
1623 | rc = hashtab_map(p->p_users.table, | ||
1624 | user_bounds_sanity_check, p); | ||
1625 | if (rc) | ||
1626 | return rc; | ||
1627 | |||
1628 | rc = hashtab_map(p->p_roles.table, | ||
1629 | role_bounds_sanity_check, p); | ||
1630 | if (rc) | ||
1631 | return rc; | ||
1632 | |||
1633 | rc = hashtab_map(p->p_types.table, | ||
1634 | type_bounds_sanity_check, p); | ||
1635 | if (rc) | ||
1636 | return rc; | ||
1637 | |||
1638 | return 0; | ||
1639 | } | ||
1640 | |||
1468 | extern int ss_initialized; | 1641 | extern int ss_initialized; |
1469 | 1642 | ||
1470 | /* | 1643 | /* |
@@ -1961,6 +2134,10 @@ int policydb_read(struct policydb *p, void *fp) | |||
1961 | goto bad; | 2134 | goto bad; |
1962 | } | 2135 | } |
1963 | 2136 | ||
2137 | rc = policydb_bounds_sanity_check(p); | ||
2138 | if (rc) | ||
2139 | goto bad; | ||
2140 | |||
1964 | rc = 0; | 2141 | rc = 0; |
1965 | out: | 2142 | out: |
1966 | return rc; | 2143 | return rc; |
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 4253370fda6a..55152d498b53 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h | |||
@@ -61,6 +61,7 @@ struct class_datum { | |||
61 | /* Role attributes */ | 61 | /* Role attributes */ |
62 | struct role_datum { | 62 | struct role_datum { |
63 | u32 value; /* internal role value */ | 63 | u32 value; /* internal role value */ |
64 | u32 bounds; /* boundary of role */ | ||
64 | struct ebitmap dominates; /* set of roles dominated by this role */ | 65 | struct ebitmap dominates; /* set of roles dominated by this role */ |
65 | struct ebitmap types; /* set of authorized types for role */ | 66 | struct ebitmap types; /* set of authorized types for role */ |
66 | }; | 67 | }; |
@@ -81,12 +82,15 @@ struct role_allow { | |||
81 | /* Type attributes */ | 82 | /* Type attributes */ |
82 | struct type_datum { | 83 | struct type_datum { |
83 | u32 value; /* internal type value */ | 84 | u32 value; /* internal type value */ |
85 | u32 bounds; /* boundary of type */ | ||
84 | unsigned char primary; /* primary name? */ | 86 | unsigned char primary; /* primary name? */ |
87 | unsigned char attribute;/* attribute ?*/ | ||
85 | }; | 88 | }; |
86 | 89 | ||
87 | /* User attributes */ | 90 | /* User attributes */ |
88 | struct user_datum { | 91 | struct user_datum { |
89 | u32 value; /* internal user value */ | 92 | u32 value; /* internal user value */ |
93 | u32 bounds; /* bounds of user */ | ||
90 | struct ebitmap roles; /* set of authorized roles for user */ | 94 | struct ebitmap roles; /* set of authorized roles for user */ |
91 | struct mls_range range; /* MLS range (min - max) for user */ | 95 | struct mls_range range; /* MLS range (min - max) for user */ |
92 | struct mls_level dfltlevel; /* default login MLS level for user */ | 96 | struct mls_level dfltlevel; /* default login MLS level for user */ |
@@ -209,6 +213,7 @@ struct policydb { | |||
209 | struct class_datum **class_val_to_struct; | 213 | struct class_datum **class_val_to_struct; |
210 | struct role_datum **role_val_to_struct; | 214 | struct role_datum **role_val_to_struct; |
211 | struct user_datum **user_val_to_struct; | 215 | struct user_datum **user_val_to_struct; |
216 | struct type_datum **type_val_to_struct; | ||
212 | 217 | ||
213 | /* type enforcement access vectors and transitions */ | 218 | /* type enforcement access vectors and transitions */ |
214 | struct avtab te_avtab; | 219 | struct avtab te_avtab; |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 5a0536bddc63..4f233d9960e7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -88,6 +88,11 @@ static u32 latest_granting; | |||
88 | static int context_struct_to_string(struct context *context, char **scontext, | 88 | static int context_struct_to_string(struct context *context, char **scontext, |
89 | u32 *scontext_len); | 89 | u32 *scontext_len); |
90 | 90 | ||
91 | static int context_struct_compute_av(struct context *scontext, | ||
92 | struct context *tcontext, | ||
93 | u16 tclass, | ||
94 | u32 requested, | ||
95 | struct av_decision *avd); | ||
91 | /* | 96 | /* |
92 | * Return the boolean value of a constraint expression | 97 | * Return the boolean value of a constraint expression |
93 | * when it is applied to the specified source and target | 98 | * when it is applied to the specified source and target |
@@ -274,6 +279,100 @@ mls_ops: | |||
274 | } | 279 | } |
275 | 280 | ||
276 | /* | 281 | /* |
282 | * security_boundary_permission - drops violated permissions | ||
283 | * on boundary constraint. | ||
284 | */ | ||
285 | static void type_attribute_bounds_av(struct context *scontext, | ||
286 | struct context *tcontext, | ||
287 | u16 tclass, | ||
288 | u32 requested, | ||
289 | struct av_decision *avd) | ||
290 | { | ||
291 | struct context lo_scontext; | ||
292 | struct context lo_tcontext; | ||
293 | struct av_decision lo_avd; | ||
294 | struct type_datum *source | ||
295 | = policydb.type_val_to_struct[scontext->type - 1]; | ||
296 | struct type_datum *target | ||
297 | = policydb.type_val_to_struct[tcontext->type - 1]; | ||
298 | u32 masked = 0; | ||
299 | |||
300 | if (source->bounds) { | ||
301 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
302 | |||
303 | memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); | ||
304 | lo_scontext.type = source->bounds; | ||
305 | |||
306 | context_struct_compute_av(&lo_scontext, | ||
307 | tcontext, | ||
308 | tclass, | ||
309 | requested, | ||
310 | &lo_avd); | ||
311 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
312 | return; /* no masked permission */ | ||
313 | masked = ~lo_avd.allowed & avd->allowed; | ||
314 | } | ||
315 | |||
316 | if (target->bounds) { | ||
317 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
318 | |||
319 | memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); | ||
320 | lo_tcontext.type = target->bounds; | ||
321 | |||
322 | context_struct_compute_av(scontext, | ||
323 | &lo_tcontext, | ||
324 | tclass, | ||
325 | requested, | ||
326 | &lo_avd); | ||
327 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
328 | return; /* no masked permission */ | ||
329 | masked = ~lo_avd.allowed & avd->allowed; | ||
330 | } | ||
331 | |||
332 | if (source->bounds && target->bounds) { | ||
333 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
334 | /* | ||
335 | * lo_scontext and lo_tcontext are already | ||
336 | * set up. | ||
337 | */ | ||
338 | |||
339 | context_struct_compute_av(&lo_scontext, | ||
340 | &lo_tcontext, | ||
341 | tclass, | ||
342 | requested, | ||
343 | &lo_avd); | ||
344 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
345 | return; /* no masked permission */ | ||
346 | masked = ~lo_avd.allowed & avd->allowed; | ||
347 | } | ||
348 | |||
349 | if (masked) { | ||
350 | struct audit_buffer *ab; | ||
351 | char *stype_name | ||
352 | = policydb.p_type_val_to_name[source->value - 1]; | ||
353 | char *ttype_name | ||
354 | = policydb.p_type_val_to_name[target->value - 1]; | ||
355 | char *tclass_name | ||
356 | = policydb.p_class_val_to_name[tclass - 1]; | ||
357 | |||
358 | /* mask violated permissions */ | ||
359 | avd->allowed &= ~masked; | ||
360 | |||
361 | /* notice to userspace via audit message */ | ||
362 | ab = audit_log_start(current->audit_context, | ||
363 | GFP_ATOMIC, AUDIT_SELINUX_ERR); | ||
364 | if (!ab) | ||
365 | return; | ||
366 | |||
367 | audit_log_format(ab, "av boundary violation: " | ||
368 | "source=%s target=%s tclass=%s", | ||
369 | stype_name, ttype_name, tclass_name); | ||
370 | avc_dump_av(ab, tclass, masked); | ||
371 | audit_log_end(ab); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | /* | ||
277 | * Compute access vectors based on a context structure pair for | 376 | * Compute access vectors based on a context structure pair for |
278 | * the permissions in a particular class. | 377 | * the permissions in a particular class. |
279 | */ | 378 | */ |
@@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, | |||
404 | PROCESS__DYNTRANSITION); | 503 | PROCESS__DYNTRANSITION); |
405 | } | 504 | } |
406 | 505 | ||
506 | /* | ||
507 | * If the given source and target types have boundary | ||
508 | * constraint, lazy checks have to mask any violated | ||
509 | * permission and notice it to userspace via audit. | ||
510 | */ | ||
511 | type_attribute_bounds_av(scontext, tcontext, | ||
512 | tclass, requested, avd); | ||
513 | |||
407 | return 0; | 514 | return 0; |
408 | 515 | ||
409 | inval_class: | 516 | inval_class: |
@@ -549,6 +656,69 @@ out: | |||
549 | return rc; | 656 | return rc; |
550 | } | 657 | } |
551 | 658 | ||
659 | /* | ||
660 | * security_bounded_transition - check whether the given | ||
661 | * transition is directed to bounded, or not. | ||
662 | * It returns 0, if @newsid is bounded by @oldsid. | ||
663 | * Otherwise, it returns error code. | ||
664 | * | ||
665 | * @oldsid : current security identifier | ||
666 | * @newsid : destinated security identifier | ||
667 | */ | ||
668 | int security_bounded_transition(u32 old_sid, u32 new_sid) | ||
669 | { | ||
670 | struct context *old_context, *new_context; | ||
671 | struct type_datum *type; | ||
672 | int index; | ||
673 | int rc = -EINVAL; | ||
674 | |||
675 | read_lock(&policy_rwlock); | ||
676 | |||
677 | old_context = sidtab_search(&sidtab, old_sid); | ||
678 | if (!old_context) { | ||
679 | printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", | ||
680 | __func__, old_sid); | ||
681 | goto out; | ||
682 | } | ||
683 | |||
684 | new_context = sidtab_search(&sidtab, new_sid); | ||
685 | if (!new_context) { | ||
686 | printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", | ||
687 | __func__, new_sid); | ||
688 | goto out; | ||
689 | } | ||
690 | |||
691 | /* type/domain unchaned */ | ||
692 | if (old_context->type == new_context->type) { | ||
693 | rc = 0; | ||
694 | goto out; | ||
695 | } | ||
696 | |||
697 | index = new_context->type; | ||
698 | while (true) { | ||
699 | type = policydb.type_val_to_struct[index - 1]; | ||
700 | BUG_ON(!type); | ||
701 | |||
702 | /* not bounded anymore */ | ||
703 | if (!type->bounds) { | ||
704 | rc = -EPERM; | ||
705 | break; | ||
706 | } | ||
707 | |||
708 | /* @newsid is bounded by @oldsid */ | ||
709 | if (type->bounds == old_context->type) { | ||
710 | rc = 0; | ||
711 | break; | ||
712 | } | ||
713 | index = type->bounds; | ||
714 | } | ||
715 | out: | ||
716 | read_unlock(&policy_rwlock); | ||
717 | |||
718 | return rc; | ||
719 | } | ||
720 | |||
721 | |||
552 | /** | 722 | /** |
553 | * security_compute_av - Compute access vector decisions. | 723 | * security_compute_av - Compute access vector decisions. |
554 | * @ssid: source security identifier | 724 | * @ssid: source security identifier |
@@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, | |||
794 | *p++ = 0; | 964 | *p++ = 0; |
795 | 965 | ||
796 | typdatum = hashtab_search(pol->p_types.table, scontextp); | 966 | typdatum = hashtab_search(pol->p_types.table, scontextp); |
797 | if (!typdatum) | 967 | if (!typdatum || typdatum->attribute) |
798 | goto out; | 968 | goto out; |
799 | 969 | ||
800 | ctx->type = typdatum->value; | 970 | ctx->type = typdatum->value; |