Sorry for resending. I'm including the linux-kernel mailing list in the CC list.
This parameter has been introduced in order to properly detect when a
filesystem is unmounted, by setting an appropriate label to the
mountpoint inode, that can be seen after the operation is done.
The label set is not stored persistently, so that the inode will get
the previous one after a system reboot.
One possible application of this new feature is that it will be possible
to deny accesses on the path where a filesystem is mounted on if the last
is unmounted for example by malicious or compromised software.
This will prevent applications referring to a given path to see files
belonging to a different filesystem.
Signed-off-by: Roberto Sassu <[email protected]>
---
security/selinux/hooks.c | 70 +++++++++++++++++++++++++++++++++-
security/selinux/include/objsec.h | 1 +
security/selinux/include/security.h | 2 +
3 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 5c9f25b..2cc3a8a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -334,6 +334,7 @@ enum {
Opt_defcontext = 3,
Opt_rootcontext = 4,
Opt_labelsupport = 5,
+ Opt_umountcontext = 6,
};
static const match_table_t tokens = {
@@ -342,6 +343,7 @@ static const match_table_t tokens = {
{Opt_defcontext, DEFCONTEXT_STR "%s"},
{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
{Opt_labelsupport, LABELSUPP_STR},
+ {Opt_umountcontext, UMOUNTCONTEXT_STR "%s"},
{Opt_error, NULL},
};
@@ -496,6 +498,10 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
if (sbsec->flags & SE_SBLABELSUPP)
opts->num_mnt_opts++;
+ /* Check if the umountcontext flag is set */
+ if (sbsec->flags & UMOUNTCONTEXT_MNT)
+ opts->num_mnt_opts++;
+
opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
if (!opts->mnt_opts) {
rc = -ENOMEM;
@@ -544,6 +550,13 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
opts->mnt_opts[i] = NULL;
opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
}
+ if (sbsec->flags & UMOUNTCONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->umount_sid, &context, &len);
+ if (rc)
+ goto out_free;
+ opts->mnt_opts[i] = context;
+ opts->mnt_opts_flags[i++] = UMOUNTCONTEXT_MNT;
+ }
BUG_ON(i != opts->num_mnt_opts);
@@ -588,7 +601,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
struct inode *inode = sbsec->sb->s_root->d_inode;
struct inode_security_struct *root_isec = inode->i_security;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
- u32 defcontext_sid = 0;
+ u32 defcontext_sid = 0, umountcontext_sid = 0;
char **mount_options = opts->mnt_opts;
int *flags = opts->mnt_opts_flags;
int num_opts = opts->num_mnt_opts;
@@ -680,6 +693,16 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->flags |= DEFCONTEXT_MNT;
break;
+ case UMOUNTCONTEXT_MNT:
+ umountcontext_sid = sid;
+
+ if (bad_option(sbsec, UMOUNTCONTEXT_MNT,
+ sbsec->umount_sid, umountcontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= UMOUNTCONTEXT_MNT;
+
+ break;
default:
rc = -EINVAL;
goto out;
@@ -767,6 +790,15 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->def_sid = defcontext_sid;
}
+ if (umountcontext_sid) {
+ rc = may_context_mount_sb_relabel(umountcontext_sid,
+ sbsec, cred);
+ if (rc)
+ goto out;
+
+ sbsec->umount_sid = umountcontext_sid;
+ }
+
rc = sb_finish_set_opts(sb);
out:
mutex_unlock(&sbsec->lock);
@@ -809,6 +841,7 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
newsbsec->sid = oldsbsec->sid;
newsbsec->def_sid = oldsbsec->def_sid;
newsbsec->behavior = oldsbsec->behavior;
+ newsbsec->umount_sid = oldsbsec->umount_sid;
if (set_context) {
u32 sid = oldsbsec->mntpoint_sid;
@@ -841,6 +874,7 @@ static int selinux_parse_opts_str(char *options,
char *p;
char *context = NULL, *defcontext = NULL;
char *fscontext = NULL, *rootcontext = NULL;
+ char *umountcontext = NULL;
int rc, num_mnt_opts = 0;
opts->num_mnt_opts = 0;
@@ -909,6 +943,13 @@ static int selinux_parse_opts_str(char *options,
break;
case Opt_labelsupport:
break;
+ case Opt_umountcontext:
+ umountcontext = match_strdup(&args[0]);
+ if (!umountcontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
default:
rc = -EINVAL;
printk(KERN_WARNING "SELinux: unknown mount option\n");
@@ -945,6 +986,12 @@ static int selinux_parse_opts_str(char *options,
opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
}
+ if (umountcontext) {
+ opts->mnt_opts[num_mnt_opts] = umountcontext;
+ opts->mnt_opts_flags[num_mnt_opts++] = UMOUNTCONTEXT_MNT;
+ }
+
+
opts->num_mnt_opts = num_mnt_opts;
return 0;
@@ -953,6 +1000,7 @@ out_err:
kfree(defcontext);
kfree(fscontext);
kfree(rootcontext);
+ kfree(umountcontext);
return rc;
}
/*
@@ -1010,6 +1058,9 @@ static void selinux_write_opts(struct seq_file *m,
case DEFCONTEXT_MNT:
prefix = DEFCONTEXT_STR;
break;
+ case UMOUNTCONTEXT_MNT:
+ prefix = UMOUNTCONTEXT_STR;
+ break;
case SE_SBLABELSUPP:
seq_putc(m, ',');
seq_puts(m, LABELSUPP_STR);
@@ -2413,7 +2464,8 @@ static inline int selinux_option(char *option, int len)
match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
- match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
+ match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len) ||
+ match_prefix(UMOUNTCONTEXT_STR, sizeof(UMOUNTCONTEXT_STR)-1, option, len));
}
static inline void take_option(char **to, char *from, int *first, int len)
@@ -2538,9 +2590,21 @@ static int selinux_mount(char *dev_name,
static int selinux_umount(struct vfsmount *mnt, int flags)
{
const struct cred *cred = current_cred();
+ struct inode_security_struct *isec = mnt->mnt_mountpoint->d_inode->i_security;
+ struct superblock_security_struct *sbsec = mnt->mnt_sb->s_security;
+ int rc;
- return superblock_has_perm(cred, mnt->mnt_sb,
+ rc = superblock_has_perm(cred, mnt->mnt_sb,
FILESYSTEM__UNMOUNT, NULL);
+
+ if (!rc) {
+ mutex_lock(&isec->lock);
+ if (sbsec->flags & UMOUNTCONTEXT_MNT)
+ isec->sid = sbsec->umount_sid;
+ mutex_unlock(&isec->lock);
+ }
+
+ return rc;
}
/* inode security operations */
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 26c7eee..4b51ead 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -58,6 +58,7 @@ struct superblock_security_struct {
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
+ u32 umount_sid; /* SID assigned to the mountpoint inode */
unsigned int behavior; /* labeling behavior */
unsigned char flags; /* which mount options were specified */
struct mutex lock;
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 1f7c249..f7768cf 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -47,12 +47,14 @@
#define SE_SBINITIALIZED 0x10
#define SE_SBPROC 0x20
#define SE_SBLABELSUPP 0x40
+#define UMOUNTCONTEXT_MNT 0x80
#define CONTEXT_STR "context="
#define FSCONTEXT_STR "fscontext="
#define ROOTCONTEXT_STR "rootcontext="
#define DEFCONTEXT_STR "defcontext="
#define LABELSUPP_STR "seclabel"
+#define UMOUNTCONTEXT_STR "umountcontext="
struct netlbl_lsm_secattr;
--
1.7.2.3