X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=security%2Fselinux%2Fhooks.c;h=f844e402b53c2f76f64a90fb7831cb7bb96b03a3;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=52fa3cfdfd5b86e4328140c2d82698b3971c0dab;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 52fa3cfdf..f844e402b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include /* for sysctl_local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ @@ -62,7 +63,6 @@ #include #include #include -#include #include #include "avc.h" @@ -87,7 +87,7 @@ __setup("enforcing=", enforcing_setup); #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) { @@ -1266,6 +1266,12 @@ static inline u32 file_to_av(struct file *file) 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); @@ -1548,10 +1554,10 @@ static int selinux_vm_enough_memory(long pages) /* * 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; @@ -1726,72 +1732,40 @@ static void selinux_bprm_free_security(struct linux_binprm *bprm) 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); @@ -1826,7 +1800,7 @@ static inline void flush_unauthorized_files(struct files_struct * files) 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); @@ -2492,21 +2466,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd, 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) @@ -2517,6 +2484,18 @@ static int selinux_file_mmap(struct file *file, unsigned long prot, unsigned lon 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) { @@ -2526,7 +2505,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, 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) @@ -2651,6 +2630,11 @@ static int selinux_task_alloc_security(struct task_struct *tsk) 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; } @@ -2822,49 +2806,52 @@ static void selinux_task_to_inode(struct task_struct *p, /* 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; } @@ -2881,19 +2868,20 @@ out: 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) @@ -2901,24 +2889,26 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad 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; } @@ -3078,6 +3068,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in 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);