diff options
-rw-r--r-- | security/selinux/hooks.c | 66 |
1 files changed, 57 insertions, 9 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7e101dbea4cb..2e8b4dfcbc74 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -320,12 +320,14 @@ enum { | |||
320 | Opt_context = 1, | 320 | Opt_context = 1, |
321 | Opt_fscontext = 2, | 321 | Opt_fscontext = 2, |
322 | Opt_defcontext = 4, | 322 | Opt_defcontext = 4, |
323 | Opt_rootcontext = 8, | ||
323 | }; | 324 | }; |
324 | 325 | ||
325 | static match_table_t tokens = { | 326 | static match_table_t tokens = { |
326 | {Opt_context, "context=%s"}, | 327 | {Opt_context, "context=%s"}, |
327 | {Opt_fscontext, "fscontext=%s"}, | 328 | {Opt_fscontext, "fscontext=%s"}, |
328 | {Opt_defcontext, "defcontext=%s"}, | 329 | {Opt_defcontext, "defcontext=%s"}, |
330 | {Opt_rootcontext, "rootcontext=%s"}, | ||
329 | }; | 331 | }; |
330 | 332 | ||
331 | #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" |
@@ -346,10 +348,25 @@ static int may_context_mount_sb_relabel(u32 sid, | |||
346 | return rc; | 348 | return rc; |
347 | } | 349 | } |
348 | 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 | |||
349 | static int try_context_mount(struct super_block *sb, void *data) | 366 | static int try_context_mount(struct super_block *sb, void *data) |
350 | { | 367 | { |
351 | char *context = NULL, *defcontext = NULL; | 368 | char *context = NULL, *defcontext = NULL; |
352 | char *fscontext = NULL; | 369 | char *fscontext = NULL, *rootcontext = NULL; |
353 | const char *name; | 370 | const char *name; |
354 | u32 sid; | 371 | u32 sid; |
355 | int alloc = 0, rc = 0, seen = 0; | 372 | int alloc = 0, rc = 0, seen = 0; |
@@ -423,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
423 | seen |= Opt_fscontext; | 440 | seen |= Opt_fscontext; |
424 | break; | 441 | break; |
425 | 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 | |||
426 | case Opt_defcontext: | 459 | case Opt_defcontext: |
427 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { | 460 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { |
428 | rc = -EINVAL; | 461 | rc = -EINVAL; |
@@ -501,6 +534,25 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
501 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | 534 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; |
502 | } | 535 | } |
503 | 536 | ||
537 | if (rootcontext) { | ||
538 | struct inode *inode = sb->s_root->d_inode; | ||
539 | struct inode_security_struct *isec = inode->i_security; | ||
540 | rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); | ||
541 | if (rc) { | ||
542 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
543 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
544 | rootcontext, sb->s_id, name, rc); | ||
545 | goto out_free; | ||
546 | } | ||
547 | |||
548 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); | ||
549 | if (rc) | ||
550 | goto out_free; | ||
551 | |||
552 | isec->sid = sid; | ||
553 | isec->initialized = 1; | ||
554 | } | ||
555 | |||
504 | if (defcontext) { | 556 | if (defcontext) { |
505 | rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); | 557 | rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); |
506 | if (rc) { | 558 | if (rc) { |
@@ -513,13 +565,7 @@ static int try_context_mount(struct super_block *sb, void *data) | |||
513 | if (sid == sbsec->def_sid) | 565 | if (sid == sbsec->def_sid) |
514 | goto out_free; | 566 | goto out_free; |
515 | 567 | ||
516 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | 568 | rc = may_context_mount_inode_relabel(sid, sbsec, tsec); |
517 | FILESYSTEM__RELABELFROM, NULL); | ||
518 | if (rc) | ||
519 | goto out_free; | ||
520 | |||
521 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
522 | FILESYSTEM__ASSOCIATE, NULL); | ||
523 | if (rc) | 569 | if (rc) |
524 | goto out_free; | 570 | goto out_free; |
525 | 571 | ||
@@ -531,6 +577,7 @@ out_free: | |||
531 | kfree(context); | 577 | kfree(context); |
532 | kfree(defcontext); | 578 | kfree(defcontext); |
533 | kfree(fscontext); | 579 | kfree(fscontext); |
580 | kfree(rootcontext); | ||
534 | } | 581 | } |
535 | out: | 582 | out: |
536 | return rc; | 583 | return rc; |
@@ -1882,7 +1929,8 @@ static inline int selinux_option(char *option, int len) | |||
1882 | { | 1929 | { |
1883 | return (match_prefix("context=", sizeof("context=")-1, option, len) || | 1930 | return (match_prefix("context=", sizeof("context=")-1, option, len) || |
1884 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || | 1931 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || |
1885 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); | 1932 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) || |
1933 | match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len)); | ||
1886 | } | 1934 | } |
1887 | 1935 | ||
1888 | static inline void take_option(char **to, char *from, int *first, int len) | 1936 | static inline void take_option(char **to, char *from, int *first, int len) |