diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 135 |
1 files changed, 113 insertions, 22 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 24caaeec8894..a91c961ba38b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -246,6 +246,7 @@ static int superblock_alloc_security(struct super_block *sb) | |||
| 246 | sbsec->sb = sb; | 246 | sbsec->sb = sb; |
| 247 | sbsec->sid = SECINITSID_UNLABELED; | 247 | sbsec->sid = SECINITSID_UNLABELED; |
| 248 | sbsec->def_sid = SECINITSID_FILE; | 248 | sbsec->def_sid = SECINITSID_FILE; |
| 249 | sbsec->mntpoint_sid = SECINITSID_UNLABELED; | ||
| 249 | sb->s_security = sbsec; | 250 | sb->s_security = sbsec; |
| 250 | 251 | ||
| 251 | return 0; | 252 | return 0; |
| @@ -319,19 +320,53 @@ enum { | |||
| 319 | Opt_context = 1, | 320 | Opt_context = 1, |
| 320 | Opt_fscontext = 2, | 321 | Opt_fscontext = 2, |
| 321 | Opt_defcontext = 4, | 322 | Opt_defcontext = 4, |
| 323 | Opt_rootcontext = 8, | ||
| 322 | }; | 324 | }; |
| 323 | 325 | ||
| 324 | static match_table_t tokens = { | 326 | static match_table_t tokens = { |
| 325 | {Opt_context, "context=%s"}, | 327 | {Opt_context, "context=%s"}, |
| 326 | {Opt_fscontext, "fscontext=%s"}, | 328 | {Opt_fscontext, "fscontext=%s"}, |
| 327 | {Opt_defcontext, "defcontext=%s"}, | 329 | {Opt_defcontext, "defcontext=%s"}, |
| 330 | {Opt_rootcontext, "rootcontext=%s"}, | ||
| 328 | }; | 331 | }; |
| 329 | 332 | ||
| 330 | #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" | 333 | #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" |
| 331 | 334 | ||
| 335 | static int may_context_mount_sb_relabel(u32 sid, | ||
| 336 | struct superblock_security_struct *sbsec, | ||
| 337 | struct task_security_struct *tsec) | ||
| 338 | { | ||
| 339 | int rc; | ||
| 340 | |||
| 341 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
| 342 | FILESYSTEM__RELABELFROM, NULL); | ||
| 343 | if (rc) | ||
| 344 | return rc; | ||
| 345 | |||
| 346 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, | ||
| 347 | FILESYSTEM__RELABELTO, NULL); | ||
| 348 | return rc; | ||
| 349 | } | ||
| 350 | |||
| 351 | static int may_context_mount_inode_relabel(u32 sid, | ||
| 352 | struct superblock_security_struct *sbsec, | ||
| 353 | struct task_security_struct *tsec) | ||
| 354 | { | ||
| 355 | int rc; | ||
| 356 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
| 357 | FILESYSTEM__RELABELFROM, NULL); | ||
| 358 | if (rc) | ||
| 359 | return rc; | ||
| 360 | |||
| 361 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
| 362 | FILESYSTEM__ASSOCIATE, NULL); | ||
| 363 | return rc; | ||
| 364 | } | ||
| 365 | |||
| 332 | static int try_context_mount(struct super_block *sb, void *data) | 366 | static int try_context_mount(struct super_block *sb, void *data) |
| 333 | { | 367 | { |
| 334 | char *context = NULL, *defcontext = NULL; | 368 | char *context = NULL, *defcontext = NULL; |
| 369 | char *fscontext = NULL, *rootcontext = NULL; | ||
| 335 | const char *name; | 370 | const char *name; |
| 336 | u32 sid; | 371 | u32 sid; |
| 337 | int alloc = 0, rc = 0, seen = 0; | 372 | int alloc = 0, rc = 0, seen = 0; |
| @@ -374,7 +409,7 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 374 | 409 | ||
| 375 | switch (token) { | 410 | switch (token) { |
| 376 | case Opt_context: | 411 | case Opt_context: |
| 377 | if (seen) { | 412 | if (seen & (Opt_context|Opt_defcontext)) { |
| 378 | rc = -EINVAL; | 413 | rc = -EINVAL; |
| 379 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | 414 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); |
| 380 | goto out_free; | 415 | goto out_free; |
| @@ -390,13 +425,13 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 390 | break; | 425 | break; |
| 391 | 426 | ||
| 392 | case Opt_fscontext: | 427 | case Opt_fscontext: |
| 393 | if (seen & (Opt_context|Opt_fscontext)) { | 428 | if (seen & Opt_fscontext) { |
| 394 | rc = -EINVAL; | 429 | rc = -EINVAL; |
| 395 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | 430 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); |
| 396 | goto out_free; | 431 | goto out_free; |
| 397 | } | 432 | } |
| 398 | context = match_strdup(&args[0]); | 433 | fscontext = match_strdup(&args[0]); |
| 399 | if (!context) { | 434 | if (!fscontext) { |
| 400 | rc = -ENOMEM; | 435 | rc = -ENOMEM; |
| 401 | goto out_free; | 436 | goto out_free; |
| 402 | } | 437 | } |
| @@ -405,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 405 | seen |= Opt_fscontext; | 440 | seen |= Opt_fscontext; |
| 406 | break; | 441 | break; |
| 407 | 442 | ||
| 443 | case Opt_rootcontext: | ||
| 444 | if (seen & Opt_rootcontext) { | ||
| 445 | rc = -EINVAL; | ||
| 446 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | ||
| 447 | goto out_free; | ||
| 448 | } | ||
| 449 | rootcontext = match_strdup(&args[0]); | ||
| 450 | if (!rootcontext) { | ||
| 451 | rc = -ENOMEM; | ||
| 452 | goto out_free; | ||
| 453 | } | ||
| 454 | if (!alloc) | ||
| 455 | alloc = 1; | ||
| 456 | seen |= Opt_rootcontext; | ||
| 457 | break; | ||
| 458 | |||
| 408 | case Opt_defcontext: | 459 | case Opt_defcontext: |
| 409 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { | 460 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { |
| 410 | rc = -EINVAL; | 461 | rc = -EINVAL; |
| @@ -441,6 +492,28 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 441 | if (!seen) | 492 | if (!seen) |
| 442 | goto out; | 493 | goto out; |
| 443 | 494 | ||
| 495 | /* sets the context of the superblock for the fs being mounted. */ | ||
| 496 | if (fscontext) { | ||
| 497 | rc = security_context_to_sid(fscontext, strlen(fscontext), &sid); | ||
| 498 | if (rc) { | ||
| 499 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
| 500 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
| 501 | fscontext, sb->s_id, name, rc); | ||
| 502 | goto out_free; | ||
| 503 | } | ||
| 504 | |||
| 505 | rc = may_context_mount_sb_relabel(sid, sbsec, tsec); | ||
| 506 | if (rc) | ||
| 507 | goto out_free; | ||
| 508 | |||
| 509 | sbsec->sid = sid; | ||
| 510 | } | ||
| 511 | |||
| 512 | /* | ||
| 513 | * Switch to using mount point labeling behavior. | ||
| 514 | * sets the label used on all file below the mountpoint, and will set | ||
| 515 | * the superblock context if not already set. | ||
| 516 | */ | ||
| 444 | if (context) { | 517 | if (context) { |
| 445 | rc = security_context_to_sid(context, strlen(context), &sid); | 518 | rc = security_context_to_sid(context, strlen(context), &sid); |
| 446 | if (rc) { | 519 | if (rc) { |
| @@ -450,20 +523,38 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 450 | goto out_free; | 523 | goto out_free; |
| 451 | } | 524 | } |
| 452 | 525 | ||
| 453 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | 526 | if (!fscontext) { |
| 454 | FILESYSTEM__RELABELFROM, NULL); | 527 | rc = may_context_mount_sb_relabel(sid, sbsec, tsec); |
| 455 | if (rc) | 528 | if (rc) |
| 529 | goto out_free; | ||
| 530 | sbsec->sid = sid; | ||
| 531 | } else { | ||
| 532 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); | ||
| 533 | if (rc) | ||
| 534 | goto out_free; | ||
| 535 | } | ||
| 536 | sbsec->mntpoint_sid = sid; | ||
| 537 | |||
| 538 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | ||
| 539 | } | ||
| 540 | |||
| 541 | if (rootcontext) { | ||
| 542 | struct inode *inode = sb->s_root->d_inode; | ||
| 543 | struct inode_security_struct *isec = inode->i_security; | ||
| 544 | rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); | ||
| 545 | if (rc) { | ||
| 546 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
| 547 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
| 548 | rootcontext, sb->s_id, name, rc); | ||
| 456 | goto out_free; | 549 | goto out_free; |
| 550 | } | ||
| 457 | 551 | ||
| 458 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, | 552 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); |
| 459 | FILESYSTEM__RELABELTO, NULL); | ||
| 460 | if (rc) | 553 | if (rc) |
| 461 | goto out_free; | 554 | goto out_free; |
| 462 | 555 | ||
| 463 | sbsec->sid = sid; | 556 | isec->sid = sid; |
| 464 | 557 | isec->initialized = 1; | |
| 465 | if (seen & Opt_context) | ||
| 466 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | ||
| 467 | } | 558 | } |
| 468 | 559 | ||
| 469 | if (defcontext) { | 560 | if (defcontext) { |
| @@ -478,13 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
| 478 | if (sid == sbsec->def_sid) | 569 | if (sid == sbsec->def_sid) |
| 479 | goto out_free; | 570 | goto out_free; |
| 480 | 571 | ||
| 481 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | 572 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); |
| 482 | FILESYSTEM__RELABELFROM, NULL); | ||
| 483 | if (rc) | ||
| 484 | goto out_free; | ||
| 485 | |||
| 486 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
| 487 | FILESYSTEM__ASSOCIATE, NULL); | ||
| 488 | if (rc) | 573 | if (rc) |
| 489 | goto out_free; | 574 | goto out_free; |
| 490 | 575 | ||
| @@ -495,6 +580,8 @@ out_free: | |||
| 495 | if (alloc) { | 580 | if (alloc) { |
| 496 | kfree(context); | 581 | kfree(context); |
| 497 | kfree(defcontext); | 582 | kfree(defcontext); |
| 583 | kfree(fscontext); | ||
| 584 | kfree(rootcontext); | ||
| 498 | } | 585 | } |
| 499 | out: | 586 | out: |
| 500 | return rc; | 587 | return rc; |
| @@ -876,8 +963,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
| 876 | goto out; | 963 | goto out; |
| 877 | isec->sid = sid; | 964 | isec->sid = sid; |
| 878 | break; | 965 | break; |
| 966 | case SECURITY_FS_USE_MNTPOINT: | ||
| 967 | isec->sid = sbsec->mntpoint_sid; | ||
| 968 | break; | ||
| 879 | default: | 969 | default: |
| 880 | /* Default to the fs SID. */ | 970 | /* Default to the fs superblock SID. */ |
| 881 | isec->sid = sbsec->sid; | 971 | isec->sid = sbsec->sid; |
| 882 | 972 | ||
| 883 | if (sbsec->proc) { | 973 | if (sbsec->proc) { |
| @@ -1843,7 +1933,8 @@ static inline int selinux_option(char *option, int len) | |||
| 1843 | { | 1933 | { |
| 1844 | return (match_prefix("context=", sizeof("context=")-1, option, len) || | 1934 | return (match_prefix("context=", sizeof("context=")-1, option, len) || |
| 1845 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || | 1935 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || |
| 1846 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); | 1936 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) || |
| 1937 | match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len)); | ||
| 1847 | } | 1938 | } |
| 1848 | 1939 | ||
| 1849 | static inline void take_option(char **to, char *from, int *first, int len) | 1940 | static inline void take_option(char **to, char *from, int *first, int len) |
