Merge branch 'master' of git://git.infradead.org/users/eparis/selinux into next
This commit is contained in:
@@ -24,9 +24,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ext2_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/xattr.h>
|
||||
@@ -36,14 +38,15 @@
|
||||
#include <linux/mman.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/tty.h>
|
||||
@@ -70,7 +73,6 @@
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/selinux.h>
|
||||
@@ -1120,39 +1122,35 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int selinux_proc_get_sid(struct proc_dir_entry *de,
|
||||
static int selinux_proc_get_sid(struct dentry *dentry,
|
||||
u16 tclass,
|
||||
u32 *sid)
|
||||
{
|
||||
int buflen, rc;
|
||||
char *buffer, *path, *end;
|
||||
int rc;
|
||||
char *buffer, *path;
|
||||
|
||||
buffer = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
buflen = PAGE_SIZE;
|
||||
end = buffer+buflen;
|
||||
*--end = '\0';
|
||||
buflen--;
|
||||
path = end-1;
|
||||
*path = '/';
|
||||
while (de && de != de->parent) {
|
||||
buflen -= de->namelen + 1;
|
||||
if (buflen < 0)
|
||||
break;
|
||||
end -= de->namelen;
|
||||
memcpy(end, de->name, de->namelen);
|
||||
*--end = '/';
|
||||
path = end;
|
||||
de = de->parent;
|
||||
path = dentry_path_raw(dentry, buffer, PAGE_SIZE);
|
||||
if (IS_ERR(path))
|
||||
rc = PTR_ERR(path);
|
||||
else {
|
||||
/* each process gets a /proc/PID/ entry. Strip off the
|
||||
* PID part to get a valid selinux labeling.
|
||||
* e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */
|
||||
while (path[1] >= '0' && path[1] <= '9') {
|
||||
path[1] = '/';
|
||||
path++;
|
||||
}
|
||||
rc = security_genfs_sid("proc", path, tclass, sid);
|
||||
}
|
||||
rc = security_genfs_sid("proc", path, tclass, sid);
|
||||
free_page((unsigned long)buffer);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
static int selinux_proc_get_sid(struct proc_dir_entry *de,
|
||||
static int selinux_proc_get_sid(struct dentry *dentry,
|
||||
u16 tclass,
|
||||
u32 *sid)
|
||||
{
|
||||
@@ -1300,10 +1298,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
|
||||
/* Try to obtain a transition SID. */
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
rc = security_transition_sid(isec->task_sid,
|
||||
sbsec->sid,
|
||||
isec->sclass,
|
||||
&sid);
|
||||
rc = security_transition_sid(isec->task_sid, sbsec->sid,
|
||||
isec->sclass, NULL, &sid);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
isec->sid = sid;
|
||||
@@ -1316,10 +1312,9 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
isec->sid = sbsec->sid;
|
||||
|
||||
if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
|
||||
struct proc_inode *proci = PROC_I(inode);
|
||||
if (proci->pde) {
|
||||
if (opt_dentry) {
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
rc = selinux_proc_get_sid(proci->pde,
|
||||
rc = selinux_proc_get_sid(opt_dentry,
|
||||
isec->sclass,
|
||||
&sid);
|
||||
if (rc)
|
||||
@@ -1578,7 +1573,7 @@ static int may_create(struct inode *dir,
|
||||
return rc;
|
||||
|
||||
if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
|
||||
rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
|
||||
rc = security_transition_sid(sid, dsec->sid, tclass, NULL, &newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
@@ -1862,82 +1857,6 @@ static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
|
||||
return task_has_capability(tsk, cred, cap, audit);
|
||||
}
|
||||
|
||||
static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
|
||||
{
|
||||
int buflen, rc;
|
||||
char *buffer, *path, *end;
|
||||
|
||||
rc = -ENOMEM;
|
||||
buffer = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!buffer)
|
||||
goto out;
|
||||
|
||||
buflen = PAGE_SIZE;
|
||||
end = buffer+buflen;
|
||||
*--end = '\0';
|
||||
buflen--;
|
||||
path = end-1;
|
||||
*path = '/';
|
||||
while (table) {
|
||||
const char *name = table->procname;
|
||||
size_t namelen = strlen(name);
|
||||
buflen -= namelen + 1;
|
||||
if (buflen < 0)
|
||||
goto out_free;
|
||||
end -= namelen;
|
||||
memcpy(end, name, namelen);
|
||||
*--end = '/';
|
||||
path = end;
|
||||
table = table->parent;
|
||||
}
|
||||
buflen -= 4;
|
||||
if (buflen < 0)
|
||||
goto out_free;
|
||||
end -= 4;
|
||||
memcpy(end, "/sys", 4);
|
||||
path = end;
|
||||
rc = security_genfs_sid("proc", path, tclass, sid);
|
||||
out_free:
|
||||
free_page((unsigned long)buffer);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_sysctl(ctl_table *table, int op)
|
||||
{
|
||||
int error = 0;
|
||||
u32 av;
|
||||
u32 tsid, sid;
|
||||
int rc;
|
||||
|
||||
sid = current_sid();
|
||||
|
||||
rc = selinux_sysctl_get_sid(table, (op == 0001) ?
|
||||
SECCLASS_DIR : SECCLASS_FILE, &tsid);
|
||||
if (rc) {
|
||||
/* Default to the well-defined sysctl SID. */
|
||||
tsid = SECINITSID_SYSCTL;
|
||||
}
|
||||
|
||||
/* The op values are "defined" in sysctl.c, thereby creating
|
||||
* a bad coupling between this module and sysctl.c */
|
||||
if (op == 001) {
|
||||
error = avc_has_perm(sid, tsid,
|
||||
SECCLASS_DIR, DIR__SEARCH, NULL);
|
||||
} else {
|
||||
av = 0;
|
||||
if (op & 004)
|
||||
av |= FILE__READ;
|
||||
if (op & 002)
|
||||
av |= FILE__WRITE;
|
||||
if (av)
|
||||
error = avc_has_perm(sid, tsid,
|
||||
SECCLASS_FILE, av, NULL);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
@@ -2060,7 +1979,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||
} else {
|
||||
/* Check for a default transition on this program. */
|
||||
rc = security_transition_sid(old_tsec->sid, isec->sid,
|
||||
SECCLASS_PROCESS, &new_tsec->sid);
|
||||
SECCLASS_PROCESS, NULL,
|
||||
&new_tsec->sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
@@ -2443,6 +2363,91 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_sb_remount(struct super_block *sb, void *data)
|
||||
{
|
||||
int rc, i, *flags;
|
||||
struct security_mnt_opts opts;
|
||||
char *secdata, **mount_options;
|
||||
struct superblock_security_struct *sbsec = sb->s_security;
|
||||
|
||||
if (!(sbsec->flags & SE_SBINITIALIZED))
|
||||
return 0;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
|
||||
return 0;
|
||||
|
||||
security_init_mnt_opts(&opts);
|
||||
secdata = alloc_secdata();
|
||||
if (!secdata)
|
||||
return -ENOMEM;
|
||||
rc = selinux_sb_copy_data(data, secdata);
|
||||
if (rc)
|
||||
goto out_free_secdata;
|
||||
|
||||
rc = selinux_parse_opts_str(secdata, &opts);
|
||||
if (rc)
|
||||
goto out_free_secdata;
|
||||
|
||||
mount_options = opts.mnt_opts;
|
||||
flags = opts.mnt_opts_flags;
|
||||
|
||||
for (i = 0; i < opts.num_mnt_opts; i++) {
|
||||
u32 sid;
|
||||
size_t len;
|
||||
|
||||
if (flags[i] == SE_SBLABELSUPP)
|
||||
continue;
|
||||
len = strlen(mount_options[i]);
|
||||
rc = security_context_to_sid(mount_options[i], len, &sid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "SELinux: security_context_to_sid"
|
||||
"(%s) failed for (dev %s, type %s) errno=%d\n",
|
||||
mount_options[i], sb->s_id, sb->s_type->name, rc);
|
||||
goto out_free_opts;
|
||||
}
|
||||
rc = -EINVAL;
|
||||
switch (flags[i]) {
|
||||
case FSCONTEXT_MNT:
|
||||
if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
case CONTEXT_MNT:
|
||||
if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
case ROOTCONTEXT_MNT: {
|
||||
struct inode_security_struct *root_isec;
|
||||
root_isec = sb->s_root->d_inode->i_security;
|
||||
|
||||
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
}
|
||||
case DEFCONTEXT_MNT:
|
||||
if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
|
||||
goto out_bad_option;
|
||||
break;
|
||||
default:
|
||||
goto out_free_opts;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
out_free_opts:
|
||||
security_free_mnt_opts(&opts);
|
||||
out_free_secdata:
|
||||
free_secdata(secdata);
|
||||
return rc;
|
||||
out_bad_option:
|
||||
printk(KERN_WARNING "SELinux: unable to change security options "
|
||||
"during remount (dev %s, type=%s)\n", sb->s_id,
|
||||
sb->s_type->name);
|
||||
goto out_free_opts;
|
||||
}
|
||||
|
||||
static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
@@ -2509,8 +2514,8 @@ static void selinux_inode_free_security(struct inode *inode)
|
||||
}
|
||||
|
||||
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
char **name, void **value,
|
||||
size_t *len)
|
||||
const struct qstr *qstr, char **name,
|
||||
void **value, size_t *len)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct inode_security_struct *dsec;
|
||||
@@ -2531,7 +2536,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
|
||||
rc = security_transition_sid(sid, dsec->sid,
|
||||
inode_mode_to_security_class(inode->i_mode),
|
||||
&newsid);
|
||||
qstr, &newsid);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "%s: "
|
||||
"security_transition_sid failed, rc=%d (dev=%s "
|
||||
@@ -2932,16 +2937,47 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
u32 av = 0;
|
||||
int error = 0;
|
||||
|
||||
if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||||
av |= FILE__WRITE;
|
||||
if (_IOC_DIR(cmd) & _IOC_READ)
|
||||
av |= FILE__READ;
|
||||
if (!av)
|
||||
av = FILE__IOCTL;
|
||||
switch (cmd) {
|
||||
case FIONREAD:
|
||||
/* fall through */
|
||||
case FIBMAP:
|
||||
/* fall through */
|
||||
case FIGETBSZ:
|
||||
/* fall through */
|
||||
case EXT2_IOC_GETFLAGS:
|
||||
/* fall through */
|
||||
case EXT2_IOC_GETVERSION:
|
||||
error = file_has_perm(cred, file, FILE__GETATTR);
|
||||
break;
|
||||
|
||||
return file_has_perm(cred, file, av);
|
||||
case EXT2_IOC_SETFLAGS:
|
||||
/* fall through */
|
||||
case EXT2_IOC_SETVERSION:
|
||||
error = file_has_perm(cred, file, FILE__SETATTR);
|
||||
break;
|
||||
|
||||
/* sys_ioctl() checks */
|
||||
case FIONBIO:
|
||||
/* fall through */
|
||||
case FIOASYNC:
|
||||
error = file_has_perm(cred, file, 0);
|
||||
break;
|
||||
|
||||
case KDSKBENT:
|
||||
case KDSKBSENT:
|
||||
error = task_has_capability(current, cred, CAP_SYS_TTY_CONFIG,
|
||||
SECURITY_CAP_AUDIT);
|
||||
break;
|
||||
|
||||
/* default case assumes that the command will go
|
||||
* to the file's ioctl() function.
|
||||
*/
|
||||
default:
|
||||
error = file_has_perm(cred, file, FILE__IOCTL);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int default_noexec;
|
||||
@@ -3644,9 +3680,16 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
|
||||
|
||||
/* socket security operations */
|
||||
|
||||
static u32 socket_sockcreate_sid(const struct task_security_struct *tsec)
|
||||
static int socket_sockcreate_sid(const struct task_security_struct *tsec,
|
||||
u16 secclass, u32 *socksid)
|
||||
{
|
||||
return tsec->sockcreate_sid ? : tsec->sid;
|
||||
if (tsec->sockcreate_sid > SECSID_NULL) {
|
||||
*socksid = tsec->sockcreate_sid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return security_transition_sid(tsec->sid, tsec->sid, secclass, NULL,
|
||||
socksid);
|
||||
}
|
||||
|
||||
static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
|
||||
@@ -3670,12 +3713,16 @@ static int selinux_socket_create(int family, int type,
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
u32 newsid;
|
||||
u16 secclass;
|
||||
int rc;
|
||||
|
||||
if (kern)
|
||||
return 0;
|
||||
|
||||
newsid = socket_sockcreate_sid(tsec);
|
||||
secclass = socket_type_to_security_class(family, type, protocol);
|
||||
rc = socket_sockcreate_sid(tsec, secclass, &newsid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL);
|
||||
}
|
||||
|
||||
@@ -3687,12 +3734,16 @@ static int selinux_socket_post_create(struct socket *sock, int family,
|
||||
struct sk_security_struct *sksec;
|
||||
int err = 0;
|
||||
|
||||
isec->sclass = socket_type_to_security_class(family, type, protocol);
|
||||
|
||||
if (kern)
|
||||
isec->sid = SECINITSID_KERNEL;
|
||||
else
|
||||
isec->sid = socket_sockcreate_sid(tsec);
|
||||
else {
|
||||
err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
isec->sclass = socket_type_to_security_class(family, type, protocol);
|
||||
isec->initialized = 1;
|
||||
|
||||
if (sock->sk) {
|
||||
@@ -4002,7 +4053,6 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
|
||||
{
|
||||
int err = 0;
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
u32 peer_sid;
|
||||
u32 sk_sid = sksec->sid;
|
||||
struct common_audit_data ad;
|
||||
char *addrp;
|
||||
@@ -4021,20 +4071,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
if (selinux_policycap_netpeer) {
|
||||
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
|
||||
if (err)
|
||||
return err;
|
||||
err = avc_has_perm(sk_sid, peer_sid,
|
||||
SECCLASS_PEER, PEER__RECV, &ad);
|
||||
if (err)
|
||||
selinux_netlbl_err(skb, err, 0);
|
||||
} else {
|
||||
err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
|
||||
if (err)
|
||||
return err;
|
||||
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
|
||||
}
|
||||
err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
|
||||
if (err)
|
||||
return err;
|
||||
err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -4529,9 +4569,8 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
|
||||
SECCLASS_PACKET, PACKET__SEND, &ad))
|
||||
return NF_DROP_ERR(-ECONNREFUSED);
|
||||
|
||||
if (selinux_policycap_netpeer)
|
||||
if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
|
||||
return NF_DROP_ERR(-ECONNREFUSED);
|
||||
if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
|
||||
return NF_DROP_ERR(-ECONNREFUSED);
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
@@ -4574,27 +4613,14 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
|
||||
* from the sending socket, otherwise use the kernel's sid */
|
||||
sk = skb->sk;
|
||||
if (sk == NULL) {
|
||||
switch (family) {
|
||||
case PF_INET:
|
||||
if (IPCB(skb)->flags & IPSKB_FORWARDED)
|
||||
secmark_perm = PACKET__FORWARD_OUT;
|
||||
else
|
||||
secmark_perm = PACKET__SEND;
|
||||
break;
|
||||
case PF_INET6:
|
||||
if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
|
||||
secmark_perm = PACKET__FORWARD_OUT;
|
||||
else
|
||||
secmark_perm = PACKET__SEND;
|
||||
break;
|
||||
default:
|
||||
return NF_DROP_ERR(-ECONNREFUSED);
|
||||
}
|
||||
if (secmark_perm == PACKET__FORWARD_OUT) {
|
||||
if (skb->skb_iif) {
|
||||
secmark_perm = PACKET__FORWARD_OUT;
|
||||
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
|
||||
return NF_DROP;
|
||||
} else
|
||||
} else {
|
||||
secmark_perm = PACKET__SEND;
|
||||
peer_sid = SECINITSID_KERNEL;
|
||||
}
|
||||
} else {
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
peer_sid = sksec->sid;
|
||||
@@ -4848,7 +4874,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
|
||||
* message queue this message will be stored in
|
||||
*/
|
||||
rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
|
||||
&msec->sid);
|
||||
NULL, &msec->sid);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
@@ -5402,7 +5428,6 @@ static struct security_operations selinux_ops = {
|
||||
.ptrace_traceme = selinux_ptrace_traceme,
|
||||
.capget = selinux_capget,
|
||||
.capset = selinux_capset,
|
||||
.sysctl = selinux_sysctl,
|
||||
.capable = selinux_capable,
|
||||
.quotactl = selinux_quotactl,
|
||||
.quota_on = selinux_quota_on,
|
||||
@@ -5420,6 +5445,7 @@ static struct security_operations selinux_ops = {
|
||||
.sb_alloc_security = selinux_sb_alloc_security,
|
||||
.sb_free_security = selinux_sb_free_security,
|
||||
.sb_copy_data = selinux_sb_copy_data,
|
||||
.sb_remount = selinux_sb_remount,
|
||||
.sb_kern_mount = selinux_sb_kern_mount,
|
||||
.sb_show_options = selinux_sb_show_options,
|
||||
.sb_statfs = selinux_sb_statfs,
|
||||
|
@@ -12,6 +12,10 @@
|
||||
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
|
||||
"write", "associate", "unix_read", "unix_write"
|
||||
|
||||
/*
|
||||
* Note: The name for any socket class should be suffixed by "socket",
|
||||
* and doesn't contain more than one substr of "socket".
|
||||
*/
|
||||
struct security_class_mapping secclass_map[] = {
|
||||
{ "security",
|
||||
{ "compute_av", "compute_create", "compute_member",
|
||||
@@ -132,8 +136,7 @@ struct security_class_mapping secclass_map[] = {
|
||||
{ "appletalk_socket",
|
||||
{ COMMON_SOCK_PERMS, NULL } },
|
||||
{ "packet",
|
||||
{ "send", "recv", "relabelto", "flow_in", "flow_out",
|
||||
"forward_in", "forward_out", NULL } },
|
||||
{ "send", "recv", "relabelto", "forward_in", "forward_out", NULL } },
|
||||
{ "key",
|
||||
{ "view", "read", "write", "search", "link", "setattr", "create",
|
||||
NULL } },
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#ifndef _SELINUX_SECURITY_H_
|
||||
#define _SELINUX_SECURITY_H_
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/types.h>
|
||||
#include "flask.h"
|
||||
@@ -28,13 +29,14 @@
|
||||
#define POLICYDB_VERSION_POLCAP 22
|
||||
#define POLICYDB_VERSION_PERMISSIVE 23
|
||||
#define POLICYDB_VERSION_BOUNDARY 24
|
||||
#define POLICYDB_VERSION_FILENAME_TRANS 25
|
||||
|
||||
/* Range of policy versions we understand*/
|
||||
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
|
||||
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
|
||||
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
|
||||
#else
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY
|
||||
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_FILENAME_TRANS
|
||||
#endif
|
||||
|
||||
/* Mask for just the mount related flags */
|
||||
@@ -106,8 +108,8 @@ void security_compute_av(u32 ssid, u32 tsid,
|
||||
void security_compute_av_user(u32 ssid, u32 tsid,
|
||||
u16 tclass, struct av_decision *avd);
|
||||
|
||||
int security_transition_sid(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 *out_sid);
|
||||
int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
|
||||
const struct qstr *qstr, u32 *out_sid);
|
||||
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 *out_sid);
|
||||
|
@@ -14,7 +14,7 @@
|
||||
*
|
||||
* Copyright (C) 2003 Tresys Technology, LLC
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2.
|
||||
*
|
||||
* Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
|
||||
@@ -27,16 +27,16 @@ struct avtab_key {
|
||||
u16 source_type; /* source type */
|
||||
u16 target_type; /* target type */
|
||||
u16 target_class; /* target object class */
|
||||
#define AVTAB_ALLOWED 1
|
||||
#define AVTAB_AUDITALLOW 2
|
||||
#define AVTAB_AUDITDENY 4
|
||||
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
|
||||
#define AVTAB_TRANSITION 16
|
||||
#define AVTAB_MEMBER 32
|
||||
#define AVTAB_CHANGE 64
|
||||
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
|
||||
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
|
||||
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
|
||||
#define AVTAB_ALLOWED 0x0001
|
||||
#define AVTAB_AUDITALLOW 0x0002
|
||||
#define AVTAB_AUDITDENY 0x0004
|
||||
#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
|
||||
#define AVTAB_TRANSITION 0x0010
|
||||
#define AVTAB_MEMBER 0x0020
|
||||
#define AVTAB_CHANGE 0x0040
|
||||
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
|
||||
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
|
||||
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
|
||||
u16 specified; /* what field is specified */
|
||||
};
|
||||
|
||||
|
@@ -512,7 +512,8 @@ int mls_compute_sid(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
u32 specified,
|
||||
struct context *newcontext)
|
||||
struct context *newcontext,
|
||||
bool sock)
|
||||
{
|
||||
struct range_trans rtr;
|
||||
struct mls_range *r;
|
||||
@@ -531,7 +532,7 @@ int mls_compute_sid(struct context *scontext,
|
||||
return mls_range_set(newcontext, r);
|
||||
/* Fallthrough */
|
||||
case AVTAB_CHANGE:
|
||||
if (tclass == policydb.process_class)
|
||||
if ((tclass == policydb.process_class) || (sock == true))
|
||||
/* Use the process MLS attributes. */
|
||||
return mls_context_cpy(newcontext, scontext);
|
||||
else
|
||||
|
@@ -49,7 +49,8 @@ int mls_compute_sid(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
u32 specified,
|
||||
struct context *newcontext);
|
||||
struct context *newcontext,
|
||||
bool sock);
|
||||
|
||||
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
|
||||
struct context *usercon);
|
||||
|
@@ -123,6 +123,11 @@ static struct policydb_compat_info policydb_compat[] = {
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
{
|
||||
.version = POLICYDB_VERSION_FILENAME_TRANS,
|
||||
.sym_num = SYM_NUM,
|
||||
.ocon_num = OCON_NUM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct policydb_compat_info *policydb_lookup_compat(int version)
|
||||
@@ -704,6 +709,7 @@ void policydb_destroy(struct policydb *p)
|
||||
int i;
|
||||
struct role_allow *ra, *lra = NULL;
|
||||
struct role_trans *tr, *ltr = NULL;
|
||||
struct filename_trans *ft, *nft;
|
||||
|
||||
for (i = 0; i < SYM_NUM; i++) {
|
||||
cond_resched();
|
||||
@@ -781,6 +787,15 @@ void policydb_destroy(struct policydb *p)
|
||||
}
|
||||
flex_array_free(p->type_attr_map_array);
|
||||
}
|
||||
|
||||
ft = p->filename_trans;
|
||||
while (ft) {
|
||||
nft = ft->next;
|
||||
kfree(ft->name);
|
||||
kfree(ft);
|
||||
ft = nft;
|
||||
}
|
||||
|
||||
ebitmap_destroy(&p->policycaps);
|
||||
ebitmap_destroy(&p->permissive_map);
|
||||
|
||||
@@ -1788,6 +1803,76 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int filename_trans_read(struct policydb *p, void *fp)
|
||||
{
|
||||
struct filename_trans *ft, *last;
|
||||
u32 nel, len;
|
||||
char *name;
|
||||
__le32 buf[4];
|
||||
int rc, i;
|
||||
|
||||
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
|
||||
return 0;
|
||||
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto out;
|
||||
nel = le32_to_cpu(buf[0]);
|
||||
|
||||
printk(KERN_ERR "%s: nel=%d\n", __func__, nel);
|
||||
|
||||
last = p->filename_trans;
|
||||
while (last && last->next)
|
||||
last = last->next;
|
||||
|
||||
for (i = 0; i < nel; i++) {
|
||||
rc = -ENOMEM;
|
||||
ft = kzalloc(sizeof(*ft), GFP_KERNEL);
|
||||
if (!ft)
|
||||
goto out;
|
||||
|
||||
/* add it to the tail of the list */
|
||||
if (!last)
|
||||
p->filename_trans = ft;
|
||||
else
|
||||
last->next = ft;
|
||||
last = ft;
|
||||
|
||||
/* length of the path component string */
|
||||
rc = next_entry(buf, fp, sizeof(u32));
|
||||
if (rc)
|
||||
goto out;
|
||||
len = le32_to_cpu(buf[0]);
|
||||
|
||||
rc = -ENOMEM;
|
||||
name = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (!name)
|
||||
goto out;
|
||||
|
||||
ft->name = name;
|
||||
|
||||
/* path component string */
|
||||
rc = next_entry(name, fp, len);
|
||||
if (rc)
|
||||
goto out;
|
||||
name[len] = 0;
|
||||
|
||||
printk(KERN_ERR "%s: ft=%p ft->name=%p ft->name=%s\n", __func__, ft, ft->name, ft->name);
|
||||
|
||||
rc = next_entry(buf, fp, sizeof(u32) * 4);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
ft->stype = le32_to_cpu(buf[0]);
|
||||
ft->ttype = le32_to_cpu(buf[1]);
|
||||
ft->tclass = le32_to_cpu(buf[2]);
|
||||
ft->otype = le32_to_cpu(buf[3]);
|
||||
}
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int genfs_read(struct policydb *p, void *fp)
|
||||
{
|
||||
int i, j, rc;
|
||||
@@ -2251,6 +2336,10 @@ int policydb_read(struct policydb *p, void *fp)
|
||||
lra = ra;
|
||||
}
|
||||
|
||||
rc = filename_trans_read(p, fp);
|
||||
if (rc)
|
||||
goto bad;
|
||||
|
||||
rc = policydb_index(p);
|
||||
if (rc)
|
||||
goto bad;
|
||||
@@ -3025,6 +3114,43 @@ static int range_write(struct policydb *p, void *fp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filename_trans_write(struct policydb *p, void *fp)
|
||||
{
|
||||
struct filename_trans *ft;
|
||||
u32 len, nel = 0;
|
||||
__le32 buf[4];
|
||||
int rc;
|
||||
|
||||
for (ft = p->filename_trans; ft; ft = ft->next)
|
||||
nel++;
|
||||
|
||||
buf[0] = cpu_to_le32(nel);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (ft = p->filename_trans; ft; ft = ft->next) {
|
||||
len = strlen(ft->name);
|
||||
buf[0] = cpu_to_le32(len);
|
||||
rc = put_entry(buf, sizeof(u32), 1, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = put_entry(ft->name, sizeof(char), len, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
buf[0] = ft->stype;
|
||||
buf[1] = ft->ttype;
|
||||
buf[2] = ft->tclass;
|
||||
buf[3] = ft->otype;
|
||||
|
||||
rc = put_entry(buf, sizeof(u32), 4, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Write the configuration data in a policy database
|
||||
* structure to a policy database binary representation
|
||||
@@ -3135,6 +3261,10 @@ int policydb_write(struct policydb *p, void *fp)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = filename_trans_write(p, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = ocontext_write(p, info, fp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@@ -77,6 +77,15 @@ struct role_trans {
|
||||
struct role_trans *next;
|
||||
};
|
||||
|
||||
struct filename_trans {
|
||||
struct filename_trans *next;
|
||||
u32 stype; /* current process */
|
||||
u32 ttype; /* parent dir context */
|
||||
u16 tclass; /* class of new object */
|
||||
const char *name; /* last path component */
|
||||
u32 otype; /* expected of new object */
|
||||
};
|
||||
|
||||
struct role_allow {
|
||||
u32 role; /* current role */
|
||||
u32 new_role; /* new role */
|
||||
@@ -217,6 +226,9 @@ struct policydb {
|
||||
/* role transitions */
|
||||
struct role_trans *role_tr;
|
||||
|
||||
/* file transitions with the last path component */
|
||||
struct filename_trans *filename_trans;
|
||||
|
||||
/* bools indexed by (value - 1) */
|
||||
struct cond_bool_datum **bool_val_to_struct;
|
||||
/* type enforcement conditional access vectors and transitions */
|
||||
@@ -302,7 +314,7 @@ static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int put_entry(void *buf, size_t bytes, int num, struct policy_file *fp)
|
||||
static inline int put_entry(const void *buf, size_t bytes, int num, struct policy_file *fp)
|
||||
{
|
||||
size_t len = bytes * num;
|
||||
|
||||
|
@@ -201,6 +201,21 @@ static u16 unmap_class(u16 tclass)
|
||||
return tclass;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get kernel value for class from its policy value
|
||||
*/
|
||||
static u16 map_class(u16 pol_value)
|
||||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 1; i < current_mapping_size; i++) {
|
||||
if (current_mapping[i].value == pol_value)
|
||||
return i;
|
||||
}
|
||||
|
||||
return pol_value;
|
||||
}
|
||||
|
||||
static void map_decision(u16 tclass, struct av_decision *avd,
|
||||
int allow_unknown)
|
||||
{
|
||||
@@ -1343,10 +1358,27 @@ out:
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
static void filename_compute_type(struct policydb *p, struct context *newcontext,
|
||||
u32 scon, u32 tcon, u16 tclass,
|
||||
const struct qstr *qstr)
|
||||
{
|
||||
struct filename_trans *ft;
|
||||
for (ft = p->filename_trans; ft; ft = ft->next) {
|
||||
if (ft->stype == scon &&
|
||||
ft->ttype == tcon &&
|
||||
ft->tclass == tclass &&
|
||||
!strcmp(ft->name, qstr->name)) {
|
||||
newcontext->type = ft->otype;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int security_compute_sid(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 orig_tclass,
|
||||
u32 specified,
|
||||
const struct qstr *qstr,
|
||||
u32 *out_sid,
|
||||
bool kern)
|
||||
{
|
||||
@@ -1357,6 +1389,7 @@ static int security_compute_sid(u32 ssid,
|
||||
struct avtab_node *node;
|
||||
u16 tclass;
|
||||
int rc = 0;
|
||||
bool sock;
|
||||
|
||||
if (!ss_initialized) {
|
||||
switch (orig_tclass) {
|
||||
@@ -1374,10 +1407,13 @@ static int security_compute_sid(u32 ssid,
|
||||
|
||||
read_lock(&policy_rwlock);
|
||||
|
||||
if (kern)
|
||||
if (kern) {
|
||||
tclass = unmap_class(orig_tclass);
|
||||
else
|
||||
sock = security_is_socket_class(orig_tclass);
|
||||
} else {
|
||||
tclass = orig_tclass;
|
||||
sock = security_is_socket_class(map_class(tclass));
|
||||
}
|
||||
|
||||
scontext = sidtab_search(&sidtab, ssid);
|
||||
if (!scontext) {
|
||||
@@ -1408,7 +1444,7 @@ static int security_compute_sid(u32 ssid,
|
||||
}
|
||||
|
||||
/* Set the role and type to default values. */
|
||||
if (tclass == policydb.process_class) {
|
||||
if ((tclass == policydb.process_class) || (sock == true)) {
|
||||
/* Use the current role and type of process. */
|
||||
newcontext.role = scontext->role;
|
||||
newcontext.type = scontext->type;
|
||||
@@ -1442,6 +1478,11 @@ static int security_compute_sid(u32 ssid,
|
||||
newcontext.type = avdatum->data;
|
||||
}
|
||||
|
||||
/* if we have a qstr this is a file trans check so check those rules */
|
||||
if (qstr)
|
||||
filename_compute_type(&policydb, &newcontext, scontext->type,
|
||||
tcontext->type, tclass, qstr);
|
||||
|
||||
/* Check for class-specific changes. */
|
||||
if (tclass == policydb.process_class) {
|
||||
if (specified & AVTAB_TRANSITION) {
|
||||
@@ -1460,7 +1501,8 @@ static int security_compute_sid(u32 ssid,
|
||||
|
||||
/* Set the MLS attributes.
|
||||
This is done last because it may allocate memory. */
|
||||
rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext);
|
||||
rc = mls_compute_sid(scontext, tcontext, tclass, specified,
|
||||
&newcontext, sock);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -1495,22 +1537,17 @@ out:
|
||||
* if insufficient memory is available, or %0 if the new SID was
|
||||
* computed successfully.
|
||||
*/
|
||||
int security_transition_sid(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 tclass,
|
||||
u32 *out_sid)
|
||||
int security_transition_sid(u32 ssid, u32 tsid, u16 tclass,
|
||||
const struct qstr *qstr, u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
|
||||
out_sid, true);
|
||||
qstr, out_sid, true);
|
||||
}
|
||||
|
||||
int security_transition_sid_user(u32 ssid,
|
||||
u32 tsid,
|
||||
u16 tclass,
|
||||
u32 *out_sid)
|
||||
int security_transition_sid_user(u32 ssid, u32 tsid, u16 tclass, u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION,
|
||||
out_sid, false);
|
||||
NULL, out_sid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1531,8 +1568,8 @@ int security_member_sid(u32 ssid,
|
||||
u16 tclass,
|
||||
u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid,
|
||||
false);
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, NULL,
|
||||
out_sid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1553,8 +1590,8 @@ int security_change_sid(u32 ssid,
|
||||
u16 tclass,
|
||||
u32 *out_sid)
|
||||
{
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid,
|
||||
false);
|
||||
return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, NULL,
|
||||
out_sid, false);
|
||||
}
|
||||
|
||||
/* Clone the SID into the new SID table. */
|
||||
|
@@ -208,7 +208,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
|
||||
if (!uctx)
|
||||
goto not_from_user;
|
||||
|
||||
if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
|
||||
if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX)
|
||||
return -EINVAL;
|
||||
|
||||
str_len = uctx->ctx_len;
|
||||
|
Reference in New Issue
Block a user