summaryrefslogtreecommitdiffstats
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c135
1 files changed, 113 insertions, 22 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 24caaeec889..a91c961ba38 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)