diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 175 |
1 files changed, 94 insertions, 81 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 75c2e99bfb81..4bf4807f2d44 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -443,8 +443,7 @@ out: | |||
| 443 | * mount options, or whatever. | 443 | * mount options, or whatever. |
| 444 | */ | 444 | */ |
| 445 | static int selinux_get_mnt_opts(const struct super_block *sb, | 445 | static int selinux_get_mnt_opts(const struct super_block *sb, |
| 446 | char ***mount_options, int **mnt_opts_flags, | 446 | struct security_mnt_opts *opts) |
| 447 | int *num_opts) | ||
| 448 | { | 447 | { |
| 449 | int rc = 0, i; | 448 | int rc = 0, i; |
| 450 | struct superblock_security_struct *sbsec = sb->s_security; | 449 | struct superblock_security_struct *sbsec = sb->s_security; |
| @@ -452,9 +451,7 @@ static int selinux_get_mnt_opts(const struct super_block *sb, | |||
| 452 | u32 len; | 451 | u32 len; |
| 453 | char tmp; | 452 | char tmp; |
| 454 | 453 | ||
| 455 | *num_opts = 0; | 454 | security_init_mnt_opts(opts); |
| 456 | *mount_options = NULL; | ||
| 457 | *mnt_opts_flags = NULL; | ||
| 458 | 455 | ||
| 459 | if (!sbsec->initialized) | 456 | if (!sbsec->initialized) |
| 460 | return -EINVAL; | 457 | return -EINVAL; |
| @@ -470,18 +467,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb, | |||
| 470 | /* count the number of mount options for this sb */ | 467 | /* count the number of mount options for this sb */ |
| 471 | for (i = 0; i < 8; i++) { | 468 | for (i = 0; i < 8; i++) { |
| 472 | if (tmp & 0x01) | 469 | if (tmp & 0x01) |
| 473 | (*num_opts)++; | 470 | opts->num_mnt_opts++; |
| 474 | tmp >>= 1; | 471 | tmp >>= 1; |
| 475 | } | 472 | } |
| 476 | 473 | ||
| 477 | *mount_options = kcalloc(*num_opts, sizeof(char *), GFP_ATOMIC); | 474 | opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC); |
| 478 | if (!*mount_options) { | 475 | if (!opts->mnt_opts) { |
| 479 | rc = -ENOMEM; | 476 | rc = -ENOMEM; |
| 480 | goto out_free; | 477 | goto out_free; |
| 481 | } | 478 | } |
| 482 | 479 | ||
| 483 | *mnt_opts_flags = kcalloc(*num_opts, sizeof(int), GFP_ATOMIC); | 480 | opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC); |
| 484 | if (!*mnt_opts_flags) { | 481 | if (!opts->mnt_opts_flags) { |
| 485 | rc = -ENOMEM; | 482 | rc = -ENOMEM; |
| 486 | goto out_free; | 483 | goto out_free; |
| 487 | } | 484 | } |
| @@ -491,22 +488,22 @@ static int selinux_get_mnt_opts(const struct super_block *sb, | |||
| 491 | rc = security_sid_to_context(sbsec->sid, &context, &len); | 488 | rc = security_sid_to_context(sbsec->sid, &context, &len); |
| 492 | if (rc) | 489 | if (rc) |
| 493 | goto out_free; | 490 | goto out_free; |
| 494 | (*mount_options)[i] = context; | 491 | opts->mnt_opts[i] = context; |
| 495 | (*mnt_opts_flags)[i++] = FSCONTEXT_MNT; | 492 | opts->mnt_opts_flags[i++] = FSCONTEXT_MNT; |
| 496 | } | 493 | } |
| 497 | if (sbsec->flags & CONTEXT_MNT) { | 494 | if (sbsec->flags & CONTEXT_MNT) { |
| 498 | rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); | 495 | rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len); |
| 499 | if (rc) | 496 | if (rc) |
| 500 | goto out_free; | 497 | goto out_free; |
| 501 | (*mount_options)[i] = context; | 498 | opts->mnt_opts[i] = context; |
| 502 | (*mnt_opts_flags)[i++] = CONTEXT_MNT; | 499 | opts->mnt_opts_flags[i++] = CONTEXT_MNT; |
| 503 | } | 500 | } |
| 504 | if (sbsec->flags & DEFCONTEXT_MNT) { | 501 | if (sbsec->flags & DEFCONTEXT_MNT) { |
| 505 | rc = security_sid_to_context(sbsec->def_sid, &context, &len); | 502 | rc = security_sid_to_context(sbsec->def_sid, &context, &len); |
| 506 | if (rc) | 503 | if (rc) |
| 507 | goto out_free; | 504 | goto out_free; |
| 508 | (*mount_options)[i] = context; | 505 | opts->mnt_opts[i] = context; |
| 509 | (*mnt_opts_flags)[i++] = DEFCONTEXT_MNT; | 506 | opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT; |
| 510 | } | 507 | } |
| 511 | if (sbsec->flags & ROOTCONTEXT_MNT) { | 508 | if (sbsec->flags & ROOTCONTEXT_MNT) { |
| 512 | struct inode *root = sbsec->sb->s_root->d_inode; | 509 | struct inode *root = sbsec->sb->s_root->d_inode; |
| @@ -515,24 +512,16 @@ static int selinux_get_mnt_opts(const struct super_block *sb, | |||
| 515 | rc = security_sid_to_context(isec->sid, &context, &len); | 512 | rc = security_sid_to_context(isec->sid, &context, &len); |
| 516 | if (rc) | 513 | if (rc) |
| 517 | goto out_free; | 514 | goto out_free; |
| 518 | (*mount_options)[i] = context; | 515 | opts->mnt_opts[i] = context; |
| 519 | (*mnt_opts_flags)[i++] = ROOTCONTEXT_MNT; | 516 | opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT; |
| 520 | } | 517 | } |
| 521 | 518 | ||
| 522 | BUG_ON(i != *num_opts); | 519 | BUG_ON(i != opts->num_mnt_opts); |
| 523 | 520 | ||
| 524 | return 0; | 521 | return 0; |
| 525 | 522 | ||
| 526 | out_free: | 523 | out_free: |
| 527 | /* don't leak context string if security_sid_to_context had an error */ | 524 | security_free_mnt_opts(opts); |
| 528 | if (*mount_options && i) | ||
| 529 | for (; i > 0; i--) | ||
| 530 | kfree((*mount_options)[i-1]); | ||
| 531 | kfree(*mount_options); | ||
| 532 | *mount_options = NULL; | ||
| 533 | kfree(*mnt_opts_flags); | ||
| 534 | *mnt_opts_flags = NULL; | ||
| 535 | *num_opts = 0; | ||
| 536 | return rc; | 525 | return rc; |
| 537 | } | 526 | } |
| 538 | 527 | ||
| @@ -553,12 +542,13 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag, | |||
| 553 | return 1; | 542 | return 1; |
| 554 | return 0; | 543 | return 0; |
| 555 | } | 544 | } |
| 545 | |||
| 556 | /* | 546 | /* |
| 557 | * Allow filesystems with binary mount data to explicitly set mount point | 547 | * Allow filesystems with binary mount data to explicitly set mount point |
| 558 | * labeling information. | 548 | * labeling information. |
| 559 | */ | 549 | */ |
| 560 | static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, | 550 | static int selinux_set_mnt_opts(struct super_block *sb, |
| 561 | int *flags, int num_opts) | 551 | struct security_mnt_opts *opts) |
| 562 | { | 552 | { |
| 563 | int rc = 0, i; | 553 | int rc = 0, i; |
| 564 | struct task_security_struct *tsec = current->security; | 554 | struct task_security_struct *tsec = current->security; |
| @@ -568,6 +558,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, | |||
| 568 | struct inode_security_struct *root_isec = inode->i_security; | 558 | struct inode_security_struct *root_isec = inode->i_security; |
| 569 | u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; | 559 | u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; |
| 570 | u32 defcontext_sid = 0; | 560 | u32 defcontext_sid = 0; |
| 561 | char **mount_options = opts->mnt_opts; | ||
| 562 | int *flags = opts->mnt_opts_flags; | ||
| 563 | int num_opts = opts->num_mnt_opts; | ||
| 571 | 564 | ||
| 572 | mutex_lock(&sbsec->lock); | 565 | mutex_lock(&sbsec->lock); |
| 573 | 566 | ||
| @@ -589,6 +582,21 @@ static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, | |||
| 589 | } | 582 | } |
| 590 | 583 | ||
| 591 | /* | 584 | /* |
| 585 | * Binary mount data FS will come through this function twice. Once | ||
| 586 | * from an explicit call and once from the generic calls from the vfs. | ||
| 587 | * Since the generic VFS calls will not contain any security mount data | ||
| 588 | * we need to skip the double mount verification. | ||
| 589 | * | ||
| 590 | * This does open a hole in which we will not notice if the first | ||
| 591 | * mount using this sb set explict options and a second mount using | ||
| 592 | * this sb does not set any security options. (The first options | ||
| 593 | * will be used for both mounts) | ||
| 594 | */ | ||
| 595 | if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) | ||
| 596 | && (num_opts == 0)) | ||
| 597 | goto out; | ||
| 598 | |||
| 599 | /* | ||
| 592 | * parse the mount options, check if they are valid sids. | 600 | * parse the mount options, check if they are valid sids. |
| 593 | * also check if someone is trying to mount the same sb more | 601 | * also check if someone is trying to mount the same sb more |
| 594 | * than once with different security options. | 602 | * than once with different security options. |
| @@ -792,43 +800,14 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb, | |||
| 792 | mutex_unlock(&newsbsec->lock); | 800 | mutex_unlock(&newsbsec->lock); |
| 793 | } | 801 | } |
| 794 | 802 | ||
| 795 | /* | 803 | int selinux_parse_opts_str(char *options, struct security_mnt_opts *opts) |
| 796 | * string mount options parsing and call set the sbsec | ||
| 797 | */ | ||
| 798 | static int superblock_doinit(struct super_block *sb, void *data) | ||
| 799 | { | 804 | { |
| 805 | char *p; | ||
| 800 | char *context = NULL, *defcontext = NULL; | 806 | char *context = NULL, *defcontext = NULL; |
| 801 | char *fscontext = NULL, *rootcontext = NULL; | 807 | char *fscontext = NULL, *rootcontext = NULL; |
| 802 | int rc = 0; | 808 | int rc, num_mnt_opts = 0; |
| 803 | char *p, *options = data; | ||
| 804 | /* selinux only know about a fixed number of mount options */ | ||
| 805 | char *mnt_opts[NUM_SEL_MNT_OPTS]; | ||
| 806 | int mnt_opts_flags[NUM_SEL_MNT_OPTS], num_mnt_opts = 0; | ||
| 807 | |||
| 808 | if (!data) | ||
| 809 | goto out; | ||
| 810 | 809 | ||
| 811 | /* with the nfs patch this will become a goto out; */ | 810 | opts->num_mnt_opts = 0; |
| 812 | if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { | ||
| 813 | const char *name = sb->s_type->name; | ||
| 814 | /* NFS we understand. */ | ||
| 815 | if (!strcmp(name, "nfs")) { | ||
| 816 | struct nfs_mount_data *d = data; | ||
| 817 | |||
| 818 | if (d->version != NFS_MOUNT_VERSION) | ||
| 819 | goto out; | ||
| 820 | |||
| 821 | if (d->context[0]) { | ||
| 822 | context = kstrdup(d->context, GFP_KERNEL); | ||
| 823 | if (!context) { | ||
| 824 | rc = -ENOMEM; | ||
| 825 | goto out; | ||
| 826 | } | ||
| 827 | } | ||
| 828 | goto build_flags; | ||
| 829 | } else | ||
| 830 | goto out; | ||
| 831 | } | ||
| 832 | 811 | ||
| 833 | /* Standard string-based options. */ | 812 | /* Standard string-based options. */ |
| 834 | while ((p = strsep(&options, "|")) != NULL) { | 813 | while ((p = strsep(&options, "|")) != NULL) { |
| @@ -901,26 +880,37 @@ static int superblock_doinit(struct super_block *sb, void *data) | |||
| 901 | } | 880 | } |
| 902 | } | 881 | } |
| 903 | 882 | ||
| 904 | build_flags: | 883 | rc = -ENOMEM; |
| 884 | opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC); | ||
| 885 | if (!opts->mnt_opts) | ||
| 886 | goto out_err; | ||
| 887 | |||
| 888 | opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC); | ||
| 889 | if (!opts->mnt_opts_flags) { | ||
| 890 | kfree(opts->mnt_opts); | ||
| 891 | goto out_err; | ||
| 892 | } | ||
| 893 | |||
| 905 | if (fscontext) { | 894 | if (fscontext) { |
| 906 | mnt_opts[num_mnt_opts] = fscontext; | 895 | opts->mnt_opts[num_mnt_opts] = fscontext; |
| 907 | mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT; | 896 | opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT; |
| 908 | } | 897 | } |
| 909 | if (context) { | 898 | if (context) { |
| 910 | mnt_opts[num_mnt_opts] = context; | 899 | opts->mnt_opts[num_mnt_opts] = context; |
| 911 | mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT; | 900 | opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT; |
| 912 | } | 901 | } |
| 913 | if (rootcontext) { | 902 | if (rootcontext) { |
| 914 | mnt_opts[num_mnt_opts] = rootcontext; | 903 | opts->mnt_opts[num_mnt_opts] = rootcontext; |
| 915 | mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT; | 904 | opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT; |
| 916 | } | 905 | } |
| 917 | if (defcontext) { | 906 | if (defcontext) { |
| 918 | mnt_opts[num_mnt_opts] = defcontext; | 907 | opts->mnt_opts[num_mnt_opts] = defcontext; |
| 919 | mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT; | 908 | opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT; |
| 920 | } | 909 | } |
| 921 | 910 | ||
| 922 | out: | 911 | opts->num_mnt_opts = num_mnt_opts; |
| 923 | rc = selinux_set_mnt_opts(sb, mnt_opts, mnt_opts_flags, num_mnt_opts); | 912 | return 0; |
| 913 | |||
| 924 | out_err: | 914 | out_err: |
| 925 | kfree(context); | 915 | kfree(context); |
| 926 | kfree(defcontext); | 916 | kfree(defcontext); |
| @@ -928,6 +918,33 @@ out_err: | |||
| 928 | kfree(rootcontext); | 918 | kfree(rootcontext); |
| 929 | return rc; | 919 | return rc; |
| 930 | } | 920 | } |
| 921 | /* | ||
| 922 | * string mount options parsing and call set the sbsec | ||
| 923 | */ | ||
| 924 | static int superblock_doinit(struct super_block *sb, void *data) | ||
| 925 | { | ||
| 926 | int rc = 0; | ||
| 927 | char *options = data; | ||
| 928 | struct security_mnt_opts opts; | ||
| 929 | |||
| 930 | security_init_mnt_opts(&opts); | ||
| 931 | |||
| 932 | if (!data) | ||
| 933 | goto out; | ||
| 934 | |||
| 935 | BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA); | ||
| 936 | |||
| 937 | rc = selinux_parse_opts_str(options, &opts); | ||
| 938 | if (rc) | ||
| 939 | goto out_err; | ||
| 940 | |||
| 941 | out: | ||
| 942 | rc = selinux_set_mnt_opts(sb, &opts); | ||
| 943 | |||
| 944 | out_err: | ||
| 945 | security_free_mnt_opts(&opts); | ||
| 946 | return rc; | ||
| 947 | } | ||
| 931 | 948 | ||
| 932 | static inline u16 inode_mode_to_security_class(umode_t mode) | 949 | static inline u16 inode_mode_to_security_class(umode_t mode) |
| 933 | { | 950 | { |
| @@ -2253,7 +2270,7 @@ static inline void take_selinux_option(char **to, char *from, int *first, | |||
| 2253 | } | 2270 | } |
| 2254 | } | 2271 | } |
| 2255 | 2272 | ||
| 2256 | static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy) | 2273 | static int selinux_sb_copy_data(char *orig, char *copy) |
| 2257 | { | 2274 | { |
| 2258 | int fnosec, fsec, rc = 0; | 2275 | int fnosec, fsec, rc = 0; |
| 2259 | char *in_save, *in_curr, *in_end; | 2276 | char *in_save, *in_curr, *in_end; |
| @@ -2263,12 +2280,6 @@ static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void | |||
| 2263 | in_curr = orig; | 2280 | in_curr = orig; |
| 2264 | sec_curr = copy; | 2281 | sec_curr = copy; |
| 2265 | 2282 | ||
| 2266 | /* Binary mount data: just copy */ | ||
| 2267 | if (type->fs_flags & FS_BINARY_MOUNTDATA) { | ||
| 2268 | copy_page(sec_curr, in_curr); | ||
| 2269 | goto out; | ||
| 2270 | } | ||
| 2271 | |||
| 2272 | nosec = (char *)get_zeroed_page(GFP_KERNEL); | 2283 | nosec = (char *)get_zeroed_page(GFP_KERNEL); |
| 2273 | if (!nosec) { | 2284 | if (!nosec) { |
| 2274 | rc = -ENOMEM; | 2285 | rc = -ENOMEM; |
| @@ -5251,6 +5262,8 @@ static struct security_operations selinux_ops = { | |||
| 5251 | .sb_get_mnt_opts = selinux_get_mnt_opts, | 5262 | .sb_get_mnt_opts = selinux_get_mnt_opts, |
| 5252 | .sb_set_mnt_opts = selinux_set_mnt_opts, | 5263 | .sb_set_mnt_opts = selinux_set_mnt_opts, |
| 5253 | .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, | 5264 | .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, |
| 5265 | .sb_parse_opts_str = selinux_parse_opts_str, | ||
| 5266 | |||
| 5254 | 5267 | ||
| 5255 | .inode_alloc_security = selinux_inode_alloc_security, | 5268 | .inode_alloc_security = selinux_inode_alloc_security, |
| 5256 | .inode_free_security = selinux_inode_free_security, | 5269 | .inode_free_security = selinux_inode_free_security, |
