summaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-03-25 03:53:42 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-03-25 03:53:42 -0300
commit03dd4cb26d967f9588437b0fc9cc0e8353322bb7 (patch)
treefa581f6dc1c0596391690d1f67eceef3af8246dc /security/selinux
parentd4e493caf788ef44982e131ff9c786546904d934 (diff)
Linux-libre 4.5-gnu
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/hooks.c200
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/objsec.h6
-rw-r--r--security/selinux/include/security.h3
-rw-r--r--security/selinux/nlmsgtab.c1
-rw-r--r--security/selinux/selinuxfs.c198
-rw-r--r--security/selinux/ss/services.c34
7 files changed, 300 insertions, 144 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d0cfaa9f1..f1ab71504 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -242,6 +242,72 @@ static int inode_alloc_security(struct inode *inode)
return 0;
}
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
+
+/*
+ * Try reloading inode security labels that have been marked as invalid. The
+ * @may_sleep parameter indicates when sleeping and thus reloading labels is
+ * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is
+ * invalid. The @opt_dentry parameter should be set to a dentry of the inode;
+ * when no dentry is available, set it to NULL instead.
+ */
+static int __inode_security_revalidate(struct inode *inode,
+ struct dentry *opt_dentry,
+ bool may_sleep)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ might_sleep_if(may_sleep);
+
+ if (isec->initialized == LABEL_INVALID) {
+ if (!may_sleep)
+ return -ECHILD;
+
+ /*
+ * Try reloading the inode security label. This will fail if
+ * @opt_dentry is NULL and no dentry for this inode can be
+ * found; in that case, continue using the old label.
+ */
+ inode_doinit_with_dentry(inode, opt_dentry);
+ }
+ return 0;
+}
+
+static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
+{
+ return inode->i_security;
+}
+
+static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
+{
+ int error;
+
+ error = __inode_security_revalidate(inode, NULL, !rcu);
+ if (error)
+ return ERR_PTR(error);
+ return inode->i_security;
+}
+
+/*
+ * Get the security label of an inode.
+ */
+static struct inode_security_struct *inode_security(struct inode *inode)
+{
+ __inode_security_revalidate(inode, NULL, true);
+ return inode->i_security;
+}
+
+/*
+ * Get the security label of a dentry's backing inode.
+ */
+static struct inode_security_struct *backing_inode_security(struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+
+ __inode_security_revalidate(inode, dentry, true);
+ return inode->i_security;
+}
+
static void inode_free_rcu(struct rcu_head *head)
{
struct inode_security_struct *isec;
@@ -345,8 +411,6 @@ static const char *labeling_behaviors[7] = {
"uses native labeling",
};
-static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
-
static inline int inode_doinit(struct inode *inode)
{
return inode_doinit_with_dentry(inode, NULL);
@@ -565,8 +629,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
}
if (sbsec->flags & ROOTCONTEXT_MNT) {
- struct inode *root = d_backing_inode(sbsec->sb->s_root);
- struct inode_security_struct *isec = root->i_security;
+ struct dentry *root = sbsec->sb->s_root;
+ struct inode_security_struct *isec = backing_inode_security(root);
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
@@ -621,8 +685,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
int rc = 0, i;
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
- struct inode *inode = d_backing_inode(sbsec->sb->s_root);
- struct inode_security_struct *root_isec = inode->i_security;
+ struct dentry *root = sbsec->sb->s_root;
+ struct inode_security_struct *root_isec = backing_inode_security(root);
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
char **mount_options = opts->mnt_opts;
@@ -802,7 +866,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
goto out;
root_isec->sid = rootcontext_sid;
- root_isec->initialized = 1;
+ root_isec->initialized = LABEL_INITIALIZED;
}
if (defcontext_sid) {
@@ -852,8 +916,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid)
goto mismatch;
if (oldflags & ROOTCONTEXT_MNT) {
- struct inode_security_struct *oldroot = d_backing_inode(oldsb->s_root)->i_security;
- struct inode_security_struct *newroot = d_backing_inode(newsb->s_root)->i_security;
+ struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root);
+ struct inode_security_struct *newroot = backing_inode_security(newsb->s_root);
if (oldroot->sid != newroot->sid)
goto mismatch;
}
@@ -903,17 +967,14 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
if (!set_fscontext)
newsbsec->sid = sid;
if (!set_rootcontext) {
- struct inode *newinode = d_backing_inode(newsb->s_root);
- struct inode_security_struct *newisec = newinode->i_security;
+ struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
newisec->sid = sid;
}
newsbsec->mntpoint_sid = sid;
}
if (set_rootcontext) {
- const struct inode *oldinode = d_backing_inode(oldsb->s_root);
- const struct inode_security_struct *oldisec = oldinode->i_security;
- struct inode *newinode = d_backing_inode(newsb->s_root);
- struct inode_security_struct *newisec = newinode->i_security;
+ const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root);
+ struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
newisec->sid = oldisec->sid;
}
@@ -1293,11 +1354,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
unsigned len = 0;
int rc = 0;
- if (isec->initialized)
+ if (isec->initialized == LABEL_INITIALIZED)
goto out;
mutex_lock(&isec->lock);
- if (isec->initialized)
+ if (isec->initialized == LABEL_INITIALIZED)
goto out_unlock;
sbsec = inode->i_sb->s_security;
@@ -1469,7 +1530,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
break;
}
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
out_unlock:
mutex_unlock(&isec->lock);
@@ -1640,6 +1701,7 @@ static inline int dentry_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
+ __inode_security_revalidate(inode, dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -1655,6 +1717,7 @@ static inline int path_has_perm(const struct cred *cred,
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = *path;
+ __inode_security_revalidate(inode, path->dentry, true);
return inode_has_perm(cred, inode, av, &ad);
}
@@ -1712,13 +1775,13 @@ out:
/*
* Determine the label for an inode that might be unioned.
*/
-static int selinux_determine_inode_label(const struct inode *dir,
+static int selinux_determine_inode_label(struct inode *dir,
const struct qstr *name,
u16 tclass,
u32 *_new_isid)
{
const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
- const struct inode_security_struct *dsec = dir->i_security;
+ const struct inode_security_struct *dsec = inode_security(dir);
const struct task_security_struct *tsec = current_security();
if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1747,7 +1810,7 @@ static int may_create(struct inode *dir,
struct common_audit_data ad;
int rc;
- dsec = dir->i_security;
+ dsec = inode_security(dir);
sbsec = dir->i_sb->s_security;
sid = tsec->sid;
@@ -1800,8 +1863,8 @@ static int may_link(struct inode *dir,
u32 av;
int rc;
- dsec = dir->i_security;
- isec = d_backing_inode(dentry)->i_security;
+ dsec = inode_security(dir);
+ isec = backing_inode_security(dentry);
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
@@ -1844,10 +1907,10 @@ static inline int may_rename(struct inode *old_dir,
int old_is_dir, new_is_dir;
int rc;
- old_dsec = old_dir->i_security;
- old_isec = d_backing_inode(old_dentry)->i_security;
+ old_dsec = inode_security(old_dir);
+ old_isec = backing_inode_security(old_dentry);
old_is_dir = d_is_dir(old_dentry);
- new_dsec = new_dir->i_security;
+ new_dsec = inode_security(new_dir);
ad.type = LSM_AUDIT_DATA_DENTRY;
@@ -1875,7 +1938,7 @@ static inline int may_rename(struct inode *old_dir,
if (rc)
return rc;
if (d_is_positive(new_dentry)) {
- new_isec = d_backing_inode(new_dentry)->i_security;
+ new_isec = backing_inode_security(new_dentry);
new_is_dir = d_is_dir(new_dentry);
rc = avc_has_perm(sid, new_isec->sid,
new_isec->sclass,
@@ -2011,8 +2074,8 @@ static int selinux_binder_transfer_file(struct task_struct *from,
{
u32 sid = task_sid(to);
struct file_security_struct *fsec = file->f_security;
- struct inode *inode = d_backing_inode(file->f_path.dentry);
- struct inode_security_struct *isec = inode->i_security;
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode_security_struct *isec = backing_inode_security(dentry);
struct common_audit_data ad;
int rc;
@@ -2028,7 +2091,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
return rc;
}
- if (unlikely(IS_PRIVATE(inode)))
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
@@ -2217,7 +2280,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
old_tsec = current_security();
new_tsec = bprm->cred->security;
- isec = inode->i_security;
+ isec = inode_security(inode);
/* Default to the current task SID. */
new_tsec->sid = old_tsec->sid;
@@ -2639,7 +2702,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
break;
case ROOTCONTEXT_MNT: {
struct inode_security_struct *root_isec;
- root_isec = d_backing_inode(sb->s_root)->i_security;
+ root_isec = backing_inode_security(sb->s_root);
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
goto out_bad_option;
@@ -2753,13 +2816,11 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
void **value, size_t *len)
{
const struct task_security_struct *tsec = current_security();
- struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 sid, newsid, clen;
int rc;
char *context;
- dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
sid = tsec->sid;
@@ -2777,7 +2838,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
struct inode_security_struct *isec = inode->i_security;
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
}
if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
@@ -2858,7 +2919,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = inode_security_rcu(inode, rcu);
+ if (IS_ERR(isec))
+ return PTR_ERR(isec);
return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
rcu ? MAY_NOT_BLOCK : 0);
@@ -2910,7 +2973,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
perms = file_mask_to_av(inode->i_mode, mask);
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK);
+ if (IS_ERR(isec))
+ return PTR_ERR(isec);
rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
audited = avc_audit_required(perms, &avd, rc,
@@ -2980,7 +3045,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = backing_inode_security(dentry);
struct superblock_security_struct *sbsec;
struct common_audit_data ad;
u32 newsid, sid = current_sid();
@@ -3057,7 +3122,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
int flags)
{
struct inode *inode = d_backing_inode(dentry);
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = backing_inode_security(dentry);
u32 newsid;
int rc;
@@ -3076,7 +3141,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
return;
}
@@ -3110,12 +3175,12 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
*
* Permission check is handled by selinux_inode_getxattr hook.
*/
-static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
{
u32 size;
int error;
char *context = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
@@ -3154,7 +3219,7 @@ out_nofree:
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->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
u32 newsid;
int rc;
@@ -3170,7 +3235,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
return 0;
}
@@ -3182,9 +3247,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
return len;
}
-static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
+static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security_novalidate(inode);
*secid = isec->sid;
}
@@ -3207,13 +3272,14 @@ static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file_inode(file);
struct file_security_struct *fsec = file->f_security;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
u32 sid = current_sid();
if (!mask)
/* No permission to check. Existence test. */
return 0;
+ isec = inode_security(inode);
if (sid == fsec->sid && fsec->isid == isec->sid &&
fsec->pseqno == avc_policy_seqno())
/* No change since file_open check. */
@@ -3242,7 +3308,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
struct common_audit_data ad;
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file_inode(file);
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
struct lsm_ioctlop_audit ioctl;
u32 ssid = cred_sid(cred);
int rc;
@@ -3506,7 +3572,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
struct inode_security_struct *isec;
fsec = file->f_security;
- isec = file_inode(file)->i_security;
+ isec = inode_security(file_inode(file));
/*
* Save inode label and policy sequence number
* at open-time so that selinux_file_permission
@@ -3624,7 +3690,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
*/
static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode_security(inode);
struct task_security_struct *tsec = new->security;
u32 sid = current_sid();
int ret;
@@ -3748,7 +3814,7 @@ static void selinux_task_to_inode(struct task_struct *p,
u32 sid = task_sid(p);
isec->sid = sid;
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
}
/* Returns error only if unable to parse addresses */
@@ -4065,7 +4131,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
const struct task_security_struct *tsec = current_security();
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec;
int err = 0;
@@ -4079,7 +4145,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
return err;
}
- isec->initialized = 1;
+ isec->initialized = LABEL_INITIALIZED;
if (sock->sk) {
sksec = sock->sk->sk_security;
@@ -4265,12 +4331,12 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
if (err)
return err;
- newisec = SOCK_INODE(newsock)->i_security;
+ newisec = inode_security_novalidate(SOCK_INODE(newsock));
- isec = SOCK_INODE(sock)->i_security;
+ isec = inode_security_novalidate(SOCK_INODE(sock));
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;
- newisec->initialized = 1;
+ newisec->initialized = LABEL_INITIALIZED;
return 0;
}
@@ -4605,7 +4671,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
static void selinux_sock_graft(struct sock *sk, struct socket *parent)
{
- struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+ struct inode_security_struct *isec =
+ inode_security_novalidate(SOCK_INODE(parent));
struct sk_security_struct *sksec = sk->sk_security;
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
@@ -4785,11 +4852,12 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
if (err) {
if (err == -EINVAL) {
- printk(KERN_WARNING
- "SELinux: unrecognized netlink message:"
- " protocol=%hu nlmsg_type=%hu sclass=%s\n",
+ pr_warn_ratelimited("SELinux: unrecognized netlink"
+ " message: protocol=%hu nlmsg_type=%hu sclass=%s"
+ " pig=%d comm=%s\n",
sk->sk_protocol, nlh->nlmsg_type,
- secclass_map[sksec->sclass - 1].name);
+ secclass_map[sksec->sclass - 1].name,
+ task_pid_nr(current), current->comm);
if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
@@ -5762,6 +5830,15 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
kfree(secdata);
}
+static void selinux_inode_invalidate_secctx(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+
+ mutex_lock(&isec->lock);
+ isec->initialized = LABEL_INVALID;
+ mutex_unlock(&isec->lock);
+}
+
/*
* called with inode->i_mutex locked
*/
@@ -5993,6 +6070,7 @@ static struct security_hook_list selinux_hooks[] = {
LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid),
LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
+ LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx),
LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 5a4eef59a..ef83c4b85 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -21,7 +21,7 @@ struct security_class_mapping secclass_map[] = {
{ "compute_av", "compute_create", "compute_member",
"check_context", "load_policy", "compute_relabel",
"compute_user", "setenforce", "setbool", "setsecparam",
- "setcheckreqprot", "read_policy", NULL } },
+ "setcheckreqprot", "read_policy", "validate_trans", NULL } },
{ "process",
{ "fork", "transition", "sigchld", "sigkill",
"sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 81fa718d5..a2ae05414 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -37,6 +37,12 @@ struct task_security_struct {
u32 sockcreate_sid; /* fscreate SID */
};
+enum label_initialized {
+ LABEL_MISSING, /* not initialized */
+ LABEL_INITIALIZED, /* inizialized */
+ LABEL_INVALID /* invalid */
+};
+
struct inode_security_struct {
struct inode *inode; /* back pointer to inode object */
union {
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 223e9fd15..38feb55d5 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -187,6 +187,9 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen,
int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
+int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass);
+
int security_bounded_transition(u32 oldsid, u32 newsid);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2bbb41822..8495b9368 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -83,6 +83,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
{ TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
{ DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
{ SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+ { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE },
};
static struct nlmsg_perm nlmsg_xfrm_perms[] =
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index c02da25d7..1b1fd27de 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -116,6 +116,7 @@ enum sel_inos {
SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
SEL_STATUS, /* export current status using mmap() */
SEL_POLICY, /* allow userspace to read the in kernel policy */
+ SEL_VALIDATE_TRANS, /* compute validatetrans decision */
SEL_INO_NEXT, /* The next inode number to use */
};
@@ -147,23 +148,16 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
ssize_t length;
int new_value;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -186,7 +180,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
}
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
#else
@@ -275,27 +269,20 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t length;
int new_value;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -313,7 +300,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
#else
@@ -393,9 +380,9 @@ static int sel_open_policy(struct inode *inode, struct file *filp)
goto err;
if (i_size_read(inode) != security_policydb_len()) {
- mutex_lock(&inode->i_mutex);
+ inode_lock(inode);
i_size_write(inode, security_policydb_len());
- mutex_unlock(&inode->i_mutex);
+ inode_unlock(inode);
}
rc = security_read_policy(&plm->data, &plm->len);
@@ -611,31 +598,24 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t length;
unsigned int new_value;
length = task_has_security(current, SECURITY__SETCHECKREQPROT);
if (length)
- goto out;
+ return length;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%u", &new_value) != 1)
@@ -644,7 +624,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
selinux_checkreqprot = new_value ? 1 : 0;
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
static const struct file_operations sel_checkreqprot_ops = {
@@ -653,6 +633,83 @@ static const struct file_operations sel_checkreqprot_ops = {
.llseek = generic_file_llseek,
};
+static ssize_t sel_write_validatetrans(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *oldcon = NULL, *newcon = NULL, *taskcon = NULL;
+ char *req = NULL;
+ u32 osid, nsid, tsid;
+ u16 tclass;
+ int rc;
+
+ rc = task_has_security(current, SECURITY__VALIDATE_TRANS);
+ if (rc)
+ goto out;
+
+ rc = -ENOMEM;
+ if (count >= PAGE_SIZE)
+ goto out;
+
+ /* No partial writes. */
+ rc = -EINVAL;
+ if (*ppos != 0)
+ goto out;
+
+ rc = -ENOMEM;
+ req = kzalloc(count + 1, GFP_KERNEL);
+ if (!req)
+ goto out;
+
+ rc = -EFAULT;
+ if (copy_from_user(req, buf, count))
+ goto out;
+
+ rc = -ENOMEM;
+ oldcon = kzalloc(count + 1, GFP_KERNEL);
+ if (!oldcon)
+ goto out;
+
+ newcon = kzalloc(count + 1, GFP_KERNEL);
+ if (!newcon)
+ goto out;
+
+ taskcon = kzalloc(count + 1, GFP_KERNEL);
+ if (!taskcon)
+ goto out;
+
+ rc = -EINVAL;
+ if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4)
+ goto out;
+
+ rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL);
+ if (rc)
+ goto out;
+
+ rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL);
+ if (rc)
+ goto out;
+
+ rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL);
+ if (rc)
+ goto out;
+
+ rc = security_validate_transition_user(osid, nsid, tsid, tclass);
+ if (!rc)
+ rc = count;
+out:
+ kfree(req);
+ kfree(oldcon);
+ kfree(newcon);
+ kfree(taskcon);
+ return rc;
+}
+
+static const struct file_operations sel_transition_ops = {
+ .write = sel_write_validatetrans,
+ .llseek = generic_file_llseek,
+};
+
/*
* Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
*/
@@ -1100,14 +1157,12 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (*ppos != 0)
goto out;
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
-
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page)) {
+ length = PTR_ERR(page);
+ page = NULL;
goto out;
+ }
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -1121,7 +1176,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
out:
mutex_unlock(&sel_mutex);
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
@@ -1154,14 +1209,12 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (*ppos != 0)
goto out;
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
-
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page)) {
+ length = PTR_ERR(page);
+ page = NULL;
goto out;
+ }
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -1176,7 +1229,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
out:
mutex_unlock(&sel_mutex);
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
@@ -1292,31 +1345,24 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t ret;
int new_value;
ret = task_has_security(current, SECURITY__SETSECPARAM);
if (ret)
- goto out;
+ return ret;
- ret = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- ret = -EINVAL;
if (*ppos != 0)
- goto out;
-
- ret = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- ret = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
ret = -EINVAL;
if (sscanf(page, "%u", &new_value) != 1)
@@ -1326,7 +1372,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
ret = count;
out:
- free_page((unsigned long)page);
+ kfree(page);
return ret;
}
@@ -1759,6 +1805,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
[SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
[SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
[SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
+ [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
+ S_IWUGO},
/* last one */ {""}
};
ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ebb5eb3c3..ebda97333 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -778,8 +778,8 @@ out:
return -EPERM;
}
-int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
- u16 orig_tclass)
+static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 orig_tclass, bool user)
{
struct context *ocontext;
struct context *ncontext;
@@ -794,11 +794,12 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
read_lock(&policy_rwlock);
- tclass = unmap_class(orig_tclass);
+ if (!user)
+ tclass = unmap_class(orig_tclass);
+ else
+ tclass = orig_tclass;
if (!tclass || tclass > policydb.p_classes.nprim) {
- printk(KERN_ERR "SELinux: %s: unrecognized class %d\n",
- __func__, tclass);
rc = -EINVAL;
goto out;
}
@@ -832,8 +833,13 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
while (constraint) {
if (!constraint_expr_eval(ocontext, ncontext, tcontext,
constraint->expr)) {
- rc = security_validtrans_handle_fail(ocontext, ncontext,
- tcontext, tclass);
+ if (user)
+ rc = -EPERM;
+ else
+ rc = security_validtrans_handle_fail(ocontext,
+ ncontext,
+ tcontext,
+ tclass);
goto out;
}
constraint = constraint->next;
@@ -844,6 +850,20 @@ out:
return rc;
}
+int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass)
+{
+ return security_compute_validatetrans(oldsid, newsid, tasksid,
+ tclass, true);
+}
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 orig_tclass)
+{
+ return security_compute_validatetrans(oldsid, newsid, tasksid,
+ orig_tclass, false);
+}
+
/*
* security_bounded_transition - check whether the given
* transition is directed to bounded, or not.