aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2008-03-05 10:31:54 -0500
committerJames Morris <jmorris@namei.org>2008-03-05 16:40:53 -0500
commite0007529893c1c064be90bd21422ca0da4a0198e (patch)
treec2334ba940e682183a18d18972cf95bd3a3da46a /security/selinux/hooks.c
parent29e8c3c304b62f31b799565c9ee85d42bd163f80 (diff)
LSM/SELinux: Interfaces to allow FS to control mount options
Introduce new LSM interfaces to allow an FS to deal with their own mount options. This includes a new string parsing function exported from the LSM that an FS can use to get a security data blob and a new security data blob. This is particularly useful for an FS which uses binary mount data, like NFS, which does not pass strings into the vfs to be handled by the loaded LSM. Also fix a BUG() in both SELinux and SMACK when dealing with binary mount data. If the binary mount data is less than one page the copy_page() in security_sb_copy_data() can cause an illegal page fault and boom. Remove all NFSisms from the SELinux code since they were broken by past NFS changes. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Stephen Smalley <sds@tycho.nsa.gov> Acked-by: Casey Schaufler <casey@schaufler-ca.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c175
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 */
445static int selinux_get_mnt_opts(const struct super_block *sb, 445static 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
526out_free: 523out_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 */
560static int selinux_set_mnt_opts(struct super_block *sb, char **mount_options, 550static 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/* 803int selinux_parse_opts_str(char *options, struct security_mnt_opts *opts)
796 * string mount options parsing and call set the sbsec
797 */
798static 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
904build_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
922out: 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
924out_err: 914out_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 */
924static 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
941out:
942 rc = selinux_set_mnt_opts(sb, &opts);
943
944out_err:
945 security_free_mnt_opts(&opts);
946 return rc;
947}
931 948
932static inline u16 inode_mode_to_security_class(umode_t mode) 949static 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
2256static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy) 2273static 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,