diff options
| author | James Morris <james.l.morris@oracle.com> | 2015-08-14 23:29:57 -0400 |
|---|---|---|
| committer | James Morris <james.l.morris@oracle.com> | 2015-08-14 23:29:57 -0400 |
| commit | 3e5f206c00f73f535c914eedc8b91f424c5a14ab (patch) | |
| tree | 209f621fc8a9b84053bb4feda619185e17242982 /security | |
| parent | 0e38c35815f50e5a347977d76fb5eb4c3bf020b5 (diff) | |
| parent | fda4d578ed0a7e1d116f56a15efea0e4ba78acad (diff) | |
Merge branch 'next' of git://git.infradead.org/users/pcmoore/selinux into next
Diffstat (limited to 'security')
| -rw-r--r-- | security/lsm_audit.c | 15 | ||||
| -rw-r--r-- | security/selinux/avc.c | 418 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 147 | ||||
| -rw-r--r-- | security/selinux/include/avc.h | 6 | ||||
| -rw-r--r-- | security/selinux/include/security.h | 32 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.c | 104 | ||||
| -rw-r--r-- | security/selinux/ss/avtab.h | 33 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.c | 32 | ||||
| -rw-r--r-- | security/selinux/ss/conditional.h | 6 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 5 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 213 | ||||
| -rw-r--r-- | security/selinux/ss/services.h | 6 |
12 files changed, 907 insertions, 110 deletions
diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 4ed98107ace3..cccbf3068cdc 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c | |||
| @@ -245,6 +245,21 @@ static void dump_common_audit_data(struct audit_buffer *ab, | |||
| 245 | } | 245 | } |
| 246 | break; | 246 | break; |
| 247 | } | 247 | } |
| 248 | case LSM_AUDIT_DATA_IOCTL_OP: { | ||
| 249 | struct inode *inode; | ||
| 250 | |||
| 251 | audit_log_d_path(ab, " path=", &a->u.op->path); | ||
| 252 | |||
| 253 | inode = a->u.op->path.dentry->d_inode; | ||
| 254 | if (inode) { | ||
| 255 | audit_log_format(ab, " dev="); | ||
| 256 | audit_log_untrustedstring(ab, inode->i_sb->s_id); | ||
| 257 | audit_log_format(ab, " ino=%lu", inode->i_ino); | ||
| 258 | } | ||
| 259 | |||
| 260 | audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd); | ||
| 261 | break; | ||
| 262 | } | ||
| 248 | case LSM_AUDIT_DATA_DENTRY: { | 263 | case LSM_AUDIT_DATA_DENTRY: { |
| 249 | struct inode *inode; | 264 | struct inode *inode; |
| 250 | 265 | ||
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 0b122b1421a9..e60c79de13e1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 23 | #include <linux/skbuff.h> | 23 | #include <linux/skbuff.h> |
| 24 | #include <linux/percpu.h> | 24 | #include <linux/percpu.h> |
| 25 | #include <linux/list.h> | ||
| 25 | #include <net/sock.h> | 26 | #include <net/sock.h> |
| 26 | #include <linux/un.h> | 27 | #include <linux/un.h> |
| 27 | #include <net/af_unix.h> | 28 | #include <net/af_unix.h> |
| @@ -48,6 +49,7 @@ struct avc_entry { | |||
| 48 | u32 tsid; | 49 | u32 tsid; |
| 49 | u16 tclass; | 50 | u16 tclass; |
| 50 | struct av_decision avd; | 51 | struct av_decision avd; |
| 52 | struct avc_xperms_node *xp_node; | ||
| 51 | }; | 53 | }; |
| 52 | 54 | ||
| 53 | struct avc_node { | 55 | struct avc_node { |
| @@ -56,6 +58,16 @@ struct avc_node { | |||
| 56 | struct rcu_head rhead; | 58 | struct rcu_head rhead; |
| 57 | }; | 59 | }; |
| 58 | 60 | ||
| 61 | struct avc_xperms_decision_node { | ||
| 62 | struct extended_perms_decision xpd; | ||
| 63 | struct list_head xpd_list; /* list of extended_perms_decision */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct avc_xperms_node { | ||
| 67 | struct extended_perms xp; | ||
| 68 | struct list_head xpd_head; /* list head of extended_perms_decision */ | ||
| 69 | }; | ||
| 70 | |||
| 59 | struct avc_cache { | 71 | struct avc_cache { |
| 60 | struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ | 72 | struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */ |
| 61 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ | 73 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ |
| @@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; | |||
| 80 | static struct avc_cache avc_cache; | 92 | static struct avc_cache avc_cache; |
| 81 | static struct avc_callback_node *avc_callbacks; | 93 | static struct avc_callback_node *avc_callbacks; |
| 82 | static struct kmem_cache *avc_node_cachep; | 94 | static struct kmem_cache *avc_node_cachep; |
| 95 | static struct kmem_cache *avc_xperms_data_cachep; | ||
| 96 | static struct kmem_cache *avc_xperms_decision_cachep; | ||
| 97 | static struct kmem_cache *avc_xperms_cachep; | ||
| 83 | 98 | ||
| 84 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) | 99 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) |
| 85 | { | 100 | { |
| @@ -101,6 +116,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) | |||
| 101 | return; | 116 | return; |
| 102 | } | 117 | } |
| 103 | 118 | ||
| 119 | BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); | ||
| 104 | perms = secclass_map[tclass-1].perms; | 120 | perms = secclass_map[tclass-1].perms; |
| 105 | 121 | ||
| 106 | audit_log_format(ab, " {"); | 122 | audit_log_format(ab, " {"); |
| @@ -149,7 +165,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla | |||
| 149 | kfree(scontext); | 165 | kfree(scontext); |
| 150 | } | 166 | } |
| 151 | 167 | ||
| 152 | BUG_ON(tclass >= ARRAY_SIZE(secclass_map)); | 168 | BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); |
| 153 | audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); | 169 | audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); |
| 154 | } | 170 | } |
| 155 | 171 | ||
| @@ -170,7 +186,17 @@ void __init avc_init(void) | |||
| 170 | atomic_set(&avc_cache.lru_hint, 0); | 186 | atomic_set(&avc_cache.lru_hint, 0); |
| 171 | 187 | ||
| 172 | avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), | 188 | avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), |
| 173 | 0, SLAB_PANIC, NULL); | 189 | 0, SLAB_PANIC, NULL); |
| 190 | avc_xperms_cachep = kmem_cache_create("avc_xperms_node", | ||
| 191 | sizeof(struct avc_xperms_node), | ||
| 192 | 0, SLAB_PANIC, NULL); | ||
| 193 | avc_xperms_decision_cachep = kmem_cache_create( | ||
| 194 | "avc_xperms_decision_node", | ||
| 195 | sizeof(struct avc_xperms_decision_node), | ||
| 196 | 0, SLAB_PANIC, NULL); | ||
| 197 | avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data", | ||
| 198 | sizeof(struct extended_perms_data), | ||
| 199 | 0, SLAB_PANIC, NULL); | ||
| 174 | 200 | ||
| 175 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); | 201 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n"); |
| 176 | } | 202 | } |
| @@ -205,9 +231,261 @@ int avc_get_hash_stats(char *page) | |||
| 205 | slots_used, AVC_CACHE_SLOTS, max_chain_len); | 231 | slots_used, AVC_CACHE_SLOTS, max_chain_len); |
| 206 | } | 232 | } |
| 207 | 233 | ||
| 234 | /* | ||
| 235 | * using a linked list for extended_perms_decision lookup because the list is | ||
| 236 | * always small. i.e. less than 5, typically 1 | ||
| 237 | */ | ||
| 238 | static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver, | ||
| 239 | struct avc_xperms_node *xp_node) | ||
| 240 | { | ||
| 241 | struct avc_xperms_decision_node *xpd_node; | ||
| 242 | |||
| 243 | list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) { | ||
| 244 | if (xpd_node->xpd.driver == driver) | ||
| 245 | return &xpd_node->xpd; | ||
| 246 | } | ||
| 247 | return NULL; | ||
| 248 | } | ||
| 249 | |||
| 250 | static inline unsigned int | ||
| 251 | avc_xperms_has_perm(struct extended_perms_decision *xpd, | ||
| 252 | u8 perm, u8 which) | ||
| 253 | { | ||
| 254 | unsigned int rc = 0; | ||
| 255 | |||
| 256 | if ((which == XPERMS_ALLOWED) && | ||
| 257 | (xpd->used & XPERMS_ALLOWED)) | ||
| 258 | rc = security_xperm_test(xpd->allowed->p, perm); | ||
| 259 | else if ((which == XPERMS_AUDITALLOW) && | ||
| 260 | (xpd->used & XPERMS_AUDITALLOW)) | ||
| 261 | rc = security_xperm_test(xpd->auditallow->p, perm); | ||
| 262 | else if ((which == XPERMS_DONTAUDIT) && | ||
| 263 | (xpd->used & XPERMS_DONTAUDIT)) | ||
| 264 | rc = security_xperm_test(xpd->dontaudit->p, perm); | ||
| 265 | return rc; | ||
| 266 | } | ||
| 267 | |||
| 268 | static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node, | ||
| 269 | u8 driver, u8 perm) | ||
| 270 | { | ||
| 271 | struct extended_perms_decision *xpd; | ||
| 272 | security_xperm_set(xp_node->xp.drivers.p, driver); | ||
| 273 | xpd = avc_xperms_decision_lookup(driver, xp_node); | ||
| 274 | if (xpd && xpd->allowed) | ||
| 275 | security_xperm_set(xpd->allowed->p, perm); | ||
| 276 | } | ||
| 277 | |||
| 278 | static void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node) | ||
| 279 | { | ||
| 280 | struct extended_perms_decision *xpd; | ||
| 281 | |||
| 282 | xpd = &xpd_node->xpd; | ||
| 283 | if (xpd->allowed) | ||
| 284 | kmem_cache_free(avc_xperms_data_cachep, xpd->allowed); | ||
| 285 | if (xpd->auditallow) | ||
| 286 | kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow); | ||
| 287 | if (xpd->dontaudit) | ||
| 288 | kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit); | ||
| 289 | kmem_cache_free(avc_xperms_decision_cachep, xpd_node); | ||
| 290 | } | ||
| 291 | |||
| 292 | static void avc_xperms_free(struct avc_xperms_node *xp_node) | ||
| 293 | { | ||
| 294 | struct avc_xperms_decision_node *xpd_node, *tmp; | ||
| 295 | |||
| 296 | if (!xp_node) | ||
| 297 | return; | ||
| 298 | |||
| 299 | list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) { | ||
| 300 | list_del(&xpd_node->xpd_list); | ||
| 301 | avc_xperms_decision_free(xpd_node); | ||
| 302 | } | ||
| 303 | kmem_cache_free(avc_xperms_cachep, xp_node); | ||
| 304 | } | ||
| 305 | |||
| 306 | static void avc_copy_xperms_decision(struct extended_perms_decision *dest, | ||
| 307 | struct extended_perms_decision *src) | ||
| 308 | { | ||
| 309 | dest->driver = src->driver; | ||
| 310 | dest->used = src->used; | ||
| 311 | if (dest->used & XPERMS_ALLOWED) | ||
| 312 | memcpy(dest->allowed->p, src->allowed->p, | ||
| 313 | sizeof(src->allowed->p)); | ||
| 314 | if (dest->used & XPERMS_AUDITALLOW) | ||
| 315 | memcpy(dest->auditallow->p, src->auditallow->p, | ||
| 316 | sizeof(src->auditallow->p)); | ||
| 317 | if (dest->used & XPERMS_DONTAUDIT) | ||
| 318 | memcpy(dest->dontaudit->p, src->dontaudit->p, | ||
| 319 | sizeof(src->dontaudit->p)); | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * similar to avc_copy_xperms_decision, but only copy decision | ||
| 324 | * information relevant to this perm | ||
| 325 | */ | ||
| 326 | static inline void avc_quick_copy_xperms_decision(u8 perm, | ||
| 327 | struct extended_perms_decision *dest, | ||
| 328 | struct extended_perms_decision *src) | ||
| 329 | { | ||
| 330 | /* | ||
| 331 | * compute index of the u32 of the 256 bits (8 u32s) that contain this | ||
| 332 | * command permission | ||
| 333 | */ | ||
| 334 | u8 i = perm >> 5; | ||
| 335 | |||
| 336 | dest->used = src->used; | ||
| 337 | if (dest->used & XPERMS_ALLOWED) | ||
| 338 | dest->allowed->p[i] = src->allowed->p[i]; | ||
| 339 | if (dest->used & XPERMS_AUDITALLOW) | ||
| 340 | dest->auditallow->p[i] = src->auditallow->p[i]; | ||
| 341 | if (dest->used & XPERMS_DONTAUDIT) | ||
| 342 | dest->dontaudit->p[i] = src->dontaudit->p[i]; | ||
| 343 | } | ||
| 344 | |||
| 345 | static struct avc_xperms_decision_node | ||
| 346 | *avc_xperms_decision_alloc(u8 which) | ||
| 347 | { | ||
| 348 | struct avc_xperms_decision_node *xpd_node; | ||
| 349 | struct extended_perms_decision *xpd; | ||
| 350 | |||
| 351 | xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, | ||
| 352 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
| 353 | if (!xpd_node) | ||
| 354 | return NULL; | ||
| 355 | |||
| 356 | xpd = &xpd_node->xpd; | ||
| 357 | if (which & XPERMS_ALLOWED) { | ||
| 358 | xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
| 359 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
| 360 | if (!xpd->allowed) | ||
| 361 | goto error; | ||
| 362 | } | ||
| 363 | if (which & XPERMS_AUDITALLOW) { | ||
| 364 | xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
| 365 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
| 366 | if (!xpd->auditallow) | ||
| 367 | goto error; | ||
| 368 | } | ||
| 369 | if (which & XPERMS_DONTAUDIT) { | ||
| 370 | xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep, | ||
| 371 | GFP_ATOMIC | __GFP_NOMEMALLOC); | ||
| 372 | if (!xpd->dontaudit) | ||
| 373 | goto error; | ||
| 374 | } | ||
| 375 | return xpd_node; | ||
| 376 | error: | ||
| 377 | avc_xperms_decision_free(xpd_node); | ||
| 378 | return NULL; | ||
| 379 | } | ||
| 380 | |||
| 381 | static int avc_add_xperms_decision(struct avc_node *node, | ||
| 382 | struct extended_perms_decision *src) | ||
| 383 | { | ||
| 384 | struct avc_xperms_decision_node *dest_xpd; | ||
| 385 | |||
| 386 | node->ae.xp_node->xp.len++; | ||
| 387 | dest_xpd = avc_xperms_decision_alloc(src->used); | ||
| 388 | if (!dest_xpd) | ||
| 389 | return -ENOMEM; | ||
| 390 | avc_copy_xperms_decision(&dest_xpd->xpd, src); | ||
| 391 | list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head); | ||
| 392 | return 0; | ||
| 393 | } | ||
| 394 | |||
| 395 | static struct avc_xperms_node *avc_xperms_alloc(void) | ||
| 396 | { | ||
| 397 | struct avc_xperms_node *xp_node; | ||
| 398 | |||
| 399 | xp_node = kmem_cache_zalloc(avc_xperms_cachep, | ||
| 400 | GFP_ATOMIC|__GFP_NOMEMALLOC); | ||
| 401 | if (!xp_node) | ||
| 402 | return xp_node; | ||
| 403 | INIT_LIST_HEAD(&xp_node->xpd_head); | ||
| 404 | return xp_node; | ||
| 405 | } | ||
| 406 | |||
| 407 | static int avc_xperms_populate(struct avc_node *node, | ||
| 408 | struct avc_xperms_node *src) | ||
| 409 | { | ||
| 410 | struct avc_xperms_node *dest; | ||
| 411 | struct avc_xperms_decision_node *dest_xpd; | ||
| 412 | struct avc_xperms_decision_node *src_xpd; | ||
| 413 | |||
| 414 | if (src->xp.len == 0) | ||
| 415 | return 0; | ||
| 416 | dest = avc_xperms_alloc(); | ||
| 417 | if (!dest) | ||
| 418 | return -ENOMEM; | ||
| 419 | |||
| 420 | memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p)); | ||
| 421 | dest->xp.len = src->xp.len; | ||
| 422 | |||
| 423 | /* for each source xpd allocate a destination xpd and copy */ | ||
| 424 | list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) { | ||
| 425 | dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used); | ||
| 426 | if (!dest_xpd) | ||
| 427 | goto error; | ||
| 428 | avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd); | ||
| 429 | list_add(&dest_xpd->xpd_list, &dest->xpd_head); | ||
| 430 | } | ||
| 431 | node->ae.xp_node = dest; | ||
| 432 | return 0; | ||
| 433 | error: | ||
| 434 | avc_xperms_free(dest); | ||
| 435 | return -ENOMEM; | ||
| 436 | |||
| 437 | } | ||
| 438 | |||
| 439 | static inline u32 avc_xperms_audit_required(u32 requested, | ||
| 440 | struct av_decision *avd, | ||
| 441 | struct extended_perms_decision *xpd, | ||
| 442 | u8 perm, | ||
| 443 | int result, | ||
| 444 | u32 *deniedp) | ||
| 445 | { | ||
| 446 | u32 denied, audited; | ||
| 447 | |||
| 448 | denied = requested & ~avd->allowed; | ||
| 449 | if (unlikely(denied)) { | ||
| 450 | audited = denied & avd->auditdeny; | ||
| 451 | if (audited && xpd) { | ||
| 452 | if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT)) | ||
| 453 | audited &= ~requested; | ||
| 454 | } | ||
| 455 | } else if (result) { | ||
| 456 | audited = denied = requested; | ||
| 457 | } else { | ||
| 458 | audited = requested & avd->auditallow; | ||
| 459 | if (audited && xpd) { | ||
| 460 | if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW)) | ||
| 461 | audited &= ~requested; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | *deniedp = denied; | ||
| 466 | return audited; | ||
| 467 | } | ||
| 468 | |||
| 469 | static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass, | ||
| 470 | u32 requested, struct av_decision *avd, | ||
| 471 | struct extended_perms_decision *xpd, | ||
| 472 | u8 perm, int result, | ||
| 473 | struct common_audit_data *ad) | ||
| 474 | { | ||
| 475 | u32 audited, denied; | ||
| 476 | |||
| 477 | audited = avc_xperms_audit_required( | ||
| 478 | requested, avd, xpd, perm, result, &denied); | ||
| 479 | if (likely(!audited)) | ||
| 480 | return 0; | ||
| 481 | return slow_avc_audit(ssid, tsid, tclass, requested, | ||
| 482 | audited, denied, result, ad, 0); | ||
| 483 | } | ||
| 484 | |||
| 208 | static void avc_node_free(struct rcu_head *rhead) | 485 | static void avc_node_free(struct rcu_head *rhead) |
| 209 | { | 486 | { |
| 210 | struct avc_node *node = container_of(rhead, struct avc_node, rhead); | 487 | struct avc_node *node = container_of(rhead, struct avc_node, rhead); |
| 488 | avc_xperms_free(node->ae.xp_node); | ||
| 211 | kmem_cache_free(avc_node_cachep, node); | 489 | kmem_cache_free(avc_node_cachep, node); |
| 212 | avc_cache_stats_incr(frees); | 490 | avc_cache_stats_incr(frees); |
| 213 | } | 491 | } |
| @@ -221,6 +499,7 @@ static void avc_node_delete(struct avc_node *node) | |||
| 221 | 499 | ||
| 222 | static void avc_node_kill(struct avc_node *node) | 500 | static void avc_node_kill(struct avc_node *node) |
| 223 | { | 501 | { |
| 502 | avc_xperms_free(node->ae.xp_node); | ||
| 224 | kmem_cache_free(avc_node_cachep, node); | 503 | kmem_cache_free(avc_node_cachep, node); |
| 225 | avc_cache_stats_incr(frees); | 504 | avc_cache_stats_incr(frees); |
| 226 | atomic_dec(&avc_cache.active_nodes); | 505 | atomic_dec(&avc_cache.active_nodes); |
| @@ -367,6 +646,7 @@ static int avc_latest_notif_update(int seqno, int is_insert) | |||
| 367 | * @tsid: target security identifier | 646 | * @tsid: target security identifier |
| 368 | * @tclass: target security class | 647 | * @tclass: target security class |
| 369 | * @avd: resulting av decision | 648 | * @avd: resulting av decision |
| 649 | * @xp_node: resulting extended permissions | ||
| 370 | * | 650 | * |
| 371 | * Insert an AVC entry for the SID pair | 651 | * Insert an AVC entry for the SID pair |
| 372 | * (@ssid, @tsid) and class @tclass. | 652 | * (@ssid, @tsid) and class @tclass. |
| @@ -378,7 +658,9 @@ static int avc_latest_notif_update(int seqno, int is_insert) | |||
| 378 | * the access vectors into a cache entry, returns | 658 | * the access vectors into a cache entry, returns |
| 379 | * avc_node inserted. Otherwise, this function returns NULL. | 659 | * avc_node inserted. Otherwise, this function returns NULL. |
| 380 | */ | 660 | */ |
| 381 | static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd) | 661 | static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, |
| 662 | struct av_decision *avd, | ||
| 663 | struct avc_xperms_node *xp_node) | ||
| 382 | { | 664 | { |
| 383 | struct avc_node *pos, *node = NULL; | 665 | struct avc_node *pos, *node = NULL; |
| 384 | int hvalue; | 666 | int hvalue; |
| @@ -391,10 +673,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec | |||
| 391 | if (node) { | 673 | if (node) { |
| 392 | struct hlist_head *head; | 674 | struct hlist_head *head; |
| 393 | spinlock_t *lock; | 675 | spinlock_t *lock; |
| 676 | int rc = 0; | ||
| 394 | 677 | ||
| 395 | hvalue = avc_hash(ssid, tsid, tclass); | 678 | hvalue = avc_hash(ssid, tsid, tclass); |
| 396 | avc_node_populate(node, ssid, tsid, tclass, avd); | 679 | avc_node_populate(node, ssid, tsid, tclass, avd); |
| 397 | 680 | rc = avc_xperms_populate(node, xp_node); | |
| 681 | if (rc) { | ||
| 682 | kmem_cache_free(avc_node_cachep, node); | ||
| 683 | return NULL; | ||
| 684 | } | ||
| 398 | head = &avc_cache.slots[hvalue]; | 685 | head = &avc_cache.slots[hvalue]; |
| 399 | lock = &avc_cache.slots_lock[hvalue]; | 686 | lock = &avc_cache.slots_lock[hvalue]; |
| 400 | 687 | ||
| @@ -523,14 +810,17 @@ out: | |||
| 523 | * @perms : Permission mask bits | 810 | * @perms : Permission mask bits |
| 524 | * @ssid,@tsid,@tclass : identifier of an AVC entry | 811 | * @ssid,@tsid,@tclass : identifier of an AVC entry |
| 525 | * @seqno : sequence number when decision was made | 812 | * @seqno : sequence number when decision was made |
| 813 | * @xpd: extended_perms_decision to be added to the node | ||
| 526 | * | 814 | * |
| 527 | * if a valid AVC entry doesn't exist,this function returns -ENOENT. | 815 | * if a valid AVC entry doesn't exist,this function returns -ENOENT. |
| 528 | * if kmalloc() called internal returns NULL, this function returns -ENOMEM. | 816 | * if kmalloc() called internal returns NULL, this function returns -ENOMEM. |
| 529 | * otherwise, this function updates the AVC entry. The original AVC-entry object | 817 | * otherwise, this function updates the AVC entry. The original AVC-entry object |
| 530 | * will release later by RCU. | 818 | * will release later by RCU. |
| 531 | */ | 819 | */ |
| 532 | static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | 820 | static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid, |
| 533 | u32 seqno) | 821 | u32 tsid, u16 tclass, u32 seqno, |
| 822 | struct extended_perms_decision *xpd, | ||
| 823 | u32 flags) | ||
| 534 | { | 824 | { |
| 535 | int hvalue, rc = 0; | 825 | int hvalue, rc = 0; |
| 536 | unsigned long flag; | 826 | unsigned long flag; |
| @@ -574,9 +864,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
| 574 | 864 | ||
| 575 | avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); | 865 | avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd); |
| 576 | 866 | ||
| 867 | if (orig->ae.xp_node) { | ||
| 868 | rc = avc_xperms_populate(node, orig->ae.xp_node); | ||
| 869 | if (rc) { | ||
| 870 | kmem_cache_free(avc_node_cachep, node); | ||
| 871 | goto out_unlock; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | |||
| 577 | switch (event) { | 875 | switch (event) { |
| 578 | case AVC_CALLBACK_GRANT: | 876 | case AVC_CALLBACK_GRANT: |
| 579 | node->ae.avd.allowed |= perms; | 877 | node->ae.avd.allowed |= perms; |
| 878 | if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS)) | ||
| 879 | avc_xperms_allow_perm(node->ae.xp_node, driver, xperm); | ||
| 580 | break; | 880 | break; |
| 581 | case AVC_CALLBACK_TRY_REVOKE: | 881 | case AVC_CALLBACK_TRY_REVOKE: |
| 582 | case AVC_CALLBACK_REVOKE: | 882 | case AVC_CALLBACK_REVOKE: |
| @@ -594,6 +894,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass, | |||
| 594 | case AVC_CALLBACK_AUDITDENY_DISABLE: | 894 | case AVC_CALLBACK_AUDITDENY_DISABLE: |
| 595 | node->ae.avd.auditdeny &= ~perms; | 895 | node->ae.avd.auditdeny &= ~perms; |
| 596 | break; | 896 | break; |
| 897 | case AVC_CALLBACK_ADD_XPERMS: | ||
| 898 | avc_add_xperms_decision(node, xpd); | ||
| 899 | break; | ||
| 597 | } | 900 | } |
| 598 | avc_node_replace(node, orig); | 901 | avc_node_replace(node, orig); |
| 599 | out_unlock: | 902 | out_unlock: |
| @@ -665,18 +968,20 @@ int avc_ss_reset(u32 seqno) | |||
| 665 | * results in a bigger stack frame. | 968 | * results in a bigger stack frame. |
| 666 | */ | 969 | */ |
| 667 | static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, | 970 | static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid, |
| 668 | u16 tclass, struct av_decision *avd) | 971 | u16 tclass, struct av_decision *avd, |
| 972 | struct avc_xperms_node *xp_node) | ||
| 669 | { | 973 | { |
| 670 | rcu_read_unlock(); | 974 | rcu_read_unlock(); |
| 671 | security_compute_av(ssid, tsid, tclass, avd); | 975 | INIT_LIST_HEAD(&xp_node->xpd_head); |
| 976 | security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp); | ||
| 672 | rcu_read_lock(); | 977 | rcu_read_lock(); |
| 673 | return avc_insert(ssid, tsid, tclass, avd); | 978 | return avc_insert(ssid, tsid, tclass, avd, xp_node); |
| 674 | } | 979 | } |
| 675 | 980 | ||
| 676 | static noinline int avc_denied(u32 ssid, u32 tsid, | 981 | static noinline int avc_denied(u32 ssid, u32 tsid, |
| 677 | u16 tclass, u32 requested, | 982 | u16 tclass, u32 requested, |
| 678 | unsigned flags, | 983 | u8 driver, u8 xperm, unsigned flags, |
| 679 | struct av_decision *avd) | 984 | struct av_decision *avd) |
| 680 | { | 985 | { |
| 681 | if (flags & AVC_STRICT) | 986 | if (flags & AVC_STRICT) |
| 682 | return -EACCES; | 987 | return -EACCES; |
| @@ -684,11 +989,91 @@ static noinline int avc_denied(u32 ssid, u32 tsid, | |||
| 684 | if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) | 989 | if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE)) |
| 685 | return -EACCES; | 990 | return -EACCES; |
| 686 | 991 | ||
| 687 | avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, | 992 | avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid, |
| 688 | tsid, tclass, avd->seqno); | 993 | tsid, tclass, avd->seqno, NULL, flags); |
| 689 | return 0; | 994 | return 0; |
| 690 | } | 995 | } |
| 691 | 996 | ||
| 997 | /* | ||
| 998 | * The avc extended permissions logic adds an additional 256 bits of | ||
| 999 | * permissions to an avc node when extended permissions for that node are | ||
| 1000 | * specified in the avtab. If the additional 256 permissions is not adequate, | ||
| 1001 | * as-is the case with ioctls, then multiple may be chained together and the | ||
| 1002 | * driver field is used to specify which set contains the permission. | ||
| 1003 | */ | ||
| 1004 | int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, | ||
| 1005 | u8 driver, u8 xperm, struct common_audit_data *ad) | ||
| 1006 | { | ||
| 1007 | struct avc_node *node; | ||
| 1008 | struct av_decision avd; | ||
| 1009 | u32 denied; | ||
| 1010 | struct extended_perms_decision local_xpd; | ||
| 1011 | struct extended_perms_decision *xpd = NULL; | ||
| 1012 | struct extended_perms_data allowed; | ||
| 1013 | struct extended_perms_data auditallow; | ||
| 1014 | struct extended_perms_data dontaudit; | ||
| 1015 | struct avc_xperms_node local_xp_node; | ||
| 1016 | struct avc_xperms_node *xp_node; | ||
| 1017 | int rc = 0, rc2; | ||
| 1018 | |||
| 1019 | xp_node = &local_xp_node; | ||
| 1020 | BUG_ON(!requested); | ||
| 1021 | |||
| 1022 | rcu_read_lock(); | ||
| 1023 | |||
| 1024 | node = avc_lookup(ssid, tsid, tclass); | ||
| 1025 | if (unlikely(!node)) { | ||
| 1026 | node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node); | ||
| 1027 | } else { | ||
| 1028 | memcpy(&avd, &node->ae.avd, sizeof(avd)); | ||
| 1029 | xp_node = node->ae.xp_node; | ||
| 1030 | } | ||
| 1031 | /* if extended permissions are not defined, only consider av_decision */ | ||
| 1032 | if (!xp_node || !xp_node->xp.len) | ||
| 1033 | goto decision; | ||
| 1034 | |||
| 1035 | local_xpd.allowed = &allowed; | ||
| 1036 | local_xpd.auditallow = &auditallow; | ||
| 1037 | local_xpd.dontaudit = &dontaudit; | ||
| 1038 | |||
| 1039 | xpd = avc_xperms_decision_lookup(driver, xp_node); | ||
| 1040 | if (unlikely(!xpd)) { | ||
| 1041 | /* | ||
| 1042 | * Compute the extended_perms_decision only if the driver | ||
| 1043 | * is flagged | ||
| 1044 | */ | ||
| 1045 | if (!security_xperm_test(xp_node->xp.drivers.p, driver)) { | ||
| 1046 | avd.allowed &= ~requested; | ||
| 1047 | goto decision; | ||
| 1048 | } | ||
| 1049 | rcu_read_unlock(); | ||
| 1050 | security_compute_xperms_decision(ssid, tsid, tclass, driver, | ||
| 1051 | &local_xpd); | ||
| 1052 | rcu_read_lock(); | ||
| 1053 | avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm, | ||
| 1054 | ssid, tsid, tclass, avd.seqno, &local_xpd, 0); | ||
| 1055 | } else { | ||
| 1056 | avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd); | ||
| 1057 | } | ||
| 1058 | xpd = &local_xpd; | ||
| 1059 | |||
| 1060 | if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED)) | ||
| 1061 | avd.allowed &= ~requested; | ||
| 1062 | |||
| 1063 | decision: | ||
| 1064 | denied = requested & ~(avd.allowed); | ||
| 1065 | if (unlikely(denied)) | ||
| 1066 | rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm, | ||
| 1067 | AVC_EXTENDED_PERMS, &avd); | ||
| 1068 | |||
| 1069 | rcu_read_unlock(); | ||
| 1070 | |||
| 1071 | rc2 = avc_xperms_audit(ssid, tsid, tclass, requested, | ||
| 1072 | &avd, xpd, xperm, rc, ad); | ||
| 1073 | if (rc2) | ||
| 1074 | return rc2; | ||
| 1075 | return rc; | ||
| 1076 | } | ||
| 692 | 1077 | ||
| 693 | /** | 1078 | /** |
| 694 | * avc_has_perm_noaudit - Check permissions but perform no auditing. | 1079 | * avc_has_perm_noaudit - Check permissions but perform no auditing. |
| @@ -716,6 +1101,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 716 | struct av_decision *avd) | 1101 | struct av_decision *avd) |
| 717 | { | 1102 | { |
| 718 | struct avc_node *node; | 1103 | struct avc_node *node; |
| 1104 | struct avc_xperms_node xp_node; | ||
| 719 | int rc = 0; | 1105 | int rc = 0; |
| 720 | u32 denied; | 1106 | u32 denied; |
| 721 | 1107 | ||
| @@ -725,13 +1111,13 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
| 725 | 1111 | ||
| 726 | node = avc_lookup(ssid, tsid, tclass); | 1112 | node = avc_lookup(ssid, tsid, tclass); |
| 727 | if (unlikely(!node)) | 1113 | if (unlikely(!node)) |
| 728 | node = avc_compute_av(ssid, tsid, tclass, avd); | 1114 | node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node); |
| 729 | else | 1115 | else |
| 730 | memcpy(avd, &node->ae.avd, sizeof(*avd)); | 1116 | memcpy(avd, &node->ae.avd, sizeof(*avd)); |
| 731 | 1117 | ||
| 732 | denied = requested & ~(avd->allowed); | 1118 | denied = requested & ~(avd->allowed); |
| 733 | if (unlikely(denied)) | 1119 | if (unlikely(denied)) |
| 734 | rc = avc_denied(ssid, tsid, tclass, requested, flags, avd); | 1120 | rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd); |
| 735 | 1121 | ||
| 736 | rcu_read_unlock(); | 1122 | rcu_read_unlock(); |
| 737 | return rc; | 1123 | return rc; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 564079c5c49d..55285054aa73 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -254,10 +254,21 @@ static void inode_free_security(struct inode *inode) | |||
| 254 | struct inode_security_struct *isec = inode->i_security; | 254 | struct inode_security_struct *isec = inode->i_security; |
| 255 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; | 255 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; |
| 256 | 256 | ||
| 257 | spin_lock(&sbsec->isec_lock); | 257 | /* |
| 258 | if (!list_empty(&isec->list)) | 258 | * As not all inode security structures are in a list, we check for |
| 259 | * empty list outside of the lock to make sure that we won't waste | ||
| 260 | * time taking a lock doing nothing. | ||
| 261 | * | ||
| 262 | * The list_del_init() function can be safely called more than once. | ||
| 263 | * It should not be possible for this function to be called with | ||
| 264 | * concurrent list_add(), but for better safety against future changes | ||
| 265 | * in the code, we use list_empty_careful() here. | ||
| 266 | */ | ||
| 267 | if (!list_empty_careful(&isec->list)) { | ||
| 268 | spin_lock(&sbsec->isec_lock); | ||
| 259 | list_del_init(&isec->list); | 269 | list_del_init(&isec->list); |
| 260 | spin_unlock(&sbsec->isec_lock); | 270 | spin_unlock(&sbsec->isec_lock); |
| 271 | } | ||
| 261 | 272 | ||
| 262 | /* | 273 | /* |
| 263 | * The inode may still be referenced in a path walk and | 274 | * The inode may still be referenced in a path walk and |
| @@ -1698,6 +1709,32 @@ out: | |||
| 1698 | return rc; | 1709 | return rc; |
| 1699 | } | 1710 | } |
| 1700 | 1711 | ||
| 1712 | /* | ||
| 1713 | * Determine the label for an inode that might be unioned. | ||
| 1714 | */ | ||
| 1715 | static int selinux_determine_inode_label(const struct inode *dir, | ||
| 1716 | const struct qstr *name, | ||
| 1717 | u16 tclass, | ||
| 1718 | u32 *_new_isid) | ||
| 1719 | { | ||
| 1720 | const struct superblock_security_struct *sbsec = dir->i_sb->s_security; | ||
| 1721 | const struct inode_security_struct *dsec = dir->i_security; | ||
| 1722 | const struct task_security_struct *tsec = current_security(); | ||
| 1723 | |||
| 1724 | if ((sbsec->flags & SE_SBINITIALIZED) && | ||
| 1725 | (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) { | ||
| 1726 | *_new_isid = sbsec->mntpoint_sid; | ||
| 1727 | } else if ((sbsec->flags & SBLABEL_MNT) && | ||
| 1728 | tsec->create_sid) { | ||
| 1729 | *_new_isid = tsec->create_sid; | ||
| 1730 | } else { | ||
| 1731 | return security_transition_sid(tsec->sid, dsec->sid, tclass, | ||
| 1732 | name, _new_isid); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | return 0; | ||
| 1736 | } | ||
| 1737 | |||
| 1701 | /* Check whether a task can create a file. */ | 1738 | /* Check whether a task can create a file. */ |
| 1702 | static int may_create(struct inode *dir, | 1739 | static int may_create(struct inode *dir, |
| 1703 | struct dentry *dentry, | 1740 | struct dentry *dentry, |
| @@ -1714,7 +1751,6 @@ static int may_create(struct inode *dir, | |||
| 1714 | sbsec = dir->i_sb->s_security; | 1751 | sbsec = dir->i_sb->s_security; |
| 1715 | 1752 | ||
| 1716 | sid = tsec->sid; | 1753 | sid = tsec->sid; |
| 1717 | newsid = tsec->create_sid; | ||
| 1718 | 1754 | ||
| 1719 | ad.type = LSM_AUDIT_DATA_DENTRY; | 1755 | ad.type = LSM_AUDIT_DATA_DENTRY; |
| 1720 | ad.u.dentry = dentry; | 1756 | ad.u.dentry = dentry; |
| @@ -1725,12 +1761,10 @@ static int may_create(struct inode *dir, | |||
| 1725 | if (rc) | 1761 | if (rc) |
| 1726 | return rc; | 1762 | return rc; |
| 1727 | 1763 | ||
| 1728 | if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { | 1764 | rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass, |
| 1729 | rc = security_transition_sid(sid, dsec->sid, tclass, | 1765 | &newsid); |
| 1730 | &dentry->d_name, &newsid); | 1766 | if (rc) |
| 1731 | if (rc) | 1767 | return rc; |
| 1732 | return rc; | ||
| 1733 | } | ||
| 1734 | 1768 | ||
| 1735 | rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); | 1769 | rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); |
| 1736 | if (rc) | 1770 | if (rc) |
| @@ -2704,32 +2738,14 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, | |||
| 2704 | struct qstr *name, void **ctx, | 2738 | struct qstr *name, void **ctx, |
| 2705 | u32 *ctxlen) | 2739 | u32 *ctxlen) |
| 2706 | { | 2740 | { |
| 2707 | const struct cred *cred = current_cred(); | ||
| 2708 | struct task_security_struct *tsec; | ||
| 2709 | struct inode_security_struct *dsec; | ||
| 2710 | struct superblock_security_struct *sbsec; | ||
| 2711 | struct inode *dir = d_backing_inode(dentry->d_parent); | ||
| 2712 | u32 newsid; | 2741 | u32 newsid; |
| 2713 | int rc; | 2742 | int rc; |
| 2714 | 2743 | ||
| 2715 | tsec = cred->security; | 2744 | rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name, |
| 2716 | dsec = dir->i_security; | 2745 | inode_mode_to_security_class(mode), |
| 2717 | sbsec = dir->i_sb->s_security; | 2746 | &newsid); |
| 2718 | 2747 | if (rc) | |
| 2719 | if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { | 2748 | return rc; |
| 2720 | newsid = tsec->create_sid; | ||
| 2721 | } else { | ||
| 2722 | rc = security_transition_sid(tsec->sid, dsec->sid, | ||
| 2723 | inode_mode_to_security_class(mode), | ||
| 2724 | name, | ||
| 2725 | &newsid); | ||
| 2726 | if (rc) { | ||
| 2727 | printk(KERN_WARNING | ||
| 2728 | "%s: security_transition_sid failed, rc=%d\n", | ||
| 2729 | __func__, -rc); | ||
| 2730 | return rc; | ||
| 2731 | } | ||
| 2732 | } | ||
| 2733 | 2749 | ||
| 2734 | return security_sid_to_context(newsid, (char **)ctx, ctxlen); | 2750 | return security_sid_to_context(newsid, (char **)ctx, ctxlen); |
| 2735 | } | 2751 | } |
| @@ -2752,22 +2768,12 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
| 2752 | sid = tsec->sid; | 2768 | sid = tsec->sid; |
| 2753 | newsid = tsec->create_sid; | 2769 | newsid = tsec->create_sid; |
| 2754 | 2770 | ||
| 2755 | if ((sbsec->flags & SE_SBINITIALIZED) && | 2771 | rc = selinux_determine_inode_label( |
| 2756 | (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) | 2772 | dir, qstr, |
| 2757 | newsid = sbsec->mntpoint_sid; | 2773 | inode_mode_to_security_class(inode->i_mode), |
| 2758 | else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) { | 2774 | &newsid); |
| 2759 | rc = security_transition_sid(sid, dsec->sid, | 2775 | if (rc) |
| 2760 | inode_mode_to_security_class(inode->i_mode), | 2776 | return rc; |
| 2761 | qstr, &newsid); | ||
| 2762 | if (rc) { | ||
| 2763 | printk(KERN_WARNING "%s: " | ||
| 2764 | "security_transition_sid failed, rc=%d (dev=%s " | ||
| 2765 | "ino=%ld)\n", | ||
| 2766 | __func__, | ||
| 2767 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
| 2768 | return rc; | ||
| 2769 | } | ||
| 2770 | } | ||
| 2771 | 2777 | ||
| 2772 | /* Possibly defer initialization to selinux_complete_init. */ | 2778 | /* Possibly defer initialization to selinux_complete_init. */ |
| 2773 | if (sbsec->flags & SE_SBINITIALIZED) { | 2779 | if (sbsec->flags & SE_SBINITIALIZED) { |
| @@ -3228,6 +3234,46 @@ static void selinux_file_free_security(struct file *file) | |||
| 3228 | file_free_security(file); | 3234 | file_free_security(file); |
| 3229 | } | 3235 | } |
| 3230 | 3236 | ||
| 3237 | /* | ||
| 3238 | * Check whether a task has the ioctl permission and cmd | ||
| 3239 | * operation to an inode. | ||
| 3240 | */ | ||
| 3241 | int ioctl_has_perm(const struct cred *cred, struct file *file, | ||
| 3242 | u32 requested, u16 cmd) | ||
| 3243 | { | ||
| 3244 | struct common_audit_data ad; | ||
| 3245 | struct file_security_struct *fsec = file->f_security; | ||
| 3246 | struct inode *inode = file_inode(file); | ||
| 3247 | struct inode_security_struct *isec = inode->i_security; | ||
| 3248 | struct lsm_ioctlop_audit ioctl; | ||
| 3249 | u32 ssid = cred_sid(cred); | ||
| 3250 | int rc; | ||
| 3251 | u8 driver = cmd >> 8; | ||
| 3252 | u8 xperm = cmd & 0xff; | ||
| 3253 | |||
| 3254 | ad.type = LSM_AUDIT_DATA_IOCTL_OP; | ||
| 3255 | ad.u.op = &ioctl; | ||
| 3256 | ad.u.op->cmd = cmd; | ||
| 3257 | ad.u.op->path = file->f_path; | ||
| 3258 | |||
| 3259 | if (ssid != fsec->sid) { | ||
| 3260 | rc = avc_has_perm(ssid, fsec->sid, | ||
| 3261 | SECCLASS_FD, | ||
| 3262 | FD__USE, | ||
| 3263 | &ad); | ||
| 3264 | if (rc) | ||
| 3265 | goto out; | ||
| 3266 | } | ||
| 3267 | |||
| 3268 | if (unlikely(IS_PRIVATE(inode))) | ||
| 3269 | return 0; | ||
| 3270 | |||
| 3271 | rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, | ||
| 3272 | requested, driver, xperm, &ad); | ||
| 3273 | out: | ||
| 3274 | return rc; | ||
| 3275 | } | ||
| 3276 | |||
| 3231 | static int selinux_file_ioctl(struct file *file, unsigned int cmd, | 3277 | static int selinux_file_ioctl(struct file *file, unsigned int cmd, |
| 3232 | unsigned long arg) | 3278 | unsigned long arg) |
| 3233 | { | 3279 | { |
| @@ -3270,7 +3316,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, | |||
| 3270 | * to the file's ioctl() function. | 3316 | * to the file's ioctl() function. |
| 3271 | */ | 3317 | */ |
| 3272 | default: | 3318 | default: |
| 3273 | error = file_has_perm(cred, file, FILE__IOCTL); | 3319 | error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); |
| 3274 | } | 3320 | } |
| 3275 | return error; | 3321 | return error; |
| 3276 | } | 3322 | } |
| @@ -4520,6 +4566,7 @@ static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority | |||
| 4520 | 4566 | ||
| 4521 | sksec->peer_sid = SECINITSID_UNLABELED; | 4567 | sksec->peer_sid = SECINITSID_UNLABELED; |
| 4522 | sksec->sid = SECINITSID_UNLABELED; | 4568 | sksec->sid = SECINITSID_UNLABELED; |
| 4569 | sksec->sclass = SECCLASS_SOCKET; | ||
| 4523 | selinux_netlbl_sk_security_reset(sksec); | 4570 | selinux_netlbl_sk_security_reset(sksec); |
| 4524 | sk->sk_security = sksec; | 4571 | sk->sk_security = sksec; |
| 4525 | 4572 | ||
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 5973c327c54e..0999df03af8b 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h | |||
| @@ -143,6 +143,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, | |||
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | #define AVC_STRICT 1 /* Ignore permissive mode. */ | 145 | #define AVC_STRICT 1 /* Ignore permissive mode. */ |
| 146 | #define AVC_EXTENDED_PERMS 2 /* update extended permissions */ | ||
| 146 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, | 147 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, |
| 147 | u16 tclass, u32 requested, | 148 | u16 tclass, u32 requested, |
| 148 | unsigned flags, | 149 | unsigned flags, |
| @@ -156,6 +157,10 @@ int avc_has_perm_flags(u32 ssid, u32 tsid, | |||
| 156 | struct common_audit_data *auditdata, | 157 | struct common_audit_data *auditdata, |
| 157 | int flags); | 158 | int flags); |
| 158 | 159 | ||
| 160 | int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested, | ||
| 161 | u8 driver, u8 perm, struct common_audit_data *ad); | ||
| 162 | |||
| 163 | |||
| 159 | u32 avc_policy_seqno(void); | 164 | u32 avc_policy_seqno(void); |
| 160 | 165 | ||
| 161 | #define AVC_CALLBACK_GRANT 1 | 166 | #define AVC_CALLBACK_GRANT 1 |
| @@ -166,6 +171,7 @@ u32 avc_policy_seqno(void); | |||
| 166 | #define AVC_CALLBACK_AUDITALLOW_DISABLE 32 | 171 | #define AVC_CALLBACK_AUDITALLOW_DISABLE 32 |
| 167 | #define AVC_CALLBACK_AUDITDENY_ENABLE 64 | 172 | #define AVC_CALLBACK_AUDITDENY_ENABLE 64 |
| 168 | #define AVC_CALLBACK_AUDITDENY_DISABLE 128 | 173 | #define AVC_CALLBACK_AUDITDENY_DISABLE 128 |
| 174 | #define AVC_CALLBACK_ADD_XPERMS 256 | ||
| 169 | 175 | ||
| 170 | int avc_add_callback(int (*callback)(u32 event), u32 events); | 176 | int avc_add_callback(int (*callback)(u32 event), u32 events); |
| 171 | 177 | ||
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 36993ad1c067..6a681d26bf20 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
| @@ -35,13 +35,14 @@ | |||
| 35 | #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 | 35 | #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 |
| 36 | #define POLICYDB_VERSION_DEFAULT_TYPE 28 | 36 | #define POLICYDB_VERSION_DEFAULT_TYPE 28 |
| 37 | #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 | 37 | #define POLICYDB_VERSION_CONSTRAINT_NAMES 29 |
| 38 | #define POLICYDB_VERSION_XPERMS_IOCTL 30 | ||
| 38 | 39 | ||
| 39 | /* Range of policy versions we understand*/ | 40 | /* Range of policy versions we understand*/ |
| 40 | #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE | 41 | #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE |
| 41 | #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX | 42 | #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX |
| 42 | #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE | 43 | #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE |
| 43 | #else | 44 | #else |
| 44 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES | 45 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL |
| 45 | #endif | 46 | #endif |
| 46 | 47 | ||
| 47 | /* Mask for just the mount related flags */ | 48 | /* Mask for just the mount related flags */ |
| @@ -109,11 +110,38 @@ struct av_decision { | |||
| 109 | u32 flags; | 110 | u32 flags; |
| 110 | }; | 111 | }; |
| 111 | 112 | ||
| 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 | |||
| 112 | /* definitions of av_decision.flags */ | 136 | /* definitions of av_decision.flags */ |
| 113 | #define AVD_FLAGS_PERMISSIVE 0x0001 | 137 | #define AVD_FLAGS_PERMISSIVE 0x0001 |
| 114 | 138 | ||
| 115 | void security_compute_av(u32 ssid, u32 tsid, | 139 | 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); | ||
| 117 | 145 | ||
| 118 | void security_compute_av_user(u32 ssid, u32 tsid, | 146 | void security_compute_av_user(u32 ssid, u32 tsid, |
| 119 | u16 tclass, struct av_decision *avd); | 147 | u16 tclass, struct av_decision *avd); |
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index b64f2772b030..3628d3a868b6 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "policydb.h" | 24 | #include "policydb.h" |
| 25 | 25 | ||
| 26 | static struct kmem_cache *avtab_node_cachep; | 26 | static struct kmem_cache *avtab_node_cachep; |
| 27 | static struct kmem_cache *avtab_xperms_cachep; | ||
| 27 | 28 | ||
| 28 | /* Based on MurmurHash3, written by Austin Appleby and placed in the | 29 | /* Based on MurmurHash3, written by Austin Appleby and placed in the |
| 29 | * public domain. | 30 | * public domain. |
| @@ -70,11 +71,24 @@ avtab_insert_node(struct avtab *h, int hvalue, | |||
| 70 | struct avtab_key *key, struct avtab_datum *datum) | 71 | struct avtab_key *key, struct avtab_datum *datum) |
| 71 | { | 72 | { |
| 72 | struct avtab_node *newnode; | 73 | struct avtab_node *newnode; |
| 74 | struct avtab_extended_perms *xperms; | ||
| 73 | newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); | 75 | newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL); |
| 74 | if (newnode == NULL) | 76 | if (newnode == NULL) |
| 75 | return NULL; | 77 | return NULL; |
| 76 | newnode->key = *key; | 78 | 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 | |||
| 78 | if (prev) { | 92 | if (prev) { |
| 79 | newnode->next = prev->next; | 93 | newnode->next = prev->next; |
| 80 | prev->next = newnode; | 94 | prev->next = newnode; |
| @@ -107,8 +121,12 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat | |||
| 107 | if (key->source_type == cur->key.source_type && | 121 | if (key->source_type == cur->key.source_type && |
| 108 | key->target_type == cur->key.target_type && | 122 | key->target_type == cur->key.target_type && |
| 109 | key->target_class == cur->key.target_class && | 123 | 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; | ||
| 111 | return -EEXIST; | 128 | return -EEXIST; |
| 129 | } | ||
| 112 | if (key->source_type < cur->key.source_type) | 130 | if (key->source_type < cur->key.source_type) |
| 113 | break; | 131 | break; |
| 114 | if (key->source_type == cur->key.source_type && | 132 | if (key->source_type == cur->key.source_type && |
| @@ -271,6 +289,9 @@ void avtab_destroy(struct avtab *h) | |||
| 271 | while (cur) { | 289 | while (cur) { |
| 272 | temp = cur; | 290 | temp = cur; |
| 273 | cur = cur->next; | 291 | cur = cur->next; |
| 292 | if (temp->key.specified & AVTAB_XPERMS) | ||
| 293 | kmem_cache_free(avtab_xperms_cachep, | ||
| 294 | temp->datum.u.xperms); | ||
| 274 | kmem_cache_free(avtab_node_cachep, temp); | 295 | kmem_cache_free(avtab_node_cachep, temp); |
| 275 | } | 296 | } |
| 276 | } | 297 | } |
| @@ -359,7 +380,10 @@ static uint16_t spec_order[] = { | |||
| 359 | AVTAB_AUDITALLOW, | 380 | AVTAB_AUDITALLOW, |
| 360 | AVTAB_TRANSITION, | 381 | AVTAB_TRANSITION, |
| 361 | AVTAB_CHANGE, | 382 | AVTAB_CHANGE, |
| 362 | AVTAB_MEMBER | 383 | AVTAB_MEMBER, |
| 384 | AVTAB_XPERMS_ALLOWED, | ||
| 385 | AVTAB_XPERMS_AUDITALLOW, | ||
| 386 | AVTAB_XPERMS_DONTAUDIT | ||
| 363 | }; | 387 | }; |
| 364 | 388 | ||
| 365 | int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | 389 | 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, | |||
| 369 | { | 393 | { |
| 370 | __le16 buf16[4]; | 394 | __le16 buf16[4]; |
| 371 | u16 enabled; | 395 | u16 enabled; |
| 372 | __le32 buf32[7]; | ||
| 373 | u32 items, items2, val, vers = pol->policyvers; | 396 | u32 items, items2, val, vers = pol->policyvers; |
| 374 | struct avtab_key key; | 397 | struct avtab_key key; |
| 375 | struct avtab_datum datum; | 398 | struct avtab_datum datum; |
| 399 | struct avtab_extended_perms xperms; | ||
| 400 | __le32 buf32[ARRAY_SIZE(xperms.perms.p)]; | ||
| 376 | int i, rc; | 401 | int i, rc; |
| 377 | unsigned set; | 402 | unsigned set; |
| 378 | 403 | ||
| @@ -429,11 +454,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 429 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); | 454 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); |
| 430 | return -EINVAL; | 455 | return -EINVAL; |
| 431 | } | 456 | } |
| 457 | if (val & AVTAB_XPERMS) { | ||
| 458 | printk(KERN_ERR "SELinux: avtab: entry has extended permissions\n"); | ||
| 459 | return -EINVAL; | ||
| 460 | } | ||
| 432 | 461 | ||
| 433 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { | 462 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { |
| 434 | if (val & spec_order[i]) { | 463 | if (val & spec_order[i]) { |
| 435 | key.specified = spec_order[i] | enabled; | 464 | key.specified = spec_order[i] | enabled; |
| 436 | datum.data = le32_to_cpu(buf32[items++]); | 465 | datum.u.data = le32_to_cpu(buf32[items++]); |
| 437 | rc = insertf(a, &key, &datum, p); | 466 | rc = insertf(a, &key, &datum, p); |
| 438 | if (rc) | 467 | if (rc) |
| 439 | return rc; | 468 | return rc; |
| @@ -476,14 +505,42 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
| 476 | return -EINVAL; | 505 | return -EINVAL; |
| 477 | } | 506 | } |
| 478 | 507 | ||
| 479 | rc = next_entry(buf32, fp, sizeof(u32)); | 508 | if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) && |
| 480 | if (rc) { | 509 | (key.specified & AVTAB_XPERMS)) { |
| 481 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 510 | printk(KERN_ERR "SELinux: avtab: policy version %u does not " |
| 482 | return rc; | 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); | ||
| 483 | } | 541 | } |
| 484 | datum.data = le32_to_cpu(*buf32); | ||
| 485 | if ((key.specified & AVTAB_TYPE) && | 542 | if ((key.specified & AVTAB_TYPE) && |
| 486 | !policydb_type_isvalid(pol, datum.data)) { | 543 | !policydb_type_isvalid(pol, datum.u.data)) { |
| 487 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); | 544 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); |
| 488 | return -EINVAL; | 545 | return -EINVAL; |
| 489 | } | 546 | } |
| @@ -543,8 +600,9 @@ bad: | |||
| 543 | int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) | 600 | int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp) |
| 544 | { | 601 | { |
| 545 | __le16 buf16[4]; | 602 | __le16 buf16[4]; |
| 546 | __le32 buf32[1]; | 603 | __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)]; |
| 547 | int rc; | 604 | int rc; |
| 605 | unsigned int i; | ||
| 548 | 606 | ||
| 549 | buf16[0] = cpu_to_le16(cur->key.source_type); | 607 | buf16[0] = cpu_to_le16(cur->key.source_type); |
| 550 | buf16[1] = cpu_to_le16(cur->key.target_type); | 608 | 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) | |||
| 553 | rc = put_entry(buf16, sizeof(u16), 4, fp); | 611 | rc = put_entry(buf16, sizeof(u16), 4, fp); |
| 554 | if (rc) | 612 | if (rc) |
| 555 | return rc; | 613 | return rc; |
| 556 | buf32[0] = cpu_to_le32(cur->datum.data); | 614 | |
| 557 | rc = put_entry(buf32, sizeof(u32), 1, fp); | 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 | } | ||
| 558 | if (rc) | 630 | if (rc) |
| 559 | return rc; | 631 | return rc; |
| 560 | return 0; | 632 | return 0; |
| @@ -588,9 +660,13 @@ void avtab_cache_init(void) | |||
| 588 | avtab_node_cachep = kmem_cache_create("avtab_node", | 660 | avtab_node_cachep = kmem_cache_create("avtab_node", |
| 589 | sizeof(struct avtab_node), | 661 | sizeof(struct avtab_node), |
| 590 | 0, SLAB_PANIC, NULL); | 662 | 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); | ||
| 591 | } | 666 | } |
| 592 | 667 | ||
| 593 | void avtab_cache_destroy(void) | 668 | void avtab_cache_destroy(void) |
| 594 | { | 669 | { |
| 595 | kmem_cache_destroy(avtab_node_cachep); | 670 | kmem_cache_destroy(avtab_node_cachep); |
| 671 | kmem_cache_destroy(avtab_xperms_cachep); | ||
| 596 | } | 672 | } |
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index adb451cd44f9..d946c9dc3c9c 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #ifndef _SS_AVTAB_H_ | 23 | #ifndef _SS_AVTAB_H_ |
| 24 | #define _SS_AVTAB_H_ | 24 | #define _SS_AVTAB_H_ |
| 25 | 25 | ||
| 26 | #include "security.h" | ||
| 26 | #include <linux/flex_array.h> | 27 | #include <linux/flex_array.h> |
| 27 | 28 | ||
| 28 | struct avtab_key { | 29 | struct avtab_key { |
| @@ -37,13 +38,43 @@ struct avtab_key { | |||
| 37 | #define AVTAB_MEMBER 0x0020 | 38 | #define AVTAB_MEMBER 0x0020 |
| 38 | #define AVTAB_CHANGE 0x0040 | 39 | #define AVTAB_CHANGE 0x0040 |
| 39 | #define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) | 40 | #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) | ||
| 40 | #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ | 48 | #define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */ |
| 41 | #define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ | 49 | #define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */ |
| 42 | u16 specified; /* what field is specified */ | 50 | u16 specified; /* what field is specified */ |
| 43 | }; | 51 | }; |
| 44 | 52 | ||
| 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 | |||
| 45 | struct avtab_datum { | 73 | 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; | ||
| 47 | }; | 78 | }; |
| 48 | 79 | ||
| 49 | struct avtab_node { | 80 | struct avtab_node { |
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 62c6773be0b7..18643bf9894d 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | #include "security.h" | 16 | #include "security.h" |
| 17 | #include "conditional.h" | 17 | #include "conditional.h" |
| 18 | #include "services.h" | ||
| 18 | 19 | ||
| 19 | /* | 20 | /* |
| 20 | * cond_evaluate_expr evaluates a conditional expr | 21 | * cond_evaluate_expr evaluates a conditional expr |
| @@ -612,21 +613,39 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp) | |||
| 612 | 613 | ||
| 613 | return 0; | 614 | return 0; |
| 614 | } | 615 | } |
| 616 | |||
| 617 | void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key, | ||
| 618 | struct extended_perms_decision *xpermd) | ||
| 619 | { | ||
| 620 | struct avtab_node *node; | ||
| 621 | |||
| 622 | if (!ctab || !key || !xpermd) | ||
| 623 | return; | ||
| 624 | |||
| 625 | for (node = avtab_search_node(ctab, key); node; | ||
| 626 | node = avtab_search_node_next(node, key->specified)) { | ||
| 627 | if (node->key.specified & AVTAB_ENABLED) | ||
| 628 | services_compute_xperms_decision(xpermd, node); | ||
| 629 | } | ||
| 630 | return; | ||
| 631 | |||
| 632 | } | ||
| 615 | /* Determine whether additional permissions are granted by the conditional | 633 | /* Determine whether additional permissions are granted by the conditional |
| 616 | * av table, and if so, add them to the result | 634 | * av table, and if so, add them to the result |
| 617 | */ | 635 | */ |
| 618 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) | 636 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, |
| 637 | struct av_decision *avd, struct extended_perms *xperms) | ||
| 619 | { | 638 | { |
| 620 | struct avtab_node *node; | 639 | struct avtab_node *node; |
| 621 | 640 | ||
| 622 | if (!ctab || !key || !avd) | 641 | if (!ctab || !key || !avd || !xperms) |
| 623 | return; | 642 | return; |
| 624 | 643 | ||
| 625 | for (node = avtab_search_node(ctab, key); node; | 644 | for (node = avtab_search_node(ctab, key); node; |
| 626 | node = avtab_search_node_next(node, key->specified)) { | 645 | node = avtab_search_node_next(node, key->specified)) { |
| 627 | if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == | 646 | if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == |
| 628 | (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) | 647 | (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) |
| 629 | avd->allowed |= node->datum.data; | 648 | avd->allowed |= node->datum.u.data; |
| 630 | if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == | 649 | if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == |
| 631 | (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) | 650 | (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) |
| 632 | /* Since a '0' in an auditdeny mask represents a | 651 | /* Since a '0' in an auditdeny mask represents a |
| @@ -634,10 +653,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi | |||
| 634 | * the '&' operand to ensure that all '0's in the mask | 653 | * the '&' operand to ensure that all '0's in the mask |
| 635 | * are retained (much unlike the allow and auditallow cases). | 654 | * are retained (much unlike the allow and auditallow cases). |
| 636 | */ | 655 | */ |
| 637 | avd->auditdeny &= node->datum.data; | 656 | avd->auditdeny &= node->datum.u.data; |
| 638 | if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == | 657 | if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == |
| 639 | (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) | 658 | (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) |
| 640 | avd->auditallow |= node->datum.data; | 659 | avd->auditallow |= node->datum.u.data; |
| 660 | if ((node->key.specified & AVTAB_ENABLED) && | ||
| 661 | (node->key.specified & AVTAB_XPERMS)) | ||
| 662 | services_compute_xperms_drivers(xperms, node); | ||
| 641 | } | 663 | } |
| 642 | return; | 664 | return; |
| 643 | } | 665 | } |
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index 4d1f87466508..ddb43e7e1c75 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h | |||
| @@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp); | |||
| 73 | int cond_write_bool(void *key, void *datum, void *ptr); | 73 | int cond_write_bool(void *key, void *datum, void *ptr); |
| 74 | int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); | 74 | int cond_write_list(struct policydb *p, struct cond_node *list, void *fp); |
| 75 | 75 | ||
| 76 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); | 76 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, |
| 77 | 77 | struct av_decision *avd, struct extended_perms *xperms); | |
| 78 | void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key, | ||
| 79 | struct extended_perms_decision *xpermd); | ||
| 78 | int evaluate_cond_node(struct policydb *p, struct cond_node *node); | 80 | int evaluate_cond_node(struct policydb *p, struct cond_node *node); |
| 79 | 81 | ||
| 80 | #endif /* _CONDITIONAL_H_ */ | 82 | #endif /* _CONDITIONAL_H_ */ |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 74aa224267c1..992a31530825 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
| @@ -148,6 +148,11 @@ static struct policydb_compat_info policydb_compat[] = { | |||
| 148 | .sym_num = SYM_NUM, | 148 | .sym_num = SYM_NUM, |
| 149 | .ocon_num = OCON_NUM, | 149 | .ocon_num = OCON_NUM, |
| 150 | }, | 150 | }, |
| 151 | { | ||
| 152 | .version = POLICYDB_VERSION_XPERMS_IOCTL, | ||
| 153 | .sym_num = SYM_NUM, | ||
| 154 | .ocon_num = OCON_NUM, | ||
| 155 | }, | ||
| 151 | }; | 156 | }; |
| 152 | 157 | ||
| 153 | static struct policydb_compat_info *policydb_lookup_compat(int version) | 158 | static struct policydb_compat_info *policydb_lookup_compat(int version) |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 9e2d82070915..b7df12ba61d8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -93,9 +93,10 @@ static int context_struct_to_string(struct context *context, char **scontext, | |||
| 93 | u32 *scontext_len); | 93 | u32 *scontext_len); |
| 94 | 94 | ||
| 95 | static void context_struct_compute_av(struct context *scontext, | 95 | static void context_struct_compute_av(struct context *scontext, |
| 96 | struct context *tcontext, | 96 | struct context *tcontext, |
| 97 | u16 tclass, | 97 | u16 tclass, |
| 98 | struct av_decision *avd); | 98 | struct av_decision *avd, |
| 99 | struct extended_perms *xperms); | ||
| 99 | 100 | ||
| 100 | struct selinux_mapping { | 101 | struct selinux_mapping { |
| 101 | u16 value; /* policy value */ | 102 | u16 value; /* policy value */ |
| @@ -565,7 +566,8 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
| 565 | context_struct_compute_av(&lo_scontext, | 566 | context_struct_compute_av(&lo_scontext, |
| 566 | tcontext, | 567 | tcontext, |
| 567 | tclass, | 568 | tclass, |
| 568 | &lo_avd); | 569 | &lo_avd, |
| 570 | NULL); | ||
| 569 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 571 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
| 570 | return; /* no masked permission */ | 572 | return; /* no masked permission */ |
| 571 | masked = ~lo_avd.allowed & avd->allowed; | 573 | masked = ~lo_avd.allowed & avd->allowed; |
| @@ -580,7 +582,8 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
| 580 | context_struct_compute_av(scontext, | 582 | context_struct_compute_av(scontext, |
| 581 | &lo_tcontext, | 583 | &lo_tcontext, |
| 582 | tclass, | 584 | tclass, |
| 583 | &lo_avd); | 585 | &lo_avd, |
| 586 | NULL); | ||
| 584 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 587 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
| 585 | return; /* no masked permission */ | 588 | return; /* no masked permission */ |
| 586 | masked = ~lo_avd.allowed & avd->allowed; | 589 | masked = ~lo_avd.allowed & avd->allowed; |
| @@ -596,7 +599,8 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
| 596 | context_struct_compute_av(&lo_scontext, | 599 | context_struct_compute_av(&lo_scontext, |
| 597 | &lo_tcontext, | 600 | &lo_tcontext, |
| 598 | tclass, | 601 | tclass, |
| 599 | &lo_avd); | 602 | &lo_avd, |
| 603 | NULL); | ||
| 600 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 604 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
| 601 | return; /* no masked permission */ | 605 | return; /* no masked permission */ |
| 602 | masked = ~lo_avd.allowed & avd->allowed; | 606 | masked = ~lo_avd.allowed & avd->allowed; |
| @@ -613,13 +617,39 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
| 613 | } | 617 | } |
| 614 | 618 | ||
| 615 | /* | 619 | /* |
| 616 | * Compute access vectors based on a context structure pair for | 620 | * flag which drivers have permissions |
| 617 | * the permissions in a particular class. | 621 | * only looking for ioctl based extended permssions |
| 622 | */ | ||
| 623 | void services_compute_xperms_drivers( | ||
| 624 | struct extended_perms *xperms, | ||
| 625 | struct avtab_node *node) | ||
| 626 | { | ||
| 627 | unsigned int i; | ||
| 628 | |||
| 629 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | ||
| 630 | /* if one or more driver has all permissions allowed */ | ||
| 631 | for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++) | ||
| 632 | xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i]; | ||
| 633 | } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | ||
| 634 | /* if allowing permissions within a driver */ | ||
| 635 | security_xperm_set(xperms->drivers.p, | ||
| 636 | node->datum.u.xperms->driver); | ||
| 637 | } | ||
| 638 | |||
| 639 | /* If no ioctl commands are allowed, ignore auditallow and auditdeny */ | ||
| 640 | if (node->key.specified & AVTAB_XPERMS_ALLOWED) | ||
| 641 | xperms->len = 1; | ||
| 642 | } | ||
| 643 | |||
| 644 | /* | ||
| 645 | * Compute access vectors and extended permissions based on a context | ||
| 646 | * structure pair for the permissions in a particular class. | ||
| 618 | */ | 647 | */ |
| 619 | static void context_struct_compute_av(struct context *scontext, | 648 | static void context_struct_compute_av(struct context *scontext, |
| 620 | struct context *tcontext, | 649 | struct context *tcontext, |
| 621 | u16 tclass, | 650 | u16 tclass, |
| 622 | struct av_decision *avd) | 651 | struct av_decision *avd, |
| 652 | struct extended_perms *xperms) | ||
| 623 | { | 653 | { |
| 624 | struct constraint_node *constraint; | 654 | struct constraint_node *constraint; |
| 625 | struct role_allow *ra; | 655 | struct role_allow *ra; |
| @@ -633,6 +663,10 @@ static void context_struct_compute_av(struct context *scontext, | |||
| 633 | avd->allowed = 0; | 663 | avd->allowed = 0; |
| 634 | avd->auditallow = 0; | 664 | avd->auditallow = 0; |
| 635 | avd->auditdeny = 0xffffffff; | 665 | avd->auditdeny = 0xffffffff; |
| 666 | if (xperms) { | ||
| 667 | memset(&xperms->drivers, 0, sizeof(xperms->drivers)); | ||
| 668 | xperms->len = 0; | ||
| 669 | } | ||
| 636 | 670 | ||
| 637 | if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { | 671 | if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { |
| 638 | if (printk_ratelimit()) | 672 | if (printk_ratelimit()) |
| @@ -647,7 +681,7 @@ static void context_struct_compute_av(struct context *scontext, | |||
| 647 | * this permission check, then use it. | 681 | * this permission check, then use it. |
| 648 | */ | 682 | */ |
| 649 | avkey.target_class = tclass; | 683 | avkey.target_class = tclass; |
| 650 | avkey.specified = AVTAB_AV; | 684 | avkey.specified = AVTAB_AV | AVTAB_XPERMS; |
| 651 | sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); | 685 | sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); |
| 652 | BUG_ON(!sattr); | 686 | BUG_ON(!sattr); |
| 653 | tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); | 687 | tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); |
| @@ -660,15 +694,18 @@ static void context_struct_compute_av(struct context *scontext, | |||
| 660 | node; | 694 | node; |
| 661 | node = avtab_search_node_next(node, avkey.specified)) { | 695 | node = avtab_search_node_next(node, avkey.specified)) { |
| 662 | if (node->key.specified == AVTAB_ALLOWED) | 696 | if (node->key.specified == AVTAB_ALLOWED) |
| 663 | avd->allowed |= node->datum.data; | 697 | avd->allowed |= node->datum.u.data; |
| 664 | else if (node->key.specified == AVTAB_AUDITALLOW) | 698 | else if (node->key.specified == AVTAB_AUDITALLOW) |
| 665 | avd->auditallow |= node->datum.data; | 699 | avd->auditallow |= node->datum.u.data; |
| 666 | else if (node->key.specified == AVTAB_AUDITDENY) | 700 | else if (node->key.specified == AVTAB_AUDITDENY) |
| 667 | avd->auditdeny &= node->datum.data; | 701 | avd->auditdeny &= node->datum.u.data; |
| 702 | else if (xperms && (node->key.specified & AVTAB_XPERMS)) | ||
| 703 | services_compute_xperms_drivers(xperms, node); | ||
| 668 | } | 704 | } |
| 669 | 705 | ||
| 670 | /* Check conditional av table for additional permissions */ | 706 | /* Check conditional av table for additional permissions */ |
| 671 | cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); | 707 | cond_compute_av(&policydb.te_cond_avtab, &avkey, |
| 708 | avd, xperms); | ||
| 672 | 709 | ||
| 673 | } | 710 | } |
| 674 | } | 711 | } |
| @@ -899,6 +936,139 @@ static void avd_init(struct av_decision *avd) | |||
| 899 | avd->flags = 0; | 936 | avd->flags = 0; |
| 900 | } | 937 | } |
| 901 | 938 | ||
| 939 | void services_compute_xperms_decision(struct extended_perms_decision *xpermd, | ||
| 940 | struct avtab_node *node) | ||
| 941 | { | ||
| 942 | unsigned int i; | ||
| 943 | |||
| 944 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | ||
| 945 | if (xpermd->driver != node->datum.u.xperms->driver) | ||
| 946 | return; | ||
| 947 | } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | ||
| 948 | if (!security_xperm_test(node->datum.u.xperms->perms.p, | ||
| 949 | xpermd->driver)) | ||
| 950 | return; | ||
| 951 | } else { | ||
| 952 | BUG(); | ||
| 953 | } | ||
| 954 | |||
| 955 | if (node->key.specified == AVTAB_XPERMS_ALLOWED) { | ||
| 956 | xpermd->used |= XPERMS_ALLOWED; | ||
| 957 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | ||
| 958 | memset(xpermd->allowed->p, 0xff, | ||
| 959 | sizeof(xpermd->allowed->p)); | ||
| 960 | } | ||
| 961 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | ||
| 962 | for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++) | ||
| 963 | xpermd->allowed->p[i] |= | ||
| 964 | node->datum.u.xperms->perms.p[i]; | ||
| 965 | } | ||
| 966 | } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) { | ||
| 967 | xpermd->used |= XPERMS_AUDITALLOW; | ||
| 968 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | ||
| 969 | memset(xpermd->auditallow->p, 0xff, | ||
| 970 | sizeof(xpermd->auditallow->p)); | ||
| 971 | } | ||
| 972 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | ||
| 973 | for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++) | ||
| 974 | xpermd->auditallow->p[i] |= | ||
| 975 | node->datum.u.xperms->perms.p[i]; | ||
| 976 | } | ||
| 977 | } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) { | ||
| 978 | xpermd->used |= XPERMS_DONTAUDIT; | ||
| 979 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { | ||
| 980 | memset(xpermd->dontaudit->p, 0xff, | ||
| 981 | sizeof(xpermd->dontaudit->p)); | ||
| 982 | } | ||
| 983 | if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) { | ||
| 984 | for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++) | ||
| 985 | xpermd->dontaudit->p[i] |= | ||
| 986 | node->datum.u.xperms->perms.p[i]; | ||
| 987 | } | ||
| 988 | } else { | ||
| 989 | BUG(); | ||
| 990 | } | ||
| 991 | } | ||
| 992 | |||
| 993 | void security_compute_xperms_decision(u32 ssid, | ||
| 994 | u32 tsid, | ||
| 995 | u16 orig_tclass, | ||
| 996 | u8 driver, | ||
| 997 | struct extended_perms_decision *xpermd) | ||
| 998 | { | ||
| 999 | u16 tclass; | ||
| 1000 | struct context *scontext, *tcontext; | ||
| 1001 | struct avtab_key avkey; | ||
| 1002 | struct avtab_node *node; | ||
| 1003 | struct ebitmap *sattr, *tattr; | ||
| 1004 | struct ebitmap_node *snode, *tnode; | ||
| 1005 | unsigned int i, j; | ||
| 1006 | |||
| 1007 | xpermd->driver = driver; | ||
| 1008 | xpermd->used = 0; | ||
| 1009 | memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p)); | ||
| 1010 | memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p)); | ||
| 1011 | memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p)); | ||
| 1012 | |||
| 1013 | read_lock(&policy_rwlock); | ||
| 1014 | if (!ss_initialized) | ||
| 1015 | goto allow; | ||
| 1016 | |||
| 1017 | scontext = sidtab_search(&sidtab, ssid); | ||
| 1018 | if (!scontext) { | ||
| 1019 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
| 1020 | __func__, ssid); | ||
| 1021 | goto out; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | tcontext = sidtab_search(&sidtab, tsid); | ||
| 1025 | if (!tcontext) { | ||
| 1026 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
| 1027 | __func__, tsid); | ||
| 1028 | goto out; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | tclass = unmap_class(orig_tclass); | ||
| 1032 | if (unlikely(orig_tclass && !tclass)) { | ||
| 1033 | if (policydb.allow_unknown) | ||
| 1034 | goto allow; | ||
| 1035 | goto out; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | |||
| 1039 | if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { | ||
| 1040 | pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass); | ||
| 1041 | goto out; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | avkey.target_class = tclass; | ||
| 1045 | avkey.specified = AVTAB_XPERMS; | ||
| 1046 | sattr = flex_array_get(policydb.type_attr_map_array, | ||
| 1047 | scontext->type - 1); | ||
| 1048 | BUG_ON(!sattr); | ||
| 1049 | tattr = flex_array_get(policydb.type_attr_map_array, | ||
| 1050 | tcontext->type - 1); | ||
| 1051 | BUG_ON(!tattr); | ||
| 1052 | ebitmap_for_each_positive_bit(sattr, snode, i) { | ||
| 1053 | ebitmap_for_each_positive_bit(tattr, tnode, j) { | ||
| 1054 | avkey.source_type = i + 1; | ||
| 1055 | avkey.target_type = j + 1; | ||
| 1056 | for (node = avtab_search_node(&policydb.te_avtab, &avkey); | ||
| 1057 | node; | ||
| 1058 | node = avtab_search_node_next(node, avkey.specified)) | ||
| 1059 | services_compute_xperms_decision(xpermd, node); | ||
| 1060 | |||
| 1061 | cond_compute_xperms(&policydb.te_cond_avtab, | ||
| 1062 | &avkey, xpermd); | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | out: | ||
| 1066 | read_unlock(&policy_rwlock); | ||
| 1067 | return; | ||
| 1068 | allow: | ||
| 1069 | memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p)); | ||
| 1070 | goto out; | ||
| 1071 | } | ||
| 902 | 1072 | ||
| 903 | /** | 1073 | /** |
| 904 | * security_compute_av - Compute access vector decisions. | 1074 | * security_compute_av - Compute access vector decisions. |
| @@ -906,6 +1076,7 @@ static void avd_init(struct av_decision *avd) | |||
| 906 | * @tsid: target security identifier | 1076 | * @tsid: target security identifier |
| 907 | * @tclass: target security class | 1077 | * @tclass: target security class |
| 908 | * @avd: access vector decisions | 1078 | * @avd: access vector decisions |
| 1079 | * @xperms: extended permissions | ||
| 909 | * | 1080 | * |
| 910 | * Compute a set of access vector decisions based on the | 1081 | * Compute a set of access vector decisions based on the |
| 911 | * SID pair (@ssid, @tsid) for the permissions in @tclass. | 1082 | * SID pair (@ssid, @tsid) for the permissions in @tclass. |
| @@ -913,13 +1084,15 @@ static void avd_init(struct av_decision *avd) | |||
| 913 | void security_compute_av(u32 ssid, | 1084 | void security_compute_av(u32 ssid, |
| 914 | u32 tsid, | 1085 | u32 tsid, |
| 915 | u16 orig_tclass, | 1086 | u16 orig_tclass, |
| 916 | struct av_decision *avd) | 1087 | struct av_decision *avd, |
| 1088 | struct extended_perms *xperms) | ||
| 917 | { | 1089 | { |
| 918 | u16 tclass; | 1090 | u16 tclass; |
| 919 | struct context *scontext = NULL, *tcontext = NULL; | 1091 | struct context *scontext = NULL, *tcontext = NULL; |
| 920 | 1092 | ||
| 921 | read_lock(&policy_rwlock); | 1093 | read_lock(&policy_rwlock); |
| 922 | avd_init(avd); | 1094 | avd_init(avd); |
| 1095 | xperms->len = 0; | ||
| 923 | if (!ss_initialized) | 1096 | if (!ss_initialized) |
| 924 | goto allow; | 1097 | goto allow; |
| 925 | 1098 | ||
| @@ -947,7 +1120,7 @@ void security_compute_av(u32 ssid, | |||
| 947 | goto allow; | 1120 | goto allow; |
| 948 | goto out; | 1121 | goto out; |
| 949 | } | 1122 | } |
| 950 | context_struct_compute_av(scontext, tcontext, tclass, avd); | 1123 | context_struct_compute_av(scontext, tcontext, tclass, avd, xperms); |
| 951 | map_decision(orig_tclass, avd, policydb.allow_unknown); | 1124 | map_decision(orig_tclass, avd, policydb.allow_unknown); |
| 952 | out: | 1125 | out: |
| 953 | read_unlock(&policy_rwlock); | 1126 | read_unlock(&policy_rwlock); |
| @@ -993,7 +1166,7 @@ void security_compute_av_user(u32 ssid, | |||
| 993 | goto out; | 1166 | goto out; |
| 994 | } | 1167 | } |
| 995 | 1168 | ||
| 996 | context_struct_compute_av(scontext, tcontext, tclass, avd); | 1169 | context_struct_compute_av(scontext, tcontext, tclass, avd, NULL); |
| 997 | out: | 1170 | out: |
| 998 | read_unlock(&policy_rwlock); | 1171 | read_unlock(&policy_rwlock); |
| 999 | return; | 1172 | return; |
| @@ -1515,7 +1688,7 @@ static int security_compute_sid(u32 ssid, | |||
| 1515 | 1688 | ||
| 1516 | if (avdatum) { | 1689 | if (avdatum) { |
| 1517 | /* Use the type from the type transition/member/change rule. */ | 1690 | /* Use the type from the type transition/member/change rule. */ |
| 1518 | newcontext.type = avdatum->data; | 1691 | newcontext.type = avdatum->u.data; |
| 1519 | } | 1692 | } |
| 1520 | 1693 | ||
| 1521 | /* if we have a objname this is a file trans check so check those rules */ | 1694 | /* if we have a objname this is a file trans check so check those rules */ |
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h index e8d907e903cd..6abcd8729ec3 100644 --- a/security/selinux/ss/services.h +++ b/security/selinux/ss/services.h | |||
| @@ -11,5 +11,11 @@ | |||
| 11 | 11 | ||
| 12 | extern struct policydb policydb; | 12 | extern struct policydb policydb; |
| 13 | 13 | ||
| 14 | void services_compute_xperms_drivers(struct extended_perms *xperms, | ||
| 15 | struct avtab_node *node); | ||
| 16 | |||
| 17 | void services_compute_xperms_decision(struct extended_perms_decision *xpermd, | ||
| 18 | struct avtab_node *node); | ||
| 19 | |||
| 14 | #endif /* _SS_SERVICES_H_ */ | 20 | #endif /* _SS_SERVICES_H_ */ |
| 15 | 21 | ||
