X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=security%2Fselinux%2Fhooks.c;h=d9d22ce4a4f1c558377818024fe24454b9dd0fd4;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=52fa3cfdfd5b86e4328140c2d82698b3971c0dab;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 52fa3cfdf..d9d22ce4a 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,8 +63,8 @@ #include #include #include -#include #include +#include #include "avc.h" #include "objsec.h" @@ -72,7 +73,7 @@ #define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX -extern int policydb_loaded_version; +extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); #ifdef CONFIG_SECURITY_SELINUX_DEVELOP @@ -87,7 +88,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) { @@ -109,7 +110,7 @@ static struct security_operations *secondary_ops = NULL; /* Lists of inode and superblock security structures initialized before the policy was loaded. */ static LIST_HEAD(superblock_security_head); -static spinlock_t sb_security_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(sb_security_lock); /* Allocate and free functions for each kind of security blob. */ @@ -386,13 +387,6 @@ static int try_context_mount(struct super_block *sb, void *data) break; case Opt_fscontext: - if (sbsec->behavior != SECURITY_FS_USE_XATTR) { - rc = -EINVAL; - printk(KERN_WARNING "SELinux: " - "fscontext option is invalid for" - " this filesystem type\n"); - goto out_free; - } if (seen & (Opt_context|Opt_fscontext)) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); @@ -454,12 +448,12 @@ static int try_context_mount(struct super_block *sb, void *data) } rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL, NULL); + FILESYSTEM__RELABELFROM, NULL); if (rc) goto out_free; rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELTO, NULL, NULL); + FILESYSTEM__RELABELTO, NULL); if (rc) goto out_free; @@ -482,12 +476,12 @@ static int try_context_mount(struct super_block *sb, void *data) goto out_free; rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL, NULL); + FILESYSTEM__RELABELFROM, NULL); if (rc) goto out_free; rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, NULL, NULL); + FILESYSTEM__ASSOCIATE, NULL); if (rc) goto out_free; @@ -637,10 +631,12 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc case PF_UNIX: switch (type) { case SOCK_STREAM: + case SOCK_SEQPACKET: return SECCLASS_UNIX_STREAM_SOCKET; case SOCK_DGRAM: return SECCLASS_UNIX_DGRAM_SOCKET; } + break; case PF_INET: case PF_INET6: switch (type) { @@ -651,6 +647,7 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc case SOCK_RAW: return SECCLASS_RAWIP_SOCKET; } + break; case PF_NETLINK: switch (protocol) { case NETLINK_ROUTE: @@ -930,7 +927,7 @@ int task_has_perm(struct task_struct *tsk1, tsec1 = tsk1->security; tsec2 = tsk2->security; return avc_has_perm(tsec1->sid, tsec2->sid, - SECCLASS_PROCESS, perms, &tsec2->avcr, NULL); + SECCLASS_PROCESS, perms, NULL); } /* Check whether a task is allowed to use a capability. */ @@ -947,7 +944,7 @@ int task_has_capability(struct task_struct *tsk, ad.u.cap = cap; return avc_has_perm(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, CAP_TO_MASK(cap), NULL, &ad); + SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad); } /* Check whether a task is allowed to use a system operation. */ @@ -959,18 +956,15 @@ int task_has_system(struct task_struct *tsk, tsec = tsk->security; return avc_has_perm(tsec->sid, SECINITSID_KERNEL, - SECCLASS_SYSTEM, perms, NULL, NULL); + SECCLASS_SYSTEM, perms, NULL); } /* Check whether a task has a particular permission to an inode. - The 'aeref' parameter is optional and allows other AVC - entry references to be passed (e.g. the one in the struct file). The 'adp' parameter is optional and allows other audit data to be passed (e.g. the dentry). */ int inode_has_perm(struct task_struct *tsk, struct inode *inode, u32 perms, - struct avc_entry_ref *aeref, struct avc_audit_data *adp) { struct task_security_struct *tsec; @@ -986,8 +980,7 @@ int inode_has_perm(struct task_struct *tsk, ad.u.fs.inode = inode; } - return avc_has_perm(tsec->sid, isec->sid, isec->sclass, - perms, aeref ? aeref : &isec->avcr, adp); + return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp); } /* Same as inode_has_perm, but pass explicit audit data containing @@ -1003,7 +996,7 @@ static inline int dentry_has_perm(struct task_struct *tsk, AVC_AUDIT_DATA_INIT(&ad,FS); ad.u.fs.mnt = mnt; ad.u.fs.dentry = dentry; - return inode_has_perm(tsk, inode, av, NULL, &ad); + return inode_has_perm(tsk, inode, av, &ad); } /* Check whether a task can use an open file descriptor to @@ -1034,14 +1027,14 @@ static inline int file_has_perm(struct task_struct *tsk, rc = avc_has_perm(tsec->sid, fsec->sid, SECCLASS_FD, FD__USE, - &fsec->avcr, &ad); + &ad); if (rc) return rc; } /* av is zero if only checking access to the descriptor. */ if (av) - return inode_has_perm(tsk, inode, av, &fsec->inode_avcr, &ad); + return inode_has_perm(tsk, inode, av, &ad); return 0; } @@ -1067,7 +1060,7 @@ static int may_create(struct inode *dir, rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, - &dsec->avcr, &ad); + &ad); if (rc) return rc; @@ -1080,13 +1073,13 @@ static int may_create(struct inode *dir, return rc; } - rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, NULL, &ad); + rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad); if (rc) return rc; return avc_has_perm(newsid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, NULL, &ad); + FILESYSTEM__ASSOCIATE, &ad); } #define MAY_LINK 0 @@ -1114,8 +1107,7 @@ static int may_link(struct inode *dir, av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); - rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, - av, &dsec->avcr, &ad); + rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; @@ -1134,8 +1126,7 @@ static int may_link(struct inode *dir, return 0; } - rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, - av, &isec->avcr, &ad); + rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad); return rc; } @@ -1161,21 +1152,16 @@ static inline int may_rename(struct inode *old_dir, ad.u.fs.dentry = old_dentry; rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR, - DIR__REMOVE_NAME | DIR__SEARCH, - &old_dsec->avcr, &ad); + DIR__REMOVE_NAME | DIR__SEARCH, &ad); if (rc) return rc; rc = avc_has_perm(tsec->sid, old_isec->sid, - old_isec->sclass, - FILE__RENAME, - &old_isec->avcr, &ad); + old_isec->sclass, FILE__RENAME, &ad); if (rc) return rc; if (old_is_dir && new_dir != old_dir) { rc = avc_has_perm(tsec->sid, old_isec->sid, - old_isec->sclass, - DIR__REPARENT, - &old_isec->avcr, &ad); + old_isec->sclass, DIR__REPARENT, &ad); if (rc) return rc; } @@ -1184,8 +1170,7 @@ static inline int may_rename(struct inode *old_dir, av = DIR__ADD_NAME | DIR__SEARCH; if (new_dentry->d_inode) av |= DIR__REMOVE_NAME; - rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, - av,&new_dsec->avcr, &ad); + rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad); if (rc) return rc; if (new_dentry->d_inode) { @@ -1193,8 +1178,7 @@ static inline int may_rename(struct inode *old_dir, new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode); rc = avc_has_perm(tsec->sid, new_isec->sid, new_isec->sclass, - (new_is_dir ? DIR__RMDIR : FILE__UNLINK), - &new_isec->avcr, &ad); + (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); if (rc) return rc; } @@ -1214,7 +1198,7 @@ int superblock_has_perm(struct task_struct *tsk, tsec = tsk->security; sbsec = sb->s_security; return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - perms, NULL, ad); + perms, ad); } /* Convert a Linux mode and permission mask to an access vector. */ @@ -1266,6 +1250,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); @@ -1400,12 +1390,6 @@ static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effect static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) { - int error; - - error = task_has_perm(current, target, PROCESS__SETCAP); - if (error) - return; - secondary_ops->capset_set(target, effective, inheritable, permitted); } @@ -1445,7 +1429,7 @@ static int selinux_sysctl(ctl_table *table, int op) * a bad coupling between this module and sysctl.c */ if(op == 001) { error = avc_has_perm(tsec->sid, tsid, - SECCLASS_DIR, DIR__SEARCH, NULL, NULL); + SECCLASS_DIR, DIR__SEARCH, NULL); } else { av = 0; if (op & 004) @@ -1454,7 +1438,7 @@ static int selinux_sysctl(ctl_table *table, int op) av |= FILE__WRITE; if (av) error = avc_has_perm(tsec->sid, tsid, - SECCLASS_FILE, av, NULL, NULL); + SECCLASS_FILE, av, NULL); } return error; @@ -1491,9 +1475,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) return rc; } -static int selinux_quota_on(struct file *f) +static int selinux_quota_on(struct dentry *dentry) { - return file_has_perm(current, f, FILE__QUOTAON); + return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); } static int selinux_syslog(int type) @@ -1531,70 +1515,29 @@ static int selinux_syslog(int type) * mapping. 0 means there is enough memory for the allocation to * succeed and -ENOMEM implies there is not. * - * We currently support three overcommit policies, which are set via the - * vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting + * Note that secondary_ops->capable and task_has_perm_noaudit return 0 + * if the capability is granted, but __vm_enough_memory requires 1 if + * the capability is granted. * - * Strict overcommit modes added 2002 Feb 26 by Alan Cox. - * Additional code 2002 Jul 20 by Robert Love. + * Do not audit the selinux permission check, as this is applied to all + * processes that allocate mappings. */ static int selinux_vm_enough_memory(long pages) { - unsigned long free, allowed; - int rc; + int rc, cap_sys_admin = 0; struct task_security_struct *tsec = current->security; - vm_acct_memory(pages); - - /* - * Sometimes we want to use more memory than we have - */ - if (sysctl_overcommit_memory == 1) - return 0; - - if (sysctl_overcommit_memory == 0) { - free = get_page_cache_size(); - free += nr_free_pages(); - free += nr_swap_pages; - - /* - * Any slabs which are created with the - * SLAB_RECLAIM_ACCOUNT flag claim to have contents - * which are reclaimable, under pressure. The dentry - * cache and most inode caches should fall into this - */ - free += atomic_read(&slab_reclaim_pages); - - /* - * Leave the last 3% for privileged processes. - * Don't audit the check, as it is applied to all processes - * that allocate mappings. - */ - rc = secondary_ops->capable(current, CAP_SYS_ADMIN); - if (!rc) { - rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, - CAP_TO_MASK(CAP_SYS_ADMIN), - NULL, NULL); - } - if (rc) - free -= free / 32; + rc = secondary_ops->capable(current, CAP_SYS_ADMIN); + if (rc == 0) + rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, + SECCLASS_CAPABILITY, + CAP_TO_MASK(CAP_SYS_ADMIN), + NULL); - if (free > pages) - return 0; - vm_unacct_memory(pages); - return -ENOMEM; - } - - allowed = (totalram_pages - hugetlb_total_pages()) - * sysctl_overcommit_ratio / 100; - allowed += total_swap_pages; - - if (atomic_read(&vm_committed_space) < allowed) - return 0; - - vm_unacct_memory(pages); + if (rc == 0) + cap_sys_admin = 1; - return -ENOMEM; + return __vm_enough_memory(pages, cap_sys_admin); } /* binprm security operations */ @@ -1666,22 +1609,18 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm) if (tsec->sid == newsid) { rc = avc_has_perm(tsec->sid, isec->sid, - SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, - &isec->avcr, &ad); + SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); if (rc) return rc; } else { /* Check permissions for the transition. */ rc = avc_has_perm(tsec->sid, newsid, - SECCLASS_PROCESS, PROCESS__TRANSITION, - NULL, - &ad); + SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); if (rc) return rc; rc = avc_has_perm(newsid, isec->sid, - SECCLASS_FILE, FILE__ENTRYPOINT, - &isec->avcr, &ad); + SECCLASS_FILE, FILE__ENTRYPOINT, &ad); if (rc) return rc; @@ -1713,7 +1652,7 @@ static int selinux_bprm_secureexec (struct linux_binprm *bprm) the two SIDs, i.e. ahp returns 0. */ atsecure = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, - PROCESS__NOATSECURE, NULL, NULL); + PROCESS__NOATSECURE, NULL); } return (atsecure || secondary_ops->bprm_secureexec(bprm)); @@ -1726,72 +1665,39 @@ 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)) { + /* 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 +1732,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); @@ -1849,10 +1755,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) struct task_security_struct *tsec; struct bprm_security_struct *bsec; u32 sid; - struct av_decision avd; - struct itimerval itimer; - struct rlimit *rlim, *initrlim; - int rc, i; + int rc; secondary_ops->bprm_apply_creds(bprm, unsafe); @@ -1862,93 +1765,101 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) sid = bsec->sid; tsec->osid = tsec->sid; + bsec->unsafe = 0; if (tsec->sid != sid) { /* Check for shared state. If not ok, leave SID unchanged and kill. */ if (unsafe & LSM_UNSAFE_SHARE) { - rc = avc_has_perm_noaudit(tsec->sid, sid, - SECCLASS_PROCESS, PROCESS__SHARE, - NULL, &avd); + rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, + PROCESS__SHARE, NULL); if (rc) { - task_unlock(current); - avc_audit(tsec->sid, sid, SECCLASS_PROCESS, - PROCESS__SHARE, &avd, rc, NULL); - force_sig_specific(SIGKILL, current); - goto lock_out; + bsec->unsafe = 1; + return; } } /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and kill. */ if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { - rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid, + rc = avc_has_perm(tsec->ptrace_sid, sid, SECCLASS_PROCESS, PROCESS__PTRACE, - NULL, &avd); - if (!rc) - tsec->sid = sid; - task_unlock(current); - avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS, - PROCESS__PTRACE, &avd, rc, NULL); + NULL); if (rc) { - force_sig_specific(SIGKILL, current); - goto lock_out; + bsec->unsafe = 1; + return; } - } else { - tsec->sid = sid; - task_unlock(current); - } - - /* Close files for which the new task SID is not authorized. */ - flush_unauthorized_files(current->files); - - /* Check whether the new SID can inherit signal state - from the old SID. If not, clear itimers to avoid - subsequent signal generation and flush and unblock - signals. This must occur _after_ the task SID has - been updated so that any kill done after the flush - will be checked against the new SID. */ - rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, - PROCESS__SIGINH, NULL, NULL); - if (rc) { - memset(&itimer, 0, sizeof itimer); - for (i = 0; i < 3; i++) - do_setitimer(i, &itimer, NULL); - flush_signals(current); - spin_lock_irq(¤t->sighand->siglock); - flush_signal_handlers(current, 1); - sigemptyset(¤t->blocked); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); } + tsec->sid = sid; + } +} - /* Check whether the new SID can inherit resource limits - from the old SID. If not, reset all soft limits to - the lower of the current task's hard limit and the init - task's soft limit. Note that the setting of hard limits - (even to lower them) can be controlled by the setrlimit - check. The inclusion of the init task's soft limit into - the computation is to avoid resetting soft limits higher - than the default soft limit for cases where the default - is lower than the hard limit, e.g. RLIMIT_CORE or - RLIMIT_STACK.*/ - rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, - PROCESS__RLIMITINH, NULL, NULL); - if (rc) { - for (i = 0; i < RLIM_NLIMITS; i++) { - rlim = current->rlim + i; - initrlim = init_task.rlim+i; - rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur); - } - } +/* + * called after apply_creds without the task lock held + */ +static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm) +{ + struct task_security_struct *tsec; + struct rlimit *rlim, *initrlim; + struct itimerval itimer; + struct bprm_security_struct *bsec; + int rc, i; - /* Wake up the parent if it is waiting so that it can - recheck wait permission to the new task SID. */ - wake_up_interruptible(¤t->parent->wait_chldexit); + tsec = current->security; + bsec = bprm->security; -lock_out: - task_lock(current); + if (bsec->unsafe) { + force_sig_specific(SIGKILL, current); return; } + if (tsec->osid == tsec->sid) + return; + + /* Close files for which the new task SID is not authorized. */ + flush_unauthorized_files(current->files); + + /* Check whether the new SID can inherit signal state + from the old SID. If not, clear itimers to avoid + subsequent signal generation and flush and unblock + signals. This must occur _after_ the task SID has + been updated so that any kill done after the flush + will be checked against the new SID. */ + rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, + PROCESS__SIGINH, NULL); + if (rc) { + memset(&itimer, 0, sizeof itimer); + for (i = 0; i < 3; i++) + do_setitimer(i, &itimer, NULL); + flush_signals(current); + spin_lock_irq(¤t->sighand->siglock); + flush_signal_handlers(current, 1); + sigemptyset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + /* Check whether the new SID can inherit resource limits + from the old SID. If not, reset all soft limits to + the lower of the current task's hard limit and the init + task's soft limit. Note that the setting of hard limits + (even to lower them) can be controlled by the setrlimit + check. The inclusion of the init task's soft limit into + the computation is to avoid resetting soft limits higher + than the default soft limit for cases where the default + is lower than the hard limit, e.g. RLIMIT_CORE or + RLIMIT_STACK.*/ + rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, + PROCESS__RLIMITINH, NULL); + if (rc) { + for (i = 0; i < RLIM_NLIMITS; i++) { + rlim = current->signal->rlim + i; + initrlim = init_task.signal->rlim+i; + rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur); + } + } + + /* Wake up the parent if it is waiting so that it can + recheck wait permission to the new task SID. */ + wake_up_interruptible(¤t->parent->signal->wait_chldexit); } /* superblock security operations */ @@ -2218,7 +2129,7 @@ static int selinux_inode_permission(struct inode *inode, int mask, } return inode_has_perm(current, inode, - file_mask_to_av(inode->i_mode, mask), NULL, NULL); + file_mask_to_av(inode->i_mode, mask), NULL); } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) @@ -2229,6 +2140,9 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) if (rc) return rc; + if (iattr->ia_valid & ATTR_FORCE) + return 0; + if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME_SET | ATTR_MTIME_SET)) return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); @@ -2269,12 +2183,14 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) return -EOPNOTSUPP; + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + AVC_AUDIT_DATA_INIT(&ad,FS); ad.u.fs.dentry = dentry; rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, - FILE__RELABELFROM, - &isec->avcr, &ad); + FILE__RELABELFROM, &ad); if (rc) return rc; @@ -2283,7 +2199,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value return rc; rc = avc_has_perm(tsec->sid, newsid, isec->sclass, - FILE__RELABELTO, NULL, &ad); + FILE__RELABELTO, &ad); if (rc) return rc; @@ -2291,7 +2207,6 @@ static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value sbsec->sid, SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, - NULL, &ad); } @@ -2357,9 +2272,8 @@ static int selinux_inode_removexattr (struct dentry *dentry, char *name) return -EACCES; } -static int selinux_inode_getsecurity(struct dentry *dentry, const char *name, void *buffer, size_t size) +static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) { - struct inode *inode = dentry->d_inode; struct inode_security_struct *isec = inode->i_security; char *context; unsigned len; @@ -2387,10 +2301,9 @@ static int selinux_inode_getsecurity(struct dentry *dentry, const char *name, vo return len; } -static int selinux_inode_setsecurity(struct dentry *dentry, const char *name, +static int selinux_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - struct inode *inode = dentry->d_inode; struct inode_security_struct *isec = inode->i_security; u32 newsid; int rc; @@ -2409,10 +2322,10 @@ static int selinux_inode_setsecurity(struct dentry *dentry, const char *name, return 0; } -static int selinux_inode_listsecurity(struct dentry *dentry, char *buffer) +static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) { const int len = sizeof(XATTR_NAME_SELINUX); - if (buffer) + if (buffer && len <= buffer_size) memcpy(buffer, XATTR_NAME_SELINUX, len); return len; } @@ -2492,21 +2405,25 @@ 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 ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) { + /* + * We are making executable an anonymous mapping or a + * private file mapping that will also be writable. + * This has an additional check. + */ + int rc = task_has_perm(current, current, PROCESS__EXECMEM); + 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 +2434,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 +2455,19 @@ 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); + if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) { + /* + * We are making executable a file mapping that has + * had some COW done. Since pages might have been written, + * check ability to execute the possibly modified content. + * This typically should only occur for text relocations. + */ + int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD); + if (rc) + return rc; + } + + 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) @@ -2591,8 +2532,7 @@ static int selinux_file_set_fowner(struct file *file) } static int selinux_file_send_sigiotask(struct task_struct *tsk, - struct fown_struct *fown, - int fd, int reason) + struct fown_struct *fown, int signum) { struct file *file; u32 perm; @@ -2605,13 +2545,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk, tsec = tsk->security; fsec = file->f_security; - if (!fown->signum) + if (!signum) perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */ else - perm = signal_to_av(fown->signum); + perm = signal_to_av(signum); return avc_has_perm(fsec->fown_sid, tsec->sid, - SECCLASS_PROCESS, perm, NULL, NULL); + SECCLASS_PROCESS, perm, NULL); } static int selinux_file_receive(struct file *file) @@ -2651,6 +2591,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; } @@ -2715,7 +2660,7 @@ static int selinux_task_setnice(struct task_struct *p, int nice) static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) { - struct rlimit *old_rlim = current->rlim + resource; + struct rlimit *old_rlim = current->signal->rlim + resource; int rc; rc = secondary_ops->task_setrlimit(resource, new_rlim); @@ -2734,17 +2679,7 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp) { - struct task_security_struct *tsec1, *tsec2; - - tsec1 = current->security; - tsec2 = p->security; - - /* No auditing from the setscheduler hook, since the runqueue lock - is held and the system will deadlock if we try to log an audit - message. */ - return avc_has_perm_noaudit(tsec1->sid, tsec2->sid, - SECCLASS_PROCESS, PROCESS__SETSCHED, - &tsec2->avcr, NULL); + return task_has_perm(current, p, PROCESS__SETSCHED); } static int selinux_task_getscheduler(struct task_struct *p) @@ -2822,49 +2757,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 +2819,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 +2840,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; } @@ -2981,8 +2922,7 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock, AVC_AUDIT_DATA_INIT(&ad,NET); ad.u.net.sk = sock->sk; - err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, - perms, &isec->avcr, &ad); + err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); out: return err; @@ -3000,7 +2940,7 @@ static int selinux_socket_create(int family, int type, tsec = current->security; err = avc_has_perm(tsec->sid, tsec->sid, socket_type_to_security_class(family, type, - protocol), SOCKET__CREATE, NULL, NULL); + protocol), SOCKET__CREATE, NULL); out: return err; @@ -3078,9 +3018,10 @@ 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); + SOCKET__NAME_BIND, &ad); if (err) goto out; } @@ -3113,7 +3054,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); err = avc_has_perm(isec->sid, sid, - isec->sclass, node_perm, NULL, &ad); + isec->sclass, node_perm, &ad); if (err) goto out; } @@ -3213,8 +3154,7 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, err = avc_has_perm(isec->sid, other_isec->sid, isec->sclass, - UNIX_STREAM_SOCKET__CONNECTTO, - &other_isec->avcr, &ad); + UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) return err; @@ -3244,9 +3184,7 @@ static int selinux_socket_unix_may_send(struct socket *sock, ad.u.net.sk = other->sk; err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, - SOCKET__SENDTO, - &other_isec->avcr, &ad); + isec->sclass, SOCKET__SENDTO, &ad); if (err) return err; @@ -3258,13 +3196,11 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) u16 family; char *addrp; int len, err = 0; - u32 netif_perm, node_perm, node_sid, recv_perm = 0; + u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; u32 sock_sid = 0; u16 sock_class = 0; struct socket *sock; struct net_device *dev; - struct sel_netif *netif; - struct netif_security_struct *nsec; struct avc_audit_data ad; family = sk->sk_family; @@ -3295,13 +3231,9 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!dev) goto out; - netif = sel_netif_lookup(dev); - if (IS_ERR(netif)) { - err = PTR_ERR(netif); + err = sel_netif_sids(dev, &if_sid, NULL); + if (err) goto out; - } - - nsec = &netif->nsec; switch (sock_class) { case SECCLASS_UDP_SOCKET: @@ -3327,14 +3259,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ad.u.net.family = family; err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); - if (err) { - sel_netif_put(netif); + if (err) goto out; - } - err = avc_has_perm(sock_sid, nsec->if_sid, SECCLASS_NETIF, - netif_perm, &nsec->avcr, &ad); - sel_netif_put(netif); + err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); if (err) goto out; @@ -3343,7 +3271,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, NULL, &ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); if (err) goto out; @@ -3357,8 +3285,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) goto out; - err = avc_has_perm(sock_sid, port_sid, sock_class, - recv_perm, NULL, &ad); + err = avc_has_perm(sock_sid, port_sid, + sock_class, recv_perm, &ad); } out: return err; @@ -3450,13 +3378,11 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, { char *addrp; int len, err = NF_ACCEPT; - u32 netif_perm, node_perm, node_sid, send_perm = 0; + u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; struct sock *sk; struct socket *sock; struct inode *inode; - struct sel_netif *netif; struct sk_buff *skb = *pskb; - struct netif_security_struct *nsec; struct inode_security_struct *isec; struct avc_audit_data ad; struct net_device *dev = (struct net_device *)out; @@ -3473,13 +3399,10 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, if (!inode) goto out; - netif = sel_netif_lookup(dev); - if (IS_ERR(netif)) { - err = NF_DROP; + err = sel_netif_sids(dev, &if_sid, NULL); + if (err) goto out; - } - - nsec = &netif->nsec; + isec = inode->i_security; switch (isec->sclass) { @@ -3508,14 +3431,11 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, err = selinux_parse_skb(skb, &ad, &addrp, &len, 0) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) { - sel_netif_put(netif); + if (err != NF_ACCEPT) goto out; - } - err = avc_has_perm(isec->sid, nsec->if_sid, SECCLASS_NETIF, - netif_perm, &nsec->avcr, &ad) ? NF_DROP : NF_ACCEPT; - sel_netif_put(netif); + err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, + netif_perm, &ad) ? NF_DROP : NF_ACCEPT; if (err != NF_ACCEPT) goto out; @@ -3526,7 +3446,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, goto out; err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, - node_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT; + node_perm, &ad) ? NF_DROP : NF_ACCEPT; if (err != NF_ACCEPT) goto out; @@ -3543,7 +3463,7 @@ static unsigned int selinux_ip_postroute_last(unsigned int hooknum, goto out; err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, NULL, &ad) ? NF_DROP : NF_ACCEPT; + send_perm, &ad) ? NF_DROP : NF_ACCEPT; } out: @@ -3585,12 +3505,20 @@ static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) { - int err = 0; + struct task_security_struct *tsec; + struct av_decision avd; + int err; - if (capable(CAP_NET_ADMIN)) - cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); - else - NETLINK_CB(skb).eff_cap = 0; + err = secondary_ops->netlink_send(sk, skb); + if (err) + return err; + + tsec = current->security; + + avd.allowed = 0; + avc_has_perm_noaudit(tsec->sid, tsec->sid, + SECCLASS_CAPABILITY, ~0, &avd); + cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed); if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) err = selinux_nlmsg_perm(sk, skb); @@ -3680,8 +3608,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, AVC_AUDIT_DATA_INIT(&ad, IPC); ad.u.ipc_id = ipc_perms->key; - return avc_has_perm(tsec->sid, isec->sid, sclass, - perms, &isec->avcr, &ad); + return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad); } static int selinux_msg_msg_alloc_security(struct msg_msg *msg) @@ -3713,7 +3640,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, - MSGQ__CREATE, &isec->avcr, &ad); + MSGQ__CREATE, &ad); if (rc) { ipc_free_security(&msq->q_perm); return rc; @@ -3739,7 +3666,7 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) ad.u.ipc_id = msq->q_perm.key; return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, - MSGQ__ASSOCIATE, &isec->avcr, &ad); + MSGQ__ASSOCIATE, &ad); } static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) @@ -3803,17 +3730,15 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, /* Can this process write to the queue? */ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, - MSGQ__WRITE, &isec->avcr, &ad); + MSGQ__WRITE, &ad); if (!rc) /* Can this process send the message */ rc = avc_has_perm(tsec->sid, msec->sid, - SECCLASS_MSG, MSG__SEND, - &msec->avcr, &ad); + SECCLASS_MSG, MSG__SEND, &ad); if (!rc) /* Can the message be put in the queue? */ rc = avc_has_perm(msec->sid, isec->sid, - SECCLASS_MSGQ, MSGQ__ENQUEUE, - &isec->avcr, &ad); + SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad); return rc; } @@ -3836,12 +3761,10 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(tsec->sid, isec->sid, - SECCLASS_MSGQ, MSGQ__READ, - &isec->avcr, &ad); + SECCLASS_MSGQ, MSGQ__READ, &ad); if (!rc) rc = avc_has_perm(tsec->sid, msec->sid, - SECCLASS_MSG, MSG__RECEIVE, - &msec->avcr, &ad); + SECCLASS_MSG, MSG__RECEIVE, &ad); return rc; } @@ -3864,7 +3787,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) ad.u.ipc_id = shp->shm_perm.key; rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, - SHM__CREATE, &isec->avcr, &ad); + SHM__CREATE, &ad); if (rc) { ipc_free_security(&shp->shm_perm); return rc; @@ -3890,7 +3813,7 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) ad.u.ipc_id = shp->shm_perm.key; return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, - SHM__ASSOCIATE, &isec->avcr, &ad); + SHM__ASSOCIATE, &ad); } /* Note, at this point, shp is locked down */ @@ -3963,7 +3886,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) ad.u.ipc_id = sma->sem_perm.key; rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, - SEM__CREATE, &isec->avcr, &ad); + SEM__CREATE, &ad); if (rc) { ipc_free_security(&sma->sem_perm); return rc; @@ -3989,7 +3912,7 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) ad.u.ipc_id = sma->sem_perm.key; return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, - SEM__ASSOCIATE, &isec->avcr, &ad); + SEM__ASSOCIATE, &ad); } /* Note, at this point, sma is locked down */ @@ -4157,10 +4080,9 @@ static int selinux_setprocattr(struct task_struct *p, u32 sid = 0; int error; - if (current != p || !strcmp(name, "current")) { + if (current != p) { /* SELinux only allows a process to change its own - security attributes, and it only allows the process - current SID to change via exec. */ + security attributes. */ return -EACCES; } @@ -4173,6 +4095,8 @@ static int selinux_setprocattr(struct task_struct *p, error = task_has_perm(current, p, PROCESS__SETEXEC); else if (!strcmp(name, "fscreate")) error = task_has_perm(current, p, PROCESS__SETFSCREATE); + else if (!strcmp(name, "current")) + error = task_has_perm(current, p, PROCESS__SETCURRENT); else error = -EINVAL; if (error) @@ -4197,6 +4121,51 @@ static int selinux_setprocattr(struct task_struct *p, tsec->exec_sid = sid; else if (!strcmp(name, "fscreate")) tsec->create_sid = sid; + else if (!strcmp(name, "current")) { + struct av_decision avd; + + if (sid == 0) + return -EINVAL; + + /* Only allow single threaded processes to change context */ + if (atomic_read(&p->mm->mm_users) != 1) { + struct task_struct *g, *t; + struct mm_struct *mm = p->mm; + read_lock(&tasklist_lock); + do_each_thread(g, t) + if (t->mm == mm && t != p) { + read_unlock(&tasklist_lock); + return -EPERM; + } + while_each_thread(g, t); + read_unlock(&tasklist_lock); + } + + /* Check permissions for the transition. */ + error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, + PROCESS__DYNTRANSITION, NULL); + if (error) + return error; + + /* Check for ptracing, and update the task SID if ok. + Otherwise, leave SID unchanged and fail. */ + task_lock(p); + if (p->ptrace & PT_PTRACED) { + error = avc_has_perm_noaudit(tsec->ptrace_sid, sid, + SECCLASS_PROCESS, + PROCESS__PTRACE, &avd); + if (!error) + tsec->sid = sid; + task_unlock(p); + avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS, + PROCESS__PTRACE, &avd, error, NULL); + if (error) + return error; + } else { + tsec->sid = sid; + task_unlock(p); + } + } else return -EINVAL; @@ -4221,6 +4190,7 @@ struct security_operations selinux_ops = { .bprm_alloc_security = selinux_bprm_alloc_security, .bprm_free_security = selinux_bprm_free_security, .bprm_apply_creds = selinux_bprm_apply_creds, + .bprm_post_apply_creds = selinux_bprm_post_apply_creds, .bprm_set_security = selinux_bprm_set_security, .bprm_check_security = selinux_bprm_check_security, .bprm_secureexec = selinux_bprm_secureexec,