[PATCH] SELinux: decouple fscontext/context mount options
Eric Paris [Mon, 10 Jul 2006 11:43:53 +0000 (04:43 -0700)]
Remove the conflict between fscontext and context mount options.  If
context= is specified without fscontext it will operate just as before, if
both are specified we will use mount point labeling and all inodes will get
the label specified by context=.  The superblock will be labeled with the
label of fscontext=, thus affecting operations which check the superblock
security context, such as associate permissions.

Signed-off-by: Eric Paris <eparis@parisplace.org>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: James Morris <jmorris@namei.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

security/selinux/hooks.c
security/selinux/include/objsec.h

index 24caaee..7e101db 100644 (file)
@@ -246,6 +246,7 @@ static int superblock_alloc_security(struct super_block *sb)
        sbsec->sb = sb;
        sbsec->sid = SECINITSID_UNLABELED;
        sbsec->def_sid = SECINITSID_FILE;
+       sbsec->mntpoint_sid = SECINITSID_UNLABELED;
        sb->s_security = sbsec;
 
        return 0;
@@ -329,9 +330,26 @@ static match_table_t tokens = {
 
 #define SEL_MOUNT_FAIL_MSG "SELinux:  duplicate or incompatible mount options\n"
 
+static int may_context_mount_sb_relabel(u32 sid,
+                       struct superblock_security_struct *sbsec,
+                       struct task_security_struct *tsec)
+{
+       int rc;
+
+       rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELFROM, NULL);
+       if (rc)
+               return rc;
+
+       rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+                         FILESYSTEM__RELABELTO, NULL);
+       return rc;
+}
+
 static int try_context_mount(struct super_block *sb, void *data)
 {
        char *context = NULL, *defcontext = NULL;
+       char *fscontext = NULL;
        const char *name;
        u32 sid;
        int alloc = 0, rc = 0, seen = 0;
@@ -374,7 +392,7 @@ static int try_context_mount(struct super_block *sb, void *data)
 
                        switch (token) {
                        case Opt_context:
-                               if (seen) {
+                               if (seen & (Opt_context|Opt_defcontext)) {
                                        rc = -EINVAL;
                                        printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
                                        goto out_free;
@@ -390,13 +408,13 @@ static int try_context_mount(struct super_block *sb, void *data)
                                break;
 
                        case Opt_fscontext:
-                               if (seen & (Opt_context|Opt_fscontext)) {
+                               if (seen & Opt_fscontext) {
                                        rc = -EINVAL;
                                        printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
                                        goto out_free;
                                }
-                               context = match_strdup(&args[0]);
-                               if (!context) {
+                               fscontext = match_strdup(&args[0]);
+                               if (!fscontext) {
                                        rc = -ENOMEM;
                                        goto out_free;
                                }
@@ -441,29 +459,46 @@ static int try_context_mount(struct super_block *sb, void *data)
        if (!seen)
                goto out;
 
-       if (context) {
-               rc = security_context_to_sid(context, strlen(context), &sid);
+       /* sets the context of the superblock for the fs being mounted. */
+       if (fscontext) {
+               rc = security_context_to_sid(fscontext, strlen(fscontext), &sid);
                if (rc) {
                        printk(KERN_WARNING "SELinux: security_context_to_sid"
                               "(%s) failed for (dev %s, type %s) errno=%d\n",
-                              context, sb->s_id, name, rc);
+                              fscontext, sb->s_id, name, rc);
                        goto out_free;
                }
 
-               rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELFROM, NULL);
+               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
                if (rc)
                        goto out_free;
 
-               rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
-                                 FILESYSTEM__RELABELTO, NULL);
+               sbsec->sid = sid;
+       }
+
+       /*
+        * Switch to using mount point labeling behavior.
+        * sets the label used on all file below the mountpoint, and will set
+        * the superblock context if not already set.
+        */
+       if (context) {
+               rc = security_context_to_sid(context, strlen(context), &sid);
+               if (rc) {
+                       printk(KERN_WARNING "SELinux: security_context_to_sid"
+                              "(%s) failed for (dev %s, type %s) errno=%d\n",
+                              context, sb->s_id, name, rc);
+                       goto out_free;
+               }
+
+               rc = may_context_mount_sb_relabel(sid, sbsec, tsec);
                if (rc)
                        goto out_free;
 
-               sbsec->sid = sid;
+               if (!fscontext)
+                       sbsec->sid = sid;
+               sbsec->mntpoint_sid = sid;
 
-               if (seen & Opt_context)
-                       sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+               sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
        }
 
        if (defcontext) {
@@ -495,6 +530,7 @@ out_free:
        if (alloc) {
                kfree(context);
                kfree(defcontext);
+               kfree(fscontext);
        }
 out:
        return rc;
@@ -876,8 +912,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        goto out;
                isec->sid = sid;
                break;
+       case SECURITY_FS_USE_MNTPOINT:
+               isec->sid = sbsec->mntpoint_sid;
+               break;
        default:
-               /* Default to the fs SID. */
+               /* Default to the fs superblock SID. */
                isec->sid = sbsec->sid;
 
                if (sbsec->proc) {
index cf54a30..9401788 100644 (file)
@@ -57,8 +57,9 @@ struct file_security_struct {
 struct superblock_security_struct {
        struct super_block *sb;         /* back pointer to sb object */
        struct list_head list;          /* list of superblock_security_struct */
-       u32 sid;              /* SID of file system */
+       u32 sid;                        /* SID of file system superblock */
        u32 def_sid;                    /* default SID for labeling */
+       u32 mntpoint_sid;               /* SECURITY_FS_USE_MNTPOINT context for files */
        unsigned int behavior;          /* labeling behavior */
        unsigned char initialized;      /* initialization flag */
        unsigned char proc;             /* proc fs */