diff options
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 175 | ||||
-rw-r--r-- | security/selinux/include/security.h | 5 |
2 files changed, 99 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, |
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 837ce420d2f6..f7d2f03781f2 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
@@ -35,6 +35,11 @@ | |||
35 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_POLCAP | 35 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_POLCAP |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | #define CONTEXT_MNT 0x01 | ||
39 | #define FSCONTEXT_MNT 0x02 | ||
40 | #define ROOTCONTEXT_MNT 0x04 | ||
41 | #define DEFCONTEXT_MNT 0x08 | ||
42 | |||
38 | struct netlbl_lsm_secattr; | 43 | struct netlbl_lsm_secattr; |
39 | 44 | ||
40 | extern int selinux_enabled; | 45 | extern int selinux_enabled; |