#include <linux/kd.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
#include <net/icmp.h>
#include <net/ip.h> /* for sysctl_local_port_range[] */
#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
#include <linux/nfs_mount.h>
#include <net/ipv6.h>
#include <linux/hugetlb.h>
-#include <linux/major.h>
#include <linux/personality.h>
#include "avc.h"
#endif
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
-int selinux_enabled = 1;
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
static int __init selinux_enabled_setup(char *str)
{
int inode_security_set_sid(struct inode *inode, u32 sid)
{
struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ if (!sbsec->initialized) {
+ /* Defer initialization to selinux_complete_init. */
+ return 0;
+ }
down(&isec->sem);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
/*
* Sometimes we want to use more memory than we have
*/
- if (sysctl_overcommit_memory == 1)
+ if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
return 0;
- if (sysctl_overcommit_memory == 0) {
+ if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
free = get_page_cache_size();
free += nr_free_pages();
free += nr_swap_pages;
kfree(bsec);
}
-/* Create an open file that refers to the null device.
- Derived from the OpenWall LSM. */
-struct file *open_devnull(void)
-{
- struct inode *inode;
- struct dentry *dentry;
- struct file *file = NULL;
- struct inode_security_struct *isec;
- dev_t dev;
-
- inode = new_inode(current->fs->rootmnt->mnt_sb);
- if (!inode)
- goto out;
-
- dentry = dget(d_alloc_root(inode));
- if (!dentry)
- goto out_iput;
-
- file = get_empty_filp();
- if (!file)
- goto out_dput;
-
- dev = MKDEV(MEM_MAJOR, 3); /* null device */
-
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
- inode->i_blksize = PAGE_SIZE;
- inode->i_blocks = 0;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->i_state = I_DIRTY; /* so that mark_inode_dirty won't touch us */
-
- isec = inode->i_security;
- isec->sid = SECINITSID_DEVNULL;
- isec->sclass = SECCLASS_CHR_FILE;
- isec->initialized = 1;
-
- file->f_flags = O_RDWR;
- file->f_mode = FMODE_READ | FMODE_WRITE;
- file->f_dentry = dentry;
- file->f_vfsmnt = mntget(current->fs->rootmnt);
- file->f_pos = 0;
-
- init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, dev);
- if (inode->i_fop->open(inode, file))
- goto out_fput;
-
-out:
- return file;
-out_fput:
- mntput(file->f_vfsmnt);
- put_filp(file);
-out_dput:
- dput(dentry);
-out_iput:
- iput(inode);
- file = NULL;
- goto out;
-}
+extern struct vfsmount *selinuxfs_mount;
+extern struct dentry *selinux_null;
/* Derived from fs/exec.c:flush_old_files. */
static inline void flush_unauthorized_files(struct files_struct * files)
{
struct avc_audit_data ad;
struct file *file, *devnull = NULL;
+ struct tty_struct *tty = current->signal->tty;
long j = -1;
+ if (tty) {
+ file_list_lock();
+ file = list_entry(tty->tty_files.next, typeof(*file), f_list);
+ if (file) {
+ /* Revalidate access to controlling tty.
+ Use inode_has_perm on the tty inode directly rather
+ than using file_has_perm, as this particular open
+ file may belong to another process and we are only
+ interested in the inode-based check here. */
+ struct inode *inode = file->f_dentry->d_inode;
+ if (inode_has_perm(current, inode,
+ FILE__READ | FILE__WRITE,
+ NULL, NULL)) {
+ /* Reset controlling tty. */
+ current->signal->tty = NULL;
+ current->signal->tty_old_pgrp = 0;
+ }
+ }
+ file_list_unlock();
+ }
+
+ /* Revalidate access to inherited open files. */
+
AVC_AUDIT_DATA_INIT(&ad,FS);
spin_lock(&files->file_lock);
if (devnull) {
atomic_inc(&devnull->f_count);
} else {
- devnull = open_devnull();
+ devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
if (!devnull) {
put_unused_fd(fd);
fput(file);
return error;
}
-static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
- u32 av;
- int rc;
-
- rc = secondary_ops->file_mmap(file, prot, flags);
- if (rc)
- return rc;
-
if (file) {
/* read access is always possible with a mapping */
- av = FILE__READ;
+ u32 av = FILE__READ;
/* write access only matters if the mapping is shared */
- if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE))
+ if (shared && (prot & PROT_WRITE))
av |= FILE__WRITE;
if (prot & PROT_EXEC)
return 0;
}
+static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned long flags)
+{
+ int rc;
+
+ rc = secondary_ops->file_mmap(file, prot, flags);
+ if (rc)
+ return rc;
+
+ return file_map_prot_check(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
if (rc)
return rc;
- return selinux_file_mmap(vma->vm_file, prot, vma->vm_flags);
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
}
static int selinux_file_lock(struct file *file, unsigned int cmd)
tsec2->exec_sid = tsec1->exec_sid;
tsec2->create_sid = tsec1->create_sid;
+ /* Retain ptracer SID across fork, if any.
+ This will be reset by the ptrace hook upon any
+ subsequent ptrace_attach operations. */
+ tsec2->ptrace_sid = tsec1->ptrace_sid;
+
return 0;
}
/* Returns error only if unable to parse addresses */
static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad)
{
- int offset, ihlen, ret;
- struct iphdr iph;
+ int offset, ihlen, ret = -EINVAL;
+ struct iphdr _iph, *ih;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &iph, sizeof(iph));
- if (ret)
+ ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+ if (ih == NULL)
goto out;
- ihlen = iph.ihl * 4;
- if (ihlen < sizeof(iph))
+ ihlen = ih->ihl * 4;
+ if (ihlen < sizeof(_iph))
goto out;
- ad->u.net.v4info.saddr = iph.saddr;
- ad->u.net.v4info.daddr = iph.daddr;
+ ad->u.net.v4info.saddr = ih->saddr;
+ ad->u.net.v4info.daddr = ih->daddr;
+ ret = 0;
- switch (iph.protocol) {
+ switch (ih->protocol) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (ntohs(iph.frag_off) & IP_OFFSET)
+ if (ntohs(ih->frag_off) & IP_OFFSET)
break;
offset += ihlen;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
- break;
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad)
{
u8 nexthdr;
- int ret, offset;
- struct ipv6hdr ipv6h;
+ int ret = -EINVAL, offset;
+ struct ipv6hdr _ipv6h, *ip6;
offset = skb->nh.raw - skb->data;
- ret = skb_copy_bits(skb, offset, &ipv6h, sizeof(ipv6h));
- if (ret)
+ ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+ if (ip6 == NULL)
goto out;
- ipv6_addr_copy(&ad->u.net.v6info.saddr, &ipv6h.saddr);
- ipv6_addr_copy(&ad->u.net.v6info.daddr, &ipv6h.daddr);
+ ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+ ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+ ret = 0;
- nexthdr = ipv6h.nexthdr;
- offset += sizeof(ipv6h);
+ nexthdr = ip6->nexthdr;
+ offset += sizeof(_ipv6h);
offset = ipv6_skip_exthdr(skb, offset, &nexthdr,
skb->tail - skb->head - offset);
if (offset < 0)
switch (nexthdr) {
case IPPROTO_TCP: {
- struct tcphdr tcph;
+ struct tcphdr _tcph, *th;
- if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0)
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
break;
- ad->u.net.sport = tcph.source;
- ad->u.net.dport = tcph.dest;
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
break;
}
case IPPROTO_UDP: {
- struct udphdr udph;
+ struct udphdr _udph, *uh;
- if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0)
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
break;
- ad->u.net.sport = udph.source;
- ad->u.net.dport = udph.dest;
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
break;
}
goto out;
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
err = avc_has_perm(isec->sid, sid,
isec->sclass,
SOCKET__NAME_BIND, NULL, &ad);