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.c156
1 files changed, 135 insertions, 21 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 5d92167dbe05..1d0b37af2444 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -48,6 +48,8 @@
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/tty.h>
@@ -410,11 +412,11 @@ static inline int inode_doinit(struct inode *inode)
enum {
Opt_error = -1,
- Opt_context = 1,
+ Opt_context = 0,
+ Opt_defcontext = 1,
Opt_fscontext = 2,
- Opt_defcontext = 3,
- Opt_rootcontext = 4,
- Opt_seclabel = 5,
+ Opt_rootcontext = 3,
+ Opt_seclabel = 4,
};
#define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg}
@@ -490,16 +492,10 @@ static int may_context_mount_inode_relabel(u32 sid,
return rc;
}
-static int selinux_is_sblabel_mnt(struct super_block *sb)
+static int selinux_is_genfs_special_handling(struct super_block *sb)
{
- struct superblock_security_struct *sbsec = sb->s_security;
-
- return sbsec->behavior == SECURITY_FS_USE_XATTR ||
- sbsec->behavior == SECURITY_FS_USE_TRANS ||
- sbsec->behavior == SECURITY_FS_USE_TASK ||
- sbsec->behavior == SECURITY_FS_USE_NATIVE ||
- /* Special handling. Genfs but also in-core setxattr handler */
- !strcmp(sb->s_type->name, "sysfs") ||
+ /* Special handling. Genfs but also in-core setxattr handler */
+ return !strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
@@ -509,6 +505,34 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
!strcmp(sb->s_type->name, "cgroup2")));
}
+static int selinux_is_sblabel_mnt(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ /*
+ * IMPORTANT: Double-check logic in this function when adding a new
+ * SECURITY_FS_USE_* definition!
+ */
+ BUILD_BUG_ON(SECURITY_FS_USE_MAX != 7);
+
+ switch (sbsec->behavior) {
+ case SECURITY_FS_USE_XATTR:
+ case SECURITY_FS_USE_TRANS:
+ case SECURITY_FS_USE_TASK:
+ case SECURITY_FS_USE_NATIVE:
+ return 1;
+
+ case SECURITY_FS_USE_GENFS:
+ return selinux_is_genfs_special_handling(sb);
+
+ /* Never allow relabeling on context mounts */
+ case SECURITY_FS_USE_MNTPOINT:
+ case SECURITY_FS_USE_NONE:
+ default:
+ return 0;
+ }
+}
+
static int sb_finish_set_opts(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
@@ -915,8 +939,11 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
/* if fs is reusing a sb, make sure that the contexts match */
- if (newsbsec->flags & SE_SBINITIALIZED)
+ if (newsbsec->flags & SE_SBINITIALIZED) {
+ if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context)
+ *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
return selinux_cmp_sb_context(oldsb, newsb);
+ }
mutex_lock(&newsbsec->lock);
@@ -1045,6 +1072,7 @@ static int show_sid(struct seq_file *m, u32 sid)
if (!rc) {
bool has_comma = context && strchr(context, ',');
+ seq_putc(m, '=');
if (has_comma)
seq_putc(m, '\"');
seq_escape(m, context, "\"\n\\");
@@ -1098,7 +1126,7 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
}
if (sbsec->flags & SBLABEL_MNT) {
seq_putc(m, ',');
- seq_puts(m, LABELSUPP_STR);
+ seq_puts(m, SECLABEL_STR);
}
return 0;
}
@@ -2717,6 +2745,76 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
FILESYSTEM__UNMOUNT, NULL);
}
+static int selinux_fs_context_dup(struct fs_context *fc,
+ struct fs_context *src_fc)
+{
+ const struct selinux_mnt_opts *src = src_fc->security;
+ struct selinux_mnt_opts *opts;
+
+ if (!src)
+ return 0;
+
+ fc->security = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+ if (!fc->security)
+ return -ENOMEM;
+
+ opts = fc->security;
+
+ if (src->fscontext) {
+ opts->fscontext = kstrdup(src->fscontext, GFP_KERNEL);
+ if (!opts->fscontext)
+ return -ENOMEM;
+ }
+ if (src->context) {
+ opts->context = kstrdup(src->context, GFP_KERNEL);
+ if (!opts->context)
+ return -ENOMEM;
+ }
+ if (src->rootcontext) {
+ opts->rootcontext = kstrdup(src->rootcontext, GFP_KERNEL);
+ if (!opts->rootcontext)
+ return -ENOMEM;
+ }
+ if (src->defcontext) {
+ opts->defcontext = kstrdup(src->defcontext, GFP_KERNEL);
+ if (!opts->defcontext)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static const struct fs_parameter_spec selinux_param_specs[] = {
+ fsparam_string(CONTEXT_STR, Opt_context),
+ fsparam_string(DEFCONTEXT_STR, Opt_defcontext),
+ fsparam_string(FSCONTEXT_STR, Opt_fscontext),
+ fsparam_string(ROOTCONTEXT_STR, Opt_rootcontext),
+ fsparam_flag (SECLABEL_STR, Opt_seclabel),
+ {}
+};
+
+static const struct fs_parameter_description selinux_fs_parameters = {
+ .name = "SELinux",
+ .specs = selinux_param_specs,
+};
+
+static int selinux_fs_context_parse_param(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ struct fs_parse_result result;
+ int opt, rc;
+
+ opt = fs_parse(fc, &selinux_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ rc = selinux_add_opt(opt, param->string, &fc->security);
+ if (!rc) {
+ param->string = NULL;
+ rc = 1;
+ }
+ return rc;
+}
+
/* inode security operations */
static int selinux_inode_alloc_security(struct inode *inode)
@@ -2881,9 +2979,8 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
if (IS_ERR(isec))
return PTR_ERR(isec);
- return avc_has_perm_flags(&selinux_state,
- sid, isec->sid, isec->sclass, FILE__READ, &ad,
- rcu ? MAY_NOT_BLOCK : 0);
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, FILE__READ, &ad);
}
static noinline int audit_inode_permission(struct inode *inode,
@@ -2938,7 +3035,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return PTR_ERR(isec);
rc = avc_has_perm_noaudit(&selinux_state,
- sid, isec->sid, isec->sclass, perms, 0, &avd);
+ sid, isec->sid, isec->sclass, perms,
+ (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0,
+ &avd);
audited = avc_audit_required(perms, &avd, rc,
from_access ? FILE__AUDIT_ACCESS : 0,
&denied);
@@ -3197,12 +3296,16 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
struct inode_security_struct *isec = inode_security_novalidate(inode);
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
u32 newsid;
int rc;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
+ if (!(sbsec->flags & SBLABEL_MNT))
+ return -EOPNOTSUPP;
+
if (!value || !size)
return -EACCES;
@@ -4445,7 +4548,7 @@ err_af:
}
/* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
- * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.rst
+ * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst
*/
static int selinux_socket_connect_helper(struct socket *sock,
struct sockaddr *address, int addrlen)
@@ -5034,6 +5137,9 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
return -EINVAL;
}
+ if (walk_size + len > addrlen)
+ return -EINVAL;
+
err = -EINVAL;
switch (optname) {
/* Bind checks */
@@ -6236,7 +6342,10 @@ static void selinux_inode_invalidate_secctx(struct inode *inode)
*/
static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
- return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0);
+ int rc = selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX,
+ ctx, ctxlen, 0);
+ /* Do not return error when suppressing label (SBLABEL_MNT not set). */
+ return rc == -EOPNOTSUPP ? 0 : rc;
}
/*
@@ -6562,6 +6671,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
+ LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup),
+ LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param),
+
LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
@@ -6807,6 +6919,8 @@ static __init int selinux_init(void)
else
pr_debug("SELinux: Starting in permissive mode\n");
+ fs_validate_description(&selinux_fs_parameters);
+
return 0;
}