diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 157 | 
1 files changed, 109 insertions, 48 deletions
| diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 912deee3f01e..a86d537eb79b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,  	might_sleep_if(may_sleep); -	if (isec->initialized == LABEL_INVALID) { +	if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {  		if (!may_sleep)  			return -ECHILD; @@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)  	return inode->i_security;  } +static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) +{ +	struct inode *inode = d_backing_inode(dentry); + +	return inode->i_security; +} +  /*   * Get the security label of a dentry's backing inode.   */ @@ -506,7 +513,8 @@ static int sb_finish_set_opts(struct super_block *sb)  			rc = -EOPNOTSUPP;  			goto out;  		} -		rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); +		rc = root_inode->i_op->getxattr(root, root_inode, +						XATTR_NAME_SELINUX, NULL, 0);  		if (rc < 0 && rc != -ENODATA) {  			if (rc == -EOPNOTSUPP)  				printk(KERN_WARNING "SELinux: (dev %s, type " @@ -686,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	struct superblock_security_struct *sbsec = sb->s_security;  	const char *name = sb->s_type->name;  	struct dentry *root = sbsec->sb->s_root; -	struct inode_security_struct *root_isec = backing_inode_security(root); +	struct inode_security_struct *root_isec;  	u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;  	u32 defcontext_sid = 0;  	char **mount_options = opts->mnt_opts; @@ -729,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	    && (num_opts == 0))  		goto out; +	root_isec = backing_inode_security_novalidate(root); +  	/*  	 * parse the mount options, check if they are valid sids.  	 * also check if someone is trying to mount the same sb more @@ -1316,7 +1326,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry,  				 u32 *sid)  {  	int rc; -	struct super_block *sb = dentry->d_inode->i_sb; +	struct super_block *sb = dentry->d_sb;  	char *buffer, *path;  	buffer = (char *)__get_free_page(GFP_KERNEL); @@ -1412,13 +1422,13 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent  			goto out_unlock;  		}  		context[len] = '\0'; -		rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, +		rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,  					   context, len);  		if (rc == -ERANGE) {  			kfree(context);  			/* Need a larger buffer.  Query for the right size. */ -			rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, +			rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,  						   NULL, 0);  			if (rc < 0) {  				dput(dentry); @@ -1432,7 +1442,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent  				goto out_unlock;  			}  			context[len] = '\0'; -			rc = inode->i_op->getxattr(dentry, +			rc = inode->i_op->getxattr(dentry, inode,  						   XATTR_NAME_SELINUX,  						   context, len);  		} @@ -1622,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,  /* Check whether a task is allowed to use a capability. */  static int cred_has_capability(const struct cred *cred, -			       int cap, int audit) +			       int cap, int audit, bool initns)  {  	struct common_audit_data ad;  	struct av_decision avd; @@ -1636,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,  	switch (CAP_TO_INDEX(cap)) {  	case 0: -		sclass = SECCLASS_CAPABILITY; +		sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;  		break;  	case 1: -		sclass = SECCLASS_CAPABILITY2; +		sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;  		break;  	default:  		printk(KERN_ERR @@ -1781,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,  					 u32 *_new_isid)  {  	const struct superblock_security_struct *sbsec = dir->i_sb->s_security; -	const struct inode_security_struct *dsec = inode_security(dir);  	const struct task_security_struct *tsec = current_security();  	if ((sbsec->flags & SE_SBINITIALIZED) && @@ -1791,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,  		   tsec->create_sid) {  		*_new_isid = tsec->create_sid;  	} else { +		const struct inode_security_struct *dsec = inode_security(dir);  		return security_transition_sid(tsec->sid, dsec->sid, tclass,  					       name, _new_isid);  	} @@ -2075,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,  	u32 sid = task_sid(to);  	struct file_security_struct *fsec = file->f_security;  	struct dentry *dentry = file->f_path.dentry; -	struct inode_security_struct *isec = backing_inode_security(dentry); +	struct inode_security_struct *isec;  	struct common_audit_data ad;  	int rc; @@ -2094,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,  	if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))  		return 0; +	isec = backing_inode_security(dentry);  	return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),  			    &ad);  } @@ -2142,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,  static int selinux_capable(const struct cred *cred, struct user_namespace *ns,  			   int cap, int audit)  { -	return cred_has_capability(cred, cap, audit); +	return cred_has_capability(cred, cap, audit, ns == &init_user_ns);  }  static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) @@ -2220,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)  	int rc, cap_sys_admin = 0;  	rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, -					SECURITY_CAP_NOAUDIT); +				 SECURITY_CAP_NOAUDIT, true);  	if (rc == 0)  		cap_sys_admin = 1; @@ -2229,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)  /* binprm security operations */ +static u32 ptrace_parent_sid(struct task_struct *task) +{ +	u32 sid = 0; +	struct task_struct *tracer; + +	rcu_read_lock(); +	tracer = ptrace_parent(task); +	if (tracer) +		sid = task_sid(tracer); +	rcu_read_unlock(); + +	return sid; +} +  static int check_nnp_nosuid(const struct linux_binprm *bprm,  			    const struct task_security_struct *old_tsec,  			    const struct task_security_struct *new_tsec) @@ -2350,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)  		 * changes its SID has the appropriate permit */  		if (bprm->unsafe &  		    (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { -			struct task_struct *tracer; -			struct task_security_struct *sec; -			u32 ptsid = 0; - -			rcu_read_lock(); -			tracer = ptrace_parent(current); -			if (likely(tracer != NULL)) { -				sec = __task_cred(tracer)->security; -				ptsid = sec->sid; -			} -			rcu_read_unlock(); - +			u32 ptsid = ptrace_parent_sid(current);  			if (ptsid != 0) {  				rc = avc_has_perm(ptsid, new_tsec->sid,  						  SECCLASS_PROCESS, @@ -2760,7 +2774,7 @@ static int selinux_sb_statfs(struct dentry *dentry)  }  static int selinux_mount(const char *dev_name, -			 struct path *path, +			 const struct path *path,  			 const char *type,  			 unsigned long flags,  			 void *data) @@ -3045,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  				  const void *value, size_t size, int flags)  {  	struct inode *inode = d_backing_inode(dentry); -	struct inode_security_struct *isec = backing_inode_security(dentry); +	struct inode_security_struct *isec;  	struct superblock_security_struct *sbsec;  	struct common_audit_data ad;  	u32 newsid, sid = current_sid(); @@ -3064,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  	ad.type = LSM_AUDIT_DATA_DENTRY;  	ad.u.dentry = dentry; +	isec = backing_inode_security(dentry);  	rc = avc_has_perm(sid, isec->sid, isec->sclass,  			  FILE__RELABELFROM, &ad);  	if (rc) @@ -3122,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,  					int flags)  {  	struct inode *inode = d_backing_inode(dentry); -	struct inode_security_struct *isec = backing_inode_security(dentry); +	struct inode_security_struct *isec;  	u32 newsid;  	int rc; @@ -3139,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,  		return;  	} +	isec = backing_inode_security(dentry);  	isec->sclass = inode_mode_to_security_class(inode->i_mode);  	isec->sid = newsid;  	isec->initialized = LABEL_INITIALIZED; @@ -3180,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void  	u32 size;  	int error;  	char *context = NULL; -	struct inode_security_struct *isec = inode_security(inode); +	struct inode_security_struct *isec;  	if (strcmp(name, XATTR_SELINUX_SUFFIX))  		return -EOPNOTSUPP; @@ -3198,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void  			    SECURITY_CAP_NOAUDIT);  	if (!error)  		error = cred_has_capability(current_cred(), CAP_MAC_ADMIN, -					    SECURITY_CAP_NOAUDIT); +					    SECURITY_CAP_NOAUDIT, true); +	isec = inode_security(inode);  	if (!error)  		error = security_sid_to_context_force(isec->sid, &context,  						      &size); @@ -3219,7 +3236,7 @@ out_nofree:  static int selinux_inode_setsecurity(struct inode *inode, const char *name,  				     const void *value, size_t size, int flags)  { -	struct inode_security_struct *isec = inode_security(inode); +	struct inode_security_struct *isec = inode_security_novalidate(inode);  	u32 newsid;  	int rc; @@ -3308,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,  	struct common_audit_data ad;  	struct file_security_struct *fsec = file->f_security;  	struct inode *inode = file_inode(file); -	struct inode_security_struct *isec = inode_security(inode); +	struct inode_security_struct *isec;  	struct lsm_ioctlop_audit ioctl;  	u32 ssid = cred_sid(cred);  	int rc; @@ -3332,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,  	if (unlikely(IS_PRIVATE(inode)))  		return 0; +	isec = inode_security(inode);  	rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,  			requested, driver, xperm, &ad);  out: @@ -3373,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,  	case KDSKBENT:  	case KDSKBSENT:  		error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, -					    SECURITY_CAP_AUDIT); +					    SECURITY_CAP_AUDIT, true);  		break;  	/* default case assumes that the command will go @@ -3462,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,  		    vma->vm_end <= vma->vm_mm->brk) {  			rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);  		} else if (!vma->vm_file && -			   vma->vm_start <= vma->vm_mm->start_stack && -			   vma->vm_end >= vma->vm_mm->start_stack) { +			   ((vma->vm_start <= vma->vm_mm->start_stack && +			     vma->vm_end >= vma->vm_mm->start_stack) || +			    vma_is_stack_for_task(vma, current))) {  			rc = current_has_perm(current, PROCESS__EXECSTACK);  		} else if (vma->vm_file && vma->anon_vma) {  			/* @@ -3719,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name)  			    SYSTEM__MODULE_REQUEST, &ad);  } +static int selinux_kernel_module_from_file(struct file *file) +{ +	struct common_audit_data ad; +	struct inode_security_struct *isec; +	struct file_security_struct *fsec; +	u32 sid = current_sid(); +	int rc; + +	/* init_module */ +	if (file == NULL) +		return avc_has_perm(sid, sid, SECCLASS_SYSTEM, +					SYSTEM__MODULE_LOAD, NULL); + +	/* finit_module */ + +	ad.type = LSM_AUDIT_DATA_PATH; +	ad.u.path = file->f_path; + +	fsec = file->f_security; +	if (sid != fsec->sid) { +		rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); +		if (rc) +			return rc; +	} + +	isec = inode_security(file_inode(file)); +	return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, +				SYSTEM__MODULE_LOAD, &ad); +} + +static int selinux_kernel_read_file(struct file *file, +				    enum kernel_read_file_id id) +{ +	int rc = 0; + +	switch (id) { +	case READING_MODULE: +		rc = selinux_kernel_module_from_file(file); +		break; +	default: +		break; +	} + +	return rc; +} +  static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)  {  	return current_has_perm(p, PROCESS__SETPGID); @@ -4598,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *  {  	u32 peer_secid = SECSID_NULL;  	u16 family; +	struct inode_security_struct *isec;  	if (skb && skb->protocol == htons(ETH_P_IP))  		family = PF_INET; @@ -4608,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *  	else  		goto out; -	if (sock && family == PF_UNIX) -		selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid); -	else if (skb) +	if (sock && family == PF_UNIX) { +		isec = inode_security_novalidate(SOCK_INODE(sock)); +		peer_secid = isec->sid; +	} else if (skb)  		selinux_skb_peerlbl_sid(skb, family, &peer_secid);  out: @@ -5675,7 +5742,6 @@ static int selinux_setprocattr(struct task_struct *p,  			       char *name, void *value, size_t size)  {  	struct task_security_struct *tsec; -	struct task_struct *tracer;  	struct cred *new;  	u32 sid = 0, ptsid;  	int error; @@ -5782,14 +5848,8 @@ static int selinux_setprocattr(struct task_struct *p,  		/* Check for ptracing, and update the task SID if ok.  		   Otherwise, leave SID unchanged and fail. */ -		ptsid = 0; -		rcu_read_lock(); -		tracer = ptrace_parent(p); -		if (tracer) -			ptsid = task_sid(tracer); -		rcu_read_unlock(); - -		if (tracer) { +		ptsid = ptrace_parent_sid(p); +		if (ptsid != 0) {  			error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,  					     PROCESS__PTRACE, NULL);  			if (error) @@ -6020,6 +6080,7 @@ static struct security_hook_list selinux_hooks[] = {  	LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),  	LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),  	LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), +	LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),  	LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),  	LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),  	LSM_HOOK_INIT(task_getsid, selinux_task_getsid), | 
