diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-05 16:25:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-05 16:25:58 -0500 |
commit | 505b050fdf42097883b2d37b8e796e1f11dbef50 (patch) | |
tree | 21f5b43505a5771d13533ac675c785a9bf480fdc /security/smack | |
parent | 9b286efeb5eb5aaa2712873fc1f928b2f879dbde (diff) | |
parent | 718c43038f287e843c2f63d946977de90014cb11 (diff) |
Merge branch 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs mount API prep from Al Viro:
"Mount API prereqs.
Mostly that's LSM mount options cleanups. There are several minor
fixes in there, but nothing earth-shattering (leaks on failure exits,
mostly)"
* 'mount.part1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (27 commits)
mount_fs: suppress MAC on MS_SUBMOUNT as well as MS_KERNMOUNT
smack: rewrite smack_sb_eat_lsm_opts()
smack: get rid of match_token()
smack: take the guts of smack_parse_opts_str() into a new helper
LSM: new method: ->sb_add_mnt_opt()
selinux: rewrite selinux_sb_eat_lsm_opts()
selinux: regularize Opt_... names a bit
selinux: switch away from match_token()
selinux: new helper - selinux_add_opt()
LSM: bury struct security_mnt_opts
smack: switch to private smack_mnt_opts
selinux: switch to private struct selinux_mnt_opts
LSM: hide struct security_mnt_opts from any generic code
selinux: kill selinux_sb_get_mnt_opts()
LSM: turn sb_eat_lsm_opts() into a method
nfs_remount(): don't leak, don't ignore LSM options quietly
btrfs: sanitize security_mnt_opts use
selinux; don't open-code a loop in sb_finish_set_opts()
LSM: split ->sb_set_mnt_opts() out of ->sb_kern_mount()
new helper: security_sb_eat_lsm_opts()
...
Diffstat (limited to 'security/smack')
-rw-r--r-- | security/smack/smack_lsm.c | 359 |
1 files changed, 137 insertions, 222 deletions
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index cd720c06b78c..430d4f35e55c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
@@ -59,14 +59,31 @@ static LIST_HEAD(smk_ipv6_port_list); | |||
59 | static struct kmem_cache *smack_inode_cache; | 59 | static struct kmem_cache *smack_inode_cache; |
60 | int smack_enabled; | 60 | int smack_enabled; |
61 | 61 | ||
62 | static const match_table_t smk_mount_tokens = { | 62 | #define A(s) {"smack"#s, sizeof("smack"#s) - 1, Opt_##s} |
63 | {Opt_fsdefault, SMK_FSDEFAULT "%s"}, | 63 | static struct { |
64 | {Opt_fsfloor, SMK_FSFLOOR "%s"}, | 64 | const char *name; |
65 | {Opt_fshat, SMK_FSHAT "%s"}, | 65 | int len; |
66 | {Opt_fsroot, SMK_FSROOT "%s"}, | 66 | int opt; |
67 | {Opt_fstransmute, SMK_FSTRANS "%s"}, | 67 | } smk_mount_opts[] = { |
68 | {Opt_error, NULL}, | 68 | A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute) |
69 | }; | 69 | }; |
70 | #undef A | ||
71 | |||
72 | static int match_opt_prefix(char *s, int l, char **arg) | ||
73 | { | ||
74 | int i; | ||
75 | |||
76 | for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) { | ||
77 | size_t len = smk_mount_opts[i].len; | ||
78 | if (len > l || memcmp(s, smk_mount_opts[i].name, len)) | ||
79 | continue; | ||
80 | if (len == l || s[len] != '=') | ||
81 | continue; | ||
82 | *arg = s + len + 1; | ||
83 | return smk_mount_opts[i].opt; | ||
84 | } | ||
85 | return Opt_error; | ||
86 | } | ||
70 | 87 | ||
71 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP | 88 | #ifdef CONFIG_SECURITY_SMACK_BRINGUP |
72 | static char *smk_bu_mess[] = { | 89 | static char *smk_bu_mess[] = { |
@@ -567,175 +584,110 @@ static void smack_sb_free_security(struct super_block *sb) | |||
567 | sb->s_security = NULL; | 584 | sb->s_security = NULL; |
568 | } | 585 | } |
569 | 586 | ||
570 | /** | 587 | struct smack_mnt_opts { |
571 | * smack_sb_copy_data - copy mount options data for processing | 588 | const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute; |
572 | * @orig: where to start | 589 | }; |
573 | * @smackopts: mount options string | ||
574 | * | ||
575 | * Returns 0 on success or -ENOMEM on error. | ||
576 | * | ||
577 | * Copy the Smack specific mount options out of the mount | ||
578 | * options list. | ||
579 | */ | ||
580 | static int smack_sb_copy_data(char *orig, char *smackopts) | ||
581 | { | ||
582 | char *cp, *commap, *otheropts, *dp; | ||
583 | |||
584 | otheropts = (char *)get_zeroed_page(GFP_KERNEL); | ||
585 | if (otheropts == NULL) | ||
586 | return -ENOMEM; | ||
587 | 590 | ||
588 | for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) { | 591 | static void smack_free_mnt_opts(void *mnt_opts) |
589 | if (strstr(cp, SMK_FSDEFAULT) == cp) | 592 | { |
590 | dp = smackopts; | 593 | struct smack_mnt_opts *opts = mnt_opts; |
591 | else if (strstr(cp, SMK_FSFLOOR) == cp) | 594 | kfree(opts->fsdefault); |
592 | dp = smackopts; | 595 | kfree(opts->fsfloor); |
593 | else if (strstr(cp, SMK_FSHAT) == cp) | 596 | kfree(opts->fshat); |
594 | dp = smackopts; | 597 | kfree(opts->fsroot); |
595 | else if (strstr(cp, SMK_FSROOT) == cp) | 598 | kfree(opts->fstransmute); |
596 | dp = smackopts; | 599 | kfree(opts); |
597 | else if (strstr(cp, SMK_FSTRANS) == cp) | 600 | } |
598 | dp = smackopts; | ||
599 | else | ||
600 | dp = otheropts; | ||
601 | 601 | ||
602 | commap = strchr(cp, ','); | 602 | static int smack_add_opt(int token, const char *s, void **mnt_opts) |
603 | if (commap != NULL) | 603 | { |
604 | *commap = '\0'; | 604 | struct smack_mnt_opts *opts = *mnt_opts; |
605 | 605 | ||
606 | if (*dp != '\0') | 606 | if (!opts) { |
607 | strcat(dp, ","); | 607 | opts = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL); |
608 | strcat(dp, cp); | 608 | if (!opts) |
609 | return -ENOMEM; | ||
610 | *mnt_opts = opts; | ||
609 | } | 611 | } |
612 | if (!s) | ||
613 | return -ENOMEM; | ||
610 | 614 | ||
611 | strcpy(orig, otheropts); | 615 | switch (token) { |
612 | free_page((unsigned long)otheropts); | 616 | case Opt_fsdefault: |
613 | 617 | if (opts->fsdefault) | |
618 | goto out_opt_err; | ||
619 | opts->fsdefault = s; | ||
620 | break; | ||
621 | case Opt_fsfloor: | ||
622 | if (opts->fsfloor) | ||
623 | goto out_opt_err; | ||
624 | opts->fsfloor = s; | ||
625 | break; | ||
626 | case Opt_fshat: | ||
627 | if (opts->fshat) | ||
628 | goto out_opt_err; | ||
629 | opts->fshat = s; | ||
630 | break; | ||
631 | case Opt_fsroot: | ||
632 | if (opts->fsroot) | ||
633 | goto out_opt_err; | ||
634 | opts->fsroot = s; | ||
635 | break; | ||
636 | case Opt_fstransmute: | ||
637 | if (opts->fstransmute) | ||
638 | goto out_opt_err; | ||
639 | opts->fstransmute = s; | ||
640 | break; | ||
641 | } | ||
614 | return 0; | 642 | return 0; |
643 | |||
644 | out_opt_err: | ||
645 | pr_warn("Smack: duplicate mount options\n"); | ||
646 | return -EINVAL; | ||
615 | } | 647 | } |
616 | 648 | ||
617 | /** | 649 | static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts) |
618 | * smack_parse_opts_str - parse Smack specific mount options | ||
619 | * @options: mount options string | ||
620 | * @opts: where to store converted mount opts | ||
621 | * | ||
622 | * Returns 0 on success or -ENOMEM on error. | ||
623 | * | ||
624 | * converts Smack specific mount options to generic security option format | ||
625 | */ | ||
626 | static int smack_parse_opts_str(char *options, | ||
627 | struct security_mnt_opts *opts) | ||
628 | { | 650 | { |
629 | char *p; | 651 | char *from = options, *to = options; |
630 | char *fsdefault = NULL; | 652 | bool first = true; |
631 | char *fsfloor = NULL; | ||
632 | char *fshat = NULL; | ||
633 | char *fsroot = NULL; | ||
634 | char *fstransmute = NULL; | ||
635 | int rc = -ENOMEM; | ||
636 | int num_mnt_opts = 0; | ||
637 | int token; | ||
638 | |||
639 | opts->num_mnt_opts = 0; | ||
640 | |||
641 | if (!options) | ||
642 | return 0; | ||
643 | |||
644 | while ((p = strsep(&options, ",")) != NULL) { | ||
645 | substring_t args[MAX_OPT_ARGS]; | ||
646 | 653 | ||
647 | if (!*p) | 654 | while (1) { |
648 | continue; | 655 | char *next = strchr(from, ','); |
649 | 656 | int token, len, rc; | |
650 | token = match_token(p, smk_mount_tokens, args); | 657 | char *arg = NULL; |
651 | 658 | ||
652 | switch (token) { | 659 | if (next) |
653 | case Opt_fsdefault: | 660 | len = next - from; |
654 | if (fsdefault) | 661 | else |
655 | goto out_opt_err; | 662 | len = strlen(from); |
656 | fsdefault = match_strdup(&args[0]); | 663 | |
657 | if (!fsdefault) | 664 | token = match_opt_prefix(from, len, &arg); |
658 | goto out_err; | 665 | if (token != Opt_error) { |
659 | break; | 666 | arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL); |
660 | case Opt_fsfloor: | 667 | rc = smack_add_opt(token, arg, mnt_opts); |
661 | if (fsfloor) | 668 | if (unlikely(rc)) { |
662 | goto out_opt_err; | 669 | kfree(arg); |
663 | fsfloor = match_strdup(&args[0]); | 670 | if (*mnt_opts) |
664 | if (!fsfloor) | 671 | smack_free_mnt_opts(*mnt_opts); |
665 | goto out_err; | 672 | *mnt_opts = NULL; |
666 | break; | 673 | return rc; |
667 | case Opt_fshat: | 674 | } |
668 | if (fshat) | 675 | } else { |
669 | goto out_opt_err; | 676 | if (!first) { // copy with preceding comma |
670 | fshat = match_strdup(&args[0]); | 677 | from--; |
671 | if (!fshat) | 678 | len++; |
672 | goto out_err; | 679 | } |
673 | break; | 680 | if (to != from) |
674 | case Opt_fsroot: | 681 | memmove(to, from, len); |
675 | if (fsroot) | 682 | to += len; |
676 | goto out_opt_err; | 683 | first = false; |
677 | fsroot = match_strdup(&args[0]); | ||
678 | if (!fsroot) | ||
679 | goto out_err; | ||
680 | break; | ||
681 | case Opt_fstransmute: | ||
682 | if (fstransmute) | ||
683 | goto out_opt_err; | ||
684 | fstransmute = match_strdup(&args[0]); | ||
685 | if (!fstransmute) | ||
686 | goto out_err; | ||
687 | break; | ||
688 | default: | ||
689 | rc = -EINVAL; | ||
690 | pr_warn("Smack: unknown mount option\n"); | ||
691 | goto out_err; | ||
692 | } | 684 | } |
685 | if (!from[len]) | ||
686 | break; | ||
687 | from += len + 1; | ||
693 | } | 688 | } |
694 | 689 | *to = '\0'; | |
695 | opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL); | ||
696 | if (!opts->mnt_opts) | ||
697 | goto out_err; | ||
698 | |||
699 | opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int), | ||
700 | GFP_KERNEL); | ||
701 | if (!opts->mnt_opts_flags) | ||
702 | goto out_err; | ||
703 | |||
704 | if (fsdefault) { | ||
705 | opts->mnt_opts[num_mnt_opts] = fsdefault; | ||
706 | opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT; | ||
707 | } | ||
708 | if (fsfloor) { | ||
709 | opts->mnt_opts[num_mnt_opts] = fsfloor; | ||
710 | opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT; | ||
711 | } | ||
712 | if (fshat) { | ||
713 | opts->mnt_opts[num_mnt_opts] = fshat; | ||
714 | opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT; | ||
715 | } | ||
716 | if (fsroot) { | ||
717 | opts->mnt_opts[num_mnt_opts] = fsroot; | ||
718 | opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT; | ||
719 | } | ||
720 | if (fstransmute) { | ||
721 | opts->mnt_opts[num_mnt_opts] = fstransmute; | ||
722 | opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT; | ||
723 | } | ||
724 | |||
725 | opts->num_mnt_opts = num_mnt_opts; | ||
726 | return 0; | 690 | return 0; |
727 | |||
728 | out_opt_err: | ||
729 | rc = -EINVAL; | ||
730 | pr_warn("Smack: duplicate mount options\n"); | ||
731 | |||
732 | out_err: | ||
733 | kfree(fsdefault); | ||
734 | kfree(fsfloor); | ||
735 | kfree(fshat); | ||
736 | kfree(fsroot); | ||
737 | kfree(fstransmute); | ||
738 | return rc; | ||
739 | } | 691 | } |
740 | 692 | ||
741 | /** | 693 | /** |
@@ -751,7 +703,7 @@ out_err: | |||
751 | * labels. | 703 | * labels. |
752 | */ | 704 | */ |
753 | static int smack_set_mnt_opts(struct super_block *sb, | 705 | static int smack_set_mnt_opts(struct super_block *sb, |
754 | struct security_mnt_opts *opts, | 706 | void *mnt_opts, |
755 | unsigned long kern_flags, | 707 | unsigned long kern_flags, |
756 | unsigned long *set_kern_flags) | 708 | unsigned long *set_kern_flags) |
757 | { | 709 | { |
@@ -760,9 +712,8 @@ static int smack_set_mnt_opts(struct super_block *sb, | |||
760 | struct superblock_smack *sp = sb->s_security; | 712 | struct superblock_smack *sp = sb->s_security; |
761 | struct inode_smack *isp; | 713 | struct inode_smack *isp; |
762 | struct smack_known *skp; | 714 | struct smack_known *skp; |
763 | int i; | 715 | struct smack_mnt_opts *opts = mnt_opts; |
764 | int num_opts = opts->num_mnt_opts; | 716 | bool transmute = false; |
765 | int transmute = 0; | ||
766 | 717 | ||
767 | if (sp->smk_flags & SMK_SB_INITIALIZED) | 718 | if (sp->smk_flags & SMK_SB_INITIALIZED) |
768 | return 0; | 719 | return 0; |
@@ -771,7 +722,7 @@ static int smack_set_mnt_opts(struct super_block *sb, | |||
771 | /* | 722 | /* |
772 | * Unprivileged mounts don't get to specify Smack values. | 723 | * Unprivileged mounts don't get to specify Smack values. |
773 | */ | 724 | */ |
774 | if (num_opts) | 725 | if (opts) |
775 | return -EPERM; | 726 | return -EPERM; |
776 | /* | 727 | /* |
777 | * Unprivileged mounts get root and default from the caller. | 728 | * Unprivileged mounts get root and default from the caller. |
@@ -787,48 +738,44 @@ static int smack_set_mnt_opts(struct super_block *sb, | |||
787 | if (sb->s_user_ns != &init_user_ns && | 738 | if (sb->s_user_ns != &init_user_ns && |
788 | sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC && | 739 | sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC && |
789 | sb->s_magic != RAMFS_MAGIC) { | 740 | sb->s_magic != RAMFS_MAGIC) { |
790 | transmute = 1; | 741 | transmute = true; |
791 | sp->smk_flags |= SMK_SB_UNTRUSTED; | 742 | sp->smk_flags |= SMK_SB_UNTRUSTED; |
792 | } | 743 | } |
793 | } | 744 | } |
794 | 745 | ||
795 | sp->smk_flags |= SMK_SB_INITIALIZED; | 746 | sp->smk_flags |= SMK_SB_INITIALIZED; |
796 | 747 | ||
797 | for (i = 0; i < num_opts; i++) { | 748 | if (opts) { |
798 | switch (opts->mnt_opts_flags[i]) { | 749 | if (opts->fsdefault) { |
799 | case FSDEFAULT_MNT: | 750 | skp = smk_import_entry(opts->fsdefault, 0); |
800 | skp = smk_import_entry(opts->mnt_opts[i], 0); | ||
801 | if (IS_ERR(skp)) | 751 | if (IS_ERR(skp)) |
802 | return PTR_ERR(skp); | 752 | return PTR_ERR(skp); |
803 | sp->smk_default = skp; | 753 | sp->smk_default = skp; |
804 | break; | 754 | } |
805 | case FSFLOOR_MNT: | 755 | if (opts->fsfloor) { |
806 | skp = smk_import_entry(opts->mnt_opts[i], 0); | 756 | skp = smk_import_entry(opts->fsfloor, 0); |
807 | if (IS_ERR(skp)) | 757 | if (IS_ERR(skp)) |
808 | return PTR_ERR(skp); | 758 | return PTR_ERR(skp); |
809 | sp->smk_floor = skp; | 759 | sp->smk_floor = skp; |
810 | break; | 760 | } |
811 | case FSHAT_MNT: | 761 | if (opts->fshat) { |
812 | skp = smk_import_entry(opts->mnt_opts[i], 0); | 762 | skp = smk_import_entry(opts->fshat, 0); |
813 | if (IS_ERR(skp)) | 763 | if (IS_ERR(skp)) |
814 | return PTR_ERR(skp); | 764 | return PTR_ERR(skp); |
815 | sp->smk_hat = skp; | 765 | sp->smk_hat = skp; |
816 | break; | 766 | } |
817 | case FSROOT_MNT: | 767 | if (opts->fsroot) { |
818 | skp = smk_import_entry(opts->mnt_opts[i], 0); | 768 | skp = smk_import_entry(opts->fsroot, 0); |
819 | if (IS_ERR(skp)) | 769 | if (IS_ERR(skp)) |
820 | return PTR_ERR(skp); | 770 | return PTR_ERR(skp); |
821 | sp->smk_root = skp; | 771 | sp->smk_root = skp; |
822 | break; | 772 | } |
823 | case FSTRANS_MNT: | 773 | if (opts->fstransmute) { |
824 | skp = smk_import_entry(opts->mnt_opts[i], 0); | 774 | skp = smk_import_entry(opts->fstransmute, 0); |
825 | if (IS_ERR(skp)) | 775 | if (IS_ERR(skp)) |
826 | return PTR_ERR(skp); | 776 | return PTR_ERR(skp); |
827 | sp->smk_root = skp; | 777 | sp->smk_root = skp; |
828 | transmute = 1; | 778 | transmute = true; |
829 | break; | ||
830 | default: | ||
831 | break; | ||
832 | } | 779 | } |
833 | } | 780 | } |
834 | 781 | ||
@@ -851,37 +798,6 @@ static int smack_set_mnt_opts(struct super_block *sb, | |||
851 | } | 798 | } |
852 | 799 | ||
853 | /** | 800 | /** |
854 | * smack_sb_kern_mount - Smack specific mount processing | ||
855 | * @sb: the file system superblock | ||
856 | * @flags: the mount flags | ||
857 | * @data: the smack mount options | ||
858 | * | ||
859 | * Returns 0 on success, an error code on failure | ||
860 | */ | ||
861 | static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) | ||
862 | { | ||
863 | int rc = 0; | ||
864 | char *options = data; | ||
865 | struct security_mnt_opts opts; | ||
866 | |||
867 | security_init_mnt_opts(&opts); | ||
868 | |||
869 | if (!options) | ||
870 | goto out; | ||
871 | |||
872 | rc = smack_parse_opts_str(options, &opts); | ||
873 | if (rc) | ||
874 | goto out_err; | ||
875 | |||
876 | out: | ||
877 | rc = smack_set_mnt_opts(sb, &opts, 0, NULL); | ||
878 | |||
879 | out_err: | ||
880 | security_free_mnt_opts(&opts); | ||
881 | return rc; | ||
882 | } | ||
883 | |||
884 | /** | ||
885 | * smack_sb_statfs - Smack check on statfs | 801 | * smack_sb_statfs - Smack check on statfs |
886 | * @dentry: identifies the file system in question | 802 | * @dentry: identifies the file system in question |
887 | * | 803 | * |
@@ -4673,11 +4589,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { | |||
4673 | 4589 | ||
4674 | LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security), | 4590 | LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security), |
4675 | LSM_HOOK_INIT(sb_free_security, smack_sb_free_security), | 4591 | LSM_HOOK_INIT(sb_free_security, smack_sb_free_security), |
4676 | LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data), | 4592 | LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts), |
4677 | LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount), | 4593 | LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts), |
4678 | LSM_HOOK_INIT(sb_statfs, smack_sb_statfs), | 4594 | LSM_HOOK_INIT(sb_statfs, smack_sb_statfs), |
4679 | LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts), | 4595 | LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts), |
4680 | LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str), | ||
4681 | 4596 | ||
4682 | LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds), | 4597 | LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds), |
4683 | 4598 | ||