diff options
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 135 |
1 files changed, 113 insertions, 22 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 24caaeec8894..a91c961ba38b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -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; @@ -319,19 +320,53 @@ enum { Opt_context = 1, Opt_fscontext = 2, Opt_defcontext = 4, + Opt_rootcontext = 8, }; static match_table_t tokens = { {Opt_context, "context=%s"}, {Opt_fscontext, "fscontext=%s"}, {Opt_defcontext, "defcontext=%s"}, + {Opt_rootcontext, "rootcontext=%s"}, }; #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 may_context_mount_inode_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(sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__ASSOCIATE, NULL); + return rc; +} + static int try_context_mount(struct super_block *sb, void *data) { char *context = NULL, *defcontext = NULL; + char *fscontext = NULL, *rootcontext = NULL; const char *name; u32 sid; int alloc = 0, rc = 0, seen = 0; @@ -374,7 +409,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 +425,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; } @@ -405,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data) seen |= Opt_fscontext; break; + case Opt_rootcontext: + if (seen & Opt_rootcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_free; + } + rootcontext = match_strdup(&args[0]); + if (!rootcontext) { + rc = -ENOMEM; + goto out_free; + } + if (!alloc) + alloc = 1; + seen |= Opt_rootcontext; + break; + case Opt_defcontext: if (sbsec->behavior != SECURITY_FS_USE_XATTR) { rc = -EINVAL; @@ -441,6 +492,28 @@ static int try_context_mount(struct super_block *sb, void *data) if (!seen) goto out; + /* 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", + fscontext, 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; + } + + /* + * 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) { @@ -450,20 +523,38 @@ static int try_context_mount(struct super_block *sb, void *data) goto out_free; } - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); - if (rc) + if (!fscontext) { + rc = may_context_mount_sb_relabel(sid, sbsec, tsec); + if (rc) + goto out_free; + sbsec->sid = sid; + } else { + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + if (rc) + goto out_free; + } + sbsec->mntpoint_sid = sid; + + sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + } + + if (rootcontext) { + struct inode *inode = sb->s_root->d_inode; + struct inode_security_struct *isec = inode->i_security; + rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + rootcontext, sb->s_id, name, rc); goto out_free; + } - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELTO, NULL); + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); if (rc) goto out_free; - sbsec->sid = sid; - - if (seen & Opt_context) - sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + isec->sid = sid; + isec->initialized = 1; } if (defcontext) { @@ -478,13 +569,7 @@ static int try_context_mount(struct super_block *sb, void *data) if (sid == sbsec->def_sid) goto out_free; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); - if (rc) - goto out_free; - - rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, NULL); + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); if (rc) goto out_free; @@ -495,6 +580,8 @@ out_free: if (alloc) { kfree(context); kfree(defcontext); + kfree(fscontext); + kfree(rootcontext); } out: return rc; @@ -876,8 +963,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) { @@ -1843,7 +1933,8 @@ static inline int selinux_option(char *option, int len) { return (match_prefix("context=", sizeof("context=")-1, option, len) || match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || - match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); + match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) || + match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len)); } static inline void take_option(char **to, char *from, int *first, int len) |