diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 1258 | 
1 files changed, 672 insertions, 586 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f85597a4d733..dbeaa783b2a9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -156,33 +156,62 @@ static int selinux_secmark_enabled(void)  	return (atomic_read(&selinux_secmark_refcount) > 0);  } -/* Allocate and free functions for each kind of security blob. */ - -static int task_alloc_security(struct task_struct *task) +/* + * initialise the security for the init task + */ +static void cred_init_security(void)  { +	struct cred *cred = (struct cred *) current->real_cred;  	struct task_security_struct *tsec;  	tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);  	if (!tsec) -		return -ENOMEM; +		panic("SELinux:  Failed to initialize initial task.\n"); -	tsec->osid = tsec->sid = SECINITSID_UNLABELED; -	task->security = tsec; +	tsec->osid = tsec->sid = SECINITSID_KERNEL; +	cred->security = tsec; +} -	return 0; +/* + * get the security ID of a set of credentials + */ +static inline u32 cred_sid(const struct cred *cred) +{ +	const struct task_security_struct *tsec; + +	tsec = cred->security; +	return tsec->sid;  } -static void task_free_security(struct task_struct *task) +/* + * get the objective security ID of a task + */ +static inline u32 task_sid(const struct task_struct *task)  { -	struct task_security_struct *tsec = task->security; -	task->security = NULL; -	kfree(tsec); +	u32 sid; + +	rcu_read_lock(); +	sid = cred_sid(__task_cred(task)); +	rcu_read_unlock(); +	return sid; +} + +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ +	const struct task_security_struct *tsec = current_cred()->security; + +	return tsec->sid;  } +/* Allocate and free functions for each kind of security blob. */ +  static int inode_alloc_security(struct inode *inode)  { -	struct task_security_struct *tsec = current->security;  	struct inode_security_struct *isec; +	u32 sid = current_sid();  	isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);  	if (!isec) @@ -193,7 +222,7 @@ static int inode_alloc_security(struct inode *inode)  	isec->inode = inode;  	isec->sid = SECINITSID_UNLABELED;  	isec->sclass = SECCLASS_FILE; -	isec->task_sid = tsec->sid; +	isec->task_sid = sid;  	inode->i_security = isec;  	return 0; @@ -215,15 +244,15 @@ static void inode_free_security(struct inode *inode)  static int file_alloc_security(struct file *file)  { -	struct task_security_struct *tsec = current->security;  	struct file_security_struct *fsec; +	u32 sid = current_sid();  	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);  	if (!fsec)  		return -ENOMEM; -	fsec->sid = tsec->sid; -	fsec->fown_sid = tsec->sid; +	fsec->sid = sid; +	fsec->fown_sid = sid;  	file->f_security = fsec;  	return 0; @@ -338,8 +367,9 @@ static const match_table_t tokens = {  static int may_context_mount_sb_relabel(u32 sid,  			struct superblock_security_struct *sbsec, -			struct task_security_struct *tsec) +			const struct cred *cred)  { +	const struct task_security_struct *tsec = cred->security;  	int rc;  	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, @@ -354,8 +384,9 @@ static int may_context_mount_sb_relabel(u32 sid,  static int may_context_mount_inode_relabel(u32 sid,  			struct superblock_security_struct *sbsec, -			struct task_security_struct *tsec) +			const struct cred *cred)  { +	const struct task_security_struct *tsec = cred->security;  	int rc;  	rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,  			  FILESYSTEM__RELABELFROM, NULL); @@ -553,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,  static int selinux_set_mnt_opts(struct super_block *sb,  				struct security_mnt_opts *opts)  { +	const struct cred *cred = current_cred();  	int rc = 0, i; -	struct task_security_struct *tsec = current->security;  	struct superblock_security_struct *sbsec = sb->s_security;  	const char *name = sb->s_type->name;  	struct inode *inode = sbsec->sb->s_root->d_inode; @@ -671,7 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  		sbsec->proc = 1;  	/* Determine the labeling behavior to use for this filesystem type. */ -	rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); +	rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);  	if (rc) {  		printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",  		       __func__, sb->s_type->name, rc); @@ -680,8 +711,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	/* sets the context of the superblock for the fs being mounted. */  	if (fscontext_sid) { - -		rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec); +		rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);  		if (rc)  			goto out; @@ -695,12 +725,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	 */  	if (context_sid) {  		if (!fscontext_sid) { -			rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec); +			rc = may_context_mount_sb_relabel(context_sid, sbsec, +							  cred);  			if (rc)  				goto out;  			sbsec->sid = context_sid;  		} else { -			rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec); +			rc = may_context_mount_inode_relabel(context_sid, sbsec, +							     cred);  			if (rc)  				goto out;  		} @@ -712,7 +744,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	}  	if (rootcontext_sid) { -		rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec); +		rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, +						     cred);  		if (rc)  			goto out; @@ -730,7 +763,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  		if (defcontext_sid != sbsec->def_sid) {  			rc = may_context_mount_inode_relabel(defcontext_sid, -							     sbsec, tsec); +							     sbsec, cred);  			if (rc)  				goto out;  		} @@ -1345,18 +1378,53 @@ static inline u32 signal_to_av(int sig)  	return perm;  } -/* Check permission betweeen a pair of tasks, e.g. signal checks, -   fork check, ptrace check, etc. */ -static int task_has_perm(struct task_struct *tsk1, -			 struct task_struct *tsk2, +/* + * Check permission between a pair of credentials + * fork check, ptrace check, etc. + */ +static int cred_has_perm(const struct cred *actor, +			 const struct cred *target,  			 u32 perms)  { -	struct task_security_struct *tsec1, *tsec2; +	u32 asid = cred_sid(actor), tsid = cred_sid(target); -	tsec1 = tsk1->security; -	tsec2 = tsk2->security; -	return avc_has_perm(tsec1->sid, tsec2->sid, -			    SECCLASS_PROCESS, perms, NULL); +	return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL); +} + +/* + * Check permission between a pair of tasks, e.g. signal checks, + * fork check, ptrace check, etc. + * tsk1 is the actor and tsk2 is the target + * - this uses the default subjective creds of tsk1 + */ +static int task_has_perm(const struct task_struct *tsk1, +			 const struct task_struct *tsk2, +			 u32 perms) +{ +	const struct task_security_struct *__tsec1, *__tsec2; +	u32 sid1, sid2; + +	rcu_read_lock(); +	__tsec1 = __task_cred(tsk1)->security;	sid1 = __tsec1->sid; +	__tsec2 = __task_cred(tsk2)->security;	sid2 = __tsec2->sid; +	rcu_read_unlock(); +	return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL); +} + +/* + * Check permission between current and another task, e.g. signal checks, + * fork check, ptrace check, etc. + * current is the actor and tsk2 is the target + * - this uses current's subjective creds + */ +static int current_has_perm(const struct task_struct *tsk, +			    u32 perms) +{ +	u32 sid, tsid; + +	sid = current_sid(); +	tsid = task_sid(tsk); +	return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);  }  #if CAP_LAST_CAP > 63 @@ -1365,14 +1433,14 @@ static int task_has_perm(struct task_struct *tsk1,  /* Check whether a task is allowed to use a capability. */  static int task_has_capability(struct task_struct *tsk, -			       int cap) +			       int cap, int audit)  { -	struct task_security_struct *tsec;  	struct avc_audit_data ad; +	struct av_decision avd;  	u16 sclass; +	u32 sid = task_sid(tsk);  	u32 av = CAP_TO_MASK(cap); - -	tsec = tsk->security; +	int rc;  	AVC_AUDIT_DATA_INIT(&ad, CAP);  	ad.tsk = tsk; @@ -1390,37 +1458,39 @@ static int task_has_capability(struct task_struct *tsk,  		       "SELinux:  out of range capability %d\n", cap);  		BUG();  	} -	return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad); + +	rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); +	if (audit == SECURITY_CAP_AUDIT) +		avc_audit(sid, sid, sclass, av, &avd, rc, &ad); +	return rc;  }  /* Check whether a task is allowed to use a system operation. */  static int task_has_system(struct task_struct *tsk,  			   u32 perms)  { -	struct task_security_struct *tsec; - -	tsec = tsk->security; +	u32 sid = task_sid(tsk); -	return avc_has_perm(tsec->sid, SECINITSID_KERNEL, +	return avc_has_perm(sid, SECINITSID_KERNEL,  			    SECCLASS_SYSTEM, perms, NULL);  }  /* Check whether a task has a particular permission to an inode.     The 'adp' parameter is optional and allows other audit     data to be passed (e.g. the dentry). */ -static int inode_has_perm(struct task_struct *tsk, +static int inode_has_perm(const struct cred *cred,  			  struct inode *inode,  			  u32 perms,  			  struct avc_audit_data *adp)  { -	struct task_security_struct *tsec;  	struct inode_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid;  	if (unlikely(IS_PRIVATE(inode)))  		return 0; -	tsec = tsk->security; +	sid = cred_sid(cred);  	isec = inode->i_security;  	if (!adp) { @@ -1429,23 +1499,24 @@ static int inode_has_perm(struct task_struct *tsk,  		ad.u.fs.inode = inode;  	} -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp); +	return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);  }  /* Same as inode_has_perm, but pass explicit audit data containing     the dentry to help the auditing code to more easily generate the     pathname if needed. */ -static inline int dentry_has_perm(struct task_struct *tsk, +static inline int dentry_has_perm(const struct cred *cred,  				  struct vfsmount *mnt,  				  struct dentry *dentry,  				  u32 av)  {  	struct inode *inode = dentry->d_inode;  	struct avc_audit_data ad; +  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.mnt = mnt;  	ad.u.fs.path.dentry = dentry; -	return inode_has_perm(tsk, inode, av, &ad); +	return inode_has_perm(cred, inode, av, &ad);  }  /* Check whether a task can use an open file descriptor to @@ -1456,33 +1527,35 @@ static inline int dentry_has_perm(struct task_struct *tsk,     has the same SID as the process.  If av is zero, then     access to the file is not checked, e.g. for cases     where only the descriptor is affected like seek. */ -static int file_has_perm(struct task_struct *tsk, -				struct file *file, -				u32 av) +static int file_has_perm(const struct cred *cred, +			 struct file *file, +			 u32 av)  { -	struct task_security_struct *tsec = tsk->security;  	struct file_security_struct *fsec = file->f_security;  	struct inode *inode = file->f_path.dentry->d_inode;  	struct avc_audit_data ad; +	u32 sid = cred_sid(cred);  	int rc;  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path = file->f_path; -	if (tsec->sid != fsec->sid) { -		rc = avc_has_perm(tsec->sid, fsec->sid, +	if (sid != fsec->sid) { +		rc = avc_has_perm(sid, fsec->sid,  				  SECCLASS_FD,  				  FD__USE,  				  &ad);  		if (rc) -			return rc; +			goto out;  	}  	/* av is zero if only checking access to the descriptor. */ +	rc = 0;  	if (av) -		return inode_has_perm(tsk, inode, av, &ad); +		rc = inode_has_perm(cred, inode, av, &ad); -	return 0; +out: +	return rc;  }  /* Check whether a task can create a file. */ @@ -1490,36 +1563,36 @@ static int may_create(struct inode *dir,  		      struct dentry *dentry,  		      u16 tclass)  { -	struct task_security_struct *tsec; +	const struct cred *cred = current_cred(); +	const struct task_security_struct *tsec = cred->security;  	struct inode_security_struct *dsec;  	struct superblock_security_struct *sbsec; -	u32 newsid; +	u32 sid, newsid;  	struct avc_audit_data ad;  	int rc; -	tsec = current->security;  	dsec = dir->i_security;  	sbsec = dir->i_sb->s_security; +	sid = tsec->sid; +	newsid = tsec->create_sid; +  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.dentry = dentry; -	rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, +	rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,  			  DIR__ADD_NAME | DIR__SEARCH,  			  &ad);  	if (rc)  		return rc; -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { -		newsid = tsec->create_sid; -	} else { -		rc = security_transition_sid(tsec->sid, dsec->sid, tclass, -					     &newsid); +	if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) { +		rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);  		if (rc)  			return rc;  	} -	rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad); +	rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);  	if (rc)  		return rc; @@ -1532,11 +1605,9 @@ static int may_create(struct inode *dir,  static int may_create_key(u32 ksid,  			  struct task_struct *ctx)  { -	struct task_security_struct *tsec; - -	tsec = ctx->security; +	u32 sid = task_sid(ctx); -	return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); +	return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);  }  #define MAY_LINK	0 @@ -1549,13 +1620,12 @@ static int may_link(struct inode *dir,  		    int kind)  { -	struct task_security_struct *tsec;  	struct inode_security_struct *dsec, *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	u32 av;  	int rc; -	tsec = current->security;  	dsec = dir->i_security;  	isec = dentry->d_inode->i_security; @@ -1564,7 +1634,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, &ad); +	rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);  	if (rc)  		return rc; @@ -1584,7 +1654,7 @@ static int may_link(struct inode *dir,  		return 0;  	} -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad); +	rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);  	return rc;  } @@ -1593,14 +1663,13 @@ static inline int may_rename(struct inode *old_dir,  			     struct inode *new_dir,  			     struct dentry *new_dentry)  { -	struct task_security_struct *tsec;  	struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	u32 av;  	int old_is_dir, new_is_dir;  	int rc; -	tsec = current->security;  	old_dsec = old_dir->i_security;  	old_isec = old_dentry->d_inode->i_security;  	old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); @@ -1609,16 +1678,16 @@ static inline int may_rename(struct inode *old_dir,  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.dentry = old_dentry; -	rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR, +	rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,  			  DIR__REMOVE_NAME | DIR__SEARCH, &ad);  	if (rc)  		return rc; -	rc = avc_has_perm(tsec->sid, old_isec->sid, +	rc = avc_has_perm(sid, old_isec->sid,  			  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, +		rc = avc_has_perm(sid, old_isec->sid,  				  old_isec->sclass, DIR__REPARENT, &ad);  		if (rc)  			return rc; @@ -1628,13 +1697,13 @@ 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, &ad); +	rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);  	if (rc)  		return rc;  	if (new_dentry->d_inode) {  		new_isec = new_dentry->d_inode->i_security;  		new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode); -		rc = avc_has_perm(tsec->sid, new_isec->sid, +		rc = avc_has_perm(sid, new_isec->sid,  				  new_isec->sclass,  				  (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);  		if (rc) @@ -1645,18 +1714,16 @@ static inline int may_rename(struct inode *old_dir,  }  /* Check whether a task can perform a filesystem operation. */ -static int superblock_has_perm(struct task_struct *tsk, +static int superblock_has_perm(const struct cred *cred,  			       struct super_block *sb,  			       u32 perms,  			       struct avc_audit_data *ad)  { -	struct task_security_struct *tsec;  	struct superblock_security_struct *sbsec; +	u32 sid = cred_sid(cred); -	tsec = tsk->security;  	sbsec = sb->s_security; -	return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, -			    perms, ad); +	return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);  }  /* Convert a Linux mode and permission mask to an access vector. */ @@ -1687,15 +1754,39 @@ static inline u32 file_mask_to_av(int mode, int mask)  	return av;  } +/* Convert a Linux file to an access vector. */ +static inline u32 file_to_av(struct file *file) +{ +	u32 av = 0; + +	if (file->f_mode & FMODE_READ) +		av |= FILE__READ; +	if (file->f_mode & FMODE_WRITE) { +		if (file->f_flags & O_APPEND) +			av |= FILE__APPEND; +		else +			av |= FILE__WRITE; +	} +	if (!av) { +		/* +		 * Special file opened with flags 3 for ioctl-only use. +		 */ +		av = FILE__IOCTL; +	} + +	return av; +} +  /* - * Convert a file mask to an access vector and include the correct open + * Convert a file to an access vector and include the correct open   * open permission.   */ -static inline u32 open_file_mask_to_av(int mode, int mask) +static inline u32 open_file_to_av(struct file *file)  { -	u32 av = file_mask_to_av(mode, mask); +	u32 av = file_to_av(file);  	if (selinux_policycap_openperm) { +		mode_t mode = file->f_path.dentry->d_inode->i_mode;  		/*  		 * lnk files and socks do not really have an 'open'  		 */ @@ -1711,31 +1802,8 @@ static inline u32 open_file_mask_to_av(int mode, int mask)  			av |= DIR__OPEN;  		else  			printk(KERN_ERR "SELinux: WARNING: inside %s with " -				"unknown mode:%x\n", __func__, mode); -	} -	return av; -} - -/* Convert a Linux file to an access vector. */ -static inline u32 file_to_av(struct file *file) -{ -	u32 av = 0; - -	if (file->f_mode & FMODE_READ) -		av |= FILE__READ; -	if (file->f_mode & FMODE_WRITE) { -		if (file->f_flags & O_APPEND) -			av |= FILE__APPEND; -		else -			av |= FILE__WRITE; +				"unknown mode:%o\n", __func__, mode);  	} -	if (!av) { -		/* -		 * Special file opened with flags 3 for ioctl-only use. -		 */ -		av = FILE__IOCTL; -	} -  	return av;  } @@ -1751,13 +1819,12 @@ static int selinux_ptrace_may_access(struct task_struct *child,  		return rc;  	if (mode == PTRACE_MODE_READ) { -		struct task_security_struct *tsec = current->security; -		struct task_security_struct *csec = child->security; -		return avc_has_perm(tsec->sid, csec->sid, -				    SECCLASS_FILE, FILE__READ, NULL); +		u32 sid = current_sid(); +		u32 csid = task_sid(child); +		return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);  	} -	return task_has_perm(current, child, PROCESS__PTRACE); +	return current_has_perm(child, PROCESS__PTRACE);  }  static int selinux_ptrace_traceme(struct task_struct *parent) @@ -1776,40 +1843,37 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,  {  	int error; -	error = task_has_perm(current, target, PROCESS__GETCAP); +	error = current_has_perm(target, PROCESS__GETCAP);  	if (error)  		return error;  	return secondary_ops->capget(target, effective, inheritable, permitted);  } -static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective, -				kernel_cap_t *inheritable, kernel_cap_t *permitted) +static int selinux_capset(struct cred *new, const struct cred *old, +			  const kernel_cap_t *effective, +			  const kernel_cap_t *inheritable, +			  const kernel_cap_t *permitted)  {  	int error; -	error = secondary_ops->capset_check(target, effective, inheritable, permitted); +	error = secondary_ops->capset(new, old, +				      effective, inheritable, permitted);  	if (error)  		return error; -	return task_has_perm(current, target, PROCESS__SETCAP); -} - -static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, -			       kernel_cap_t *inheritable, kernel_cap_t *permitted) -{ -	secondary_ops->capset_set(target, effective, inheritable, permitted); +	return cred_has_perm(old, new, PROCESS__SETCAP);  } -static int selinux_capable(struct task_struct *tsk, int cap) +static int selinux_capable(struct task_struct *tsk, int cap, int audit)  {  	int rc; -	rc = secondary_ops->capable(tsk, cap); +	rc = secondary_ops->capable(tsk, cap, audit);  	if (rc)  		return rc; -	return task_has_capability(tsk, cap); +	return task_has_capability(tsk, cap, audit);  }  static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -1857,15 +1921,14 @@ static int selinux_sysctl(ctl_table *table, int op)  {  	int error = 0;  	u32 av; -	struct task_security_struct *tsec; -	u32 tsid; +	u32 tsid, sid;  	int rc;  	rc = secondary_ops->sysctl(table, op);  	if (rc)  		return rc; -	tsec = current->security; +	sid = current_sid();  	rc = selinux_sysctl_get_sid(table, (op == 0001) ?  				    SECCLASS_DIR : SECCLASS_FILE, &tsid); @@ -1877,7 +1940,7 @@ static int selinux_sysctl(ctl_table *table, int op)  	/* 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(tsec->sid, tsid, +		error = avc_has_perm(sid, tsid,  				     SECCLASS_DIR, DIR__SEARCH, NULL);  	} else {  		av = 0; @@ -1886,7 +1949,7 @@ static int selinux_sysctl(ctl_table *table, int op)  		if (op & 002)  			av |= FILE__WRITE;  		if (av) -			error = avc_has_perm(tsec->sid, tsid, +			error = avc_has_perm(sid, tsid,  					     SECCLASS_FILE, av, NULL);  	} @@ -1895,6 +1958,7 @@ static int selinux_sysctl(ctl_table *table, int op)  static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)  { +	const struct cred *cred = current_cred();  	int rc = 0;  	if (!sb) @@ -1906,14 +1970,12 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)  	case Q_QUOTAOFF:  	case Q_SETINFO:  	case Q_SETQUOTA: -		rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD, -					 NULL); +		rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL);  		break;  	case Q_GETFMT:  	case Q_GETINFO:  	case Q_GETQUOTA: -		rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET, -					 NULL); +		rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL);  		break;  	default:  		rc = 0;  /* let the kernel handle invalid cmds */ @@ -1924,7 +1986,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)  static int selinux_quota_on(struct dentry *dentry)  { -	return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); +	const struct cred *cred = current_cred(); + +	return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);  }  static int selinux_syslog(int type) @@ -1972,16 +2036,8 @@ static int selinux_syslog(int type)  static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)  {  	int rc, cap_sys_admin = 0; -	struct task_security_struct *tsec = current->security; - -	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), -					  0, -					  NULL); +	rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);  	if (rc == 0)  		cap_sys_admin = 1; @@ -1990,59 +2046,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)  /* binprm security operations */ -static int selinux_bprm_alloc_security(struct linux_binprm *bprm) +static int selinux_bprm_set_creds(struct linux_binprm *bprm)  { -	struct bprm_security_struct *bsec; - -	bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); -	if (!bsec) -		return -ENOMEM; - -	bsec->sid = SECINITSID_UNLABELED; -	bsec->set = 0; - -	bprm->security = bsec; -	return 0; -} - -static int selinux_bprm_set_security(struct linux_binprm *bprm) -{ -	struct task_security_struct *tsec; -	struct inode *inode = bprm->file->f_path.dentry->d_inode; +	const struct task_security_struct *old_tsec; +	struct task_security_struct *new_tsec;  	struct inode_security_struct *isec; -	struct bprm_security_struct *bsec; -	u32 newsid;  	struct avc_audit_data ad; +	struct inode *inode = bprm->file->f_path.dentry->d_inode;  	int rc; -	rc = secondary_ops->bprm_set_security(bprm); +	rc = secondary_ops->bprm_set_creds(bprm);  	if (rc)  		return rc; -	bsec = bprm->security; - -	if (bsec->set) +	/* SELinux context only depends on initial program or script and not +	 * the script interpreter */ +	if (bprm->cred_prepared)  		return 0; -	tsec = current->security; +	old_tsec = current_security(); +	new_tsec = bprm->cred->security;  	isec = inode->i_security;  	/* Default to the current task SID. */ -	bsec->sid = tsec->sid; +	new_tsec->sid = old_tsec->sid; +	new_tsec->osid = old_tsec->sid;  	/* Reset fs, key, and sock SIDs on execve. */ -	tsec->create_sid = 0; -	tsec->keycreate_sid = 0; -	tsec->sockcreate_sid = 0; +	new_tsec->create_sid = 0; +	new_tsec->keycreate_sid = 0; +	new_tsec->sockcreate_sid = 0; -	if (tsec->exec_sid) { -		newsid = tsec->exec_sid; +	if (old_tsec->exec_sid) { +		new_tsec->sid = old_tsec->exec_sid;  		/* Reset exec SID on execve. */ -		tsec->exec_sid = 0; +		new_tsec->exec_sid = 0;  	} else {  		/* Check for a default transition on this program. */ -		rc = security_transition_sid(tsec->sid, isec->sid, -					     SECCLASS_PROCESS, &newsid); +		rc = security_transition_sid(old_tsec->sid, isec->sid, +					     SECCLASS_PROCESS, &new_tsec->sid);  		if (rc)  			return rc;  	} @@ -2051,33 +2093,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)  	ad.u.fs.path = bprm->file->f_path;  	if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) -		newsid = tsec->sid; +		new_tsec->sid = old_tsec->sid; -	if (tsec->sid == newsid) { -		rc = avc_has_perm(tsec->sid, isec->sid, +	if (new_tsec->sid == old_tsec->sid) { +		rc = avc_has_perm(old_tsec->sid, isec->sid,  				  SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);  		if (rc)  			return rc;  	} else {  		/* Check permissions for the transition. */ -		rc = avc_has_perm(tsec->sid, newsid, +		rc = avc_has_perm(old_tsec->sid, new_tsec->sid,  				  SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);  		if (rc)  			return rc; -		rc = avc_has_perm(newsid, isec->sid, +		rc = avc_has_perm(new_tsec->sid, isec->sid,  				  SECCLASS_FILE, FILE__ENTRYPOINT, &ad);  		if (rc)  			return rc; -		/* Clear any possibly unsafe personality bits on exec: */ -		current->personality &= ~PER_CLEAR_ON_SETID; +		/* Check for shared state */ +		if (bprm->unsafe & LSM_UNSAFE_SHARE) { +			rc = avc_has_perm(old_tsec->sid, new_tsec->sid, +					  SECCLASS_PROCESS, PROCESS__SHARE, +					  NULL); +			if (rc) +				return -EPERM; +		} + +		/* Make sure that anyone attempting to ptrace over a task that +		 * 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; -		/* Set the security field to the new SID. */ -		bsec->sid = newsid; +			rcu_read_lock(); +			tracer = tracehook_tracer_task(current); +			if (likely(tracer != NULL)) { +				sec = __task_cred(tracer)->security; +				ptsid = sec->sid; +			} +			rcu_read_unlock(); + +			if (ptsid != 0) { +				rc = avc_has_perm(ptsid, new_tsec->sid, +						  SECCLASS_PROCESS, +						  PROCESS__PTRACE, NULL); +				if (rc) +					return -EPERM; +			} +		} + +		/* Clear any possibly unsafe personality bits on exec: */ +		bprm->per_clear |= PER_CLEAR_ON_SETID;  	} -	bsec->set = 1;  	return 0;  } @@ -2086,35 +2158,34 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)  	return secondary_ops->bprm_check_security(bprm);  } -  static int selinux_bprm_secureexec(struct linux_binprm *bprm)  { -	struct task_security_struct *tsec = current->security; +	const struct cred *cred = current_cred(); +	const struct task_security_struct *tsec = cred->security; +	u32 sid, osid;  	int atsecure = 0; -	if (tsec->osid != tsec->sid) { +	sid = tsec->sid; +	osid = tsec->osid; + +	if (osid != sid) {  		/* Enable secure mode for SIDs transitions unless  		   the noatsecure permission is granted between  		   the two SIDs, i.e. ahp returns 0. */ -		atsecure = avc_has_perm(tsec->osid, tsec->sid, -					 SECCLASS_PROCESS, -					 PROCESS__NOATSECURE, NULL); +		atsecure = avc_has_perm(osid, sid, +					SECCLASS_PROCESS, +					PROCESS__NOATSECURE, NULL);  	}  	return (atsecure || secondary_ops->bprm_secureexec(bprm));  } -static void selinux_bprm_free_security(struct linux_binprm *bprm) -{ -	kfree(bprm->security); -	bprm->security = NULL; -} -  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) +static inline void flush_unauthorized_files(const struct cred *cred, +					    struct files_struct *files)  {  	struct avc_audit_data ad;  	struct file *file, *devnull = NULL; @@ -2136,7 +2207,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)  			   interested in the inode-based check here. */  			file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);  			inode = file->f_path.dentry->d_inode; -			if (inode_has_perm(current, inode, +			if (inode_has_perm(cred, inode,  					   FILE__READ | FILE__WRITE, NULL)) {  				drop_tty = 1;  			} @@ -2171,7 +2242,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)  				file = fget(i);  				if (!file)  					continue; -				if (file_has_perm(current, +				if (file_has_perm(cred,  						  file,  						  file_to_av(file))) {  					sys_close(i); @@ -2185,7 +2256,10 @@ static inline void flush_unauthorized_files(struct files_struct *files)  					if (devnull) {  						get_file(devnull);  					} else { -						devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); +						devnull = dentry_open( +							dget(selinux_null), +							mntget(selinuxfs_mount), +							O_RDWR, cred);  						if (IS_ERR(devnull)) {  							devnull = NULL;  							put_unused_fd(fd); @@ -2204,94 +2278,78 @@ static inline void flush_unauthorized_files(struct files_struct *files)  	spin_unlock(&files->file_lock);  } -static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) +/* + * Prepare a process for imminent new credential changes due to exec + */ +static void selinux_bprm_committing_creds(struct linux_binprm *bprm)  { -	struct task_security_struct *tsec; -	struct bprm_security_struct *bsec; -	u32 sid; -	int rc; - -	secondary_ops->bprm_apply_creds(bprm, unsafe); - -	tsec = current->security; +	struct task_security_struct *new_tsec; +	struct rlimit *rlim, *initrlim; +	int rc, i; -	bsec = bprm->security; -	sid = bsec->sid; +	secondary_ops->bprm_committing_creds(bprm); -	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(tsec->sid, sid, SECCLASS_PROCESS, -					PROCESS__SHARE, NULL); -			if (rc) { -				bsec->unsafe = 1; -				return; -			} -		} +	new_tsec = bprm->cred->security; +	if (new_tsec->sid == new_tsec->osid) +		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)) { -			struct task_struct *tracer; -			struct task_security_struct *sec; -			u32 ptsid = 0; +	/* Close files for which the new task SID is not authorized. */ +	flush_unauthorized_files(bprm->cred, current->files); -			rcu_read_lock(); -			tracer = tracehook_tracer_task(current); -			if (likely(tracer != NULL)) { -				sec = tracer->security; -				ptsid = sec->sid; -			} -			rcu_read_unlock(); +	/* Always clear parent death signal on SID transitions. */ +	current->pdeath_signal = 0; -			if (ptsid != 0) { -				rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, -						  PROCESS__PTRACE, NULL); -				if (rc) { -					bsec->unsafe = 1; -					return; -				} -			} +	/* 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(new_tsec->osid, new_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);  		} -		tsec->sid = sid; +		update_rlimit_cpu(rlim->rlim_cur);  	}  }  /* - * called after apply_creds without the task lock held + * Clean up the process immediately after the installation of new credentials + * due to exec   */ -static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm) +static void selinux_bprm_committed_creds(struct linux_binprm *bprm)  { -	struct task_security_struct *tsec; -	struct rlimit *rlim, *initrlim; +	const struct task_security_struct *tsec = current_security();  	struct itimerval itimer; -	struct bprm_security_struct *bsec; +	struct sighand_struct *psig; +	u32 osid, sid;  	int rc, i; +	unsigned long flags; -	tsec = current->security; -	bsec = bprm->security; +	secondary_ops->bprm_committed_creds(bprm); -	if (bsec->unsafe) { -		force_sig_specific(SIGKILL, current); -		return; -	} -	if (tsec->osid == tsec->sid) +	osid = tsec->osid; +	sid = tsec->sid; + +	if (sid == osid)  		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); +	/* 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(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);  	if (rc) {  		memset(&itimer, 0, sizeof itimer);  		for (i = 0; i < 3; i++) @@ -2304,33 +2362,14 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)  		spin_unlock_irq(¤t->sighand->siglock);  	} -	/* Always clear parent death signal on SID transitions. */ -	current->pdeath_signal = 0; - -	/* 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); -		} -		update_rlimit_cpu(rlim->rlim_cur); -	} - -	/* Wake up the parent if it is waiting so that it can -	   recheck wait permission to the new task SID. */ +	/* Wake up the parent if it is waiting so that it can recheck +	 * wait permission to the new task SID. */ +	read_lock_irq(&tasklist_lock); +	psig = current->parent->sighand; +	spin_lock_irqsave(&psig->siglock, flags);  	wake_up_interruptible(¤t->parent->signal->wait_chldexit); +	spin_unlock_irqrestore(&psig->siglock, flags); +	read_unlock_irq(&tasklist_lock);  }  /* superblock security operations */ @@ -2435,8 +2474,9 @@ out:  	return rc;  } -static int selinux_sb_kern_mount(struct super_block *sb, void *data) +static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)  { +	const struct cred *cred = current_cred();  	struct avc_audit_data ad;  	int rc; @@ -2444,18 +2484,23 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)  	if (rc)  		return rc; +	/* Allow all mounts performed by the kernel */ +	if (flags & MS_KERNMOUNT) +		return 0; +  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.dentry = sb->s_root; -	return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad); +	return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);  }  static int selinux_sb_statfs(struct dentry *dentry)  { +	const struct cred *cred = current_cred();  	struct avc_audit_data ad;  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.dentry = dentry->d_sb->s_root; -	return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad); +	return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);  }  static int selinux_mount(char *dev_name, @@ -2464,6 +2509,7 @@ static int selinux_mount(char *dev_name,  			 unsigned long flags,  			 void *data)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->sb_mount(dev_name, path, type, flags, data); @@ -2471,22 +2517,23 @@ static int selinux_mount(char *dev_name,  		return rc;  	if (flags & MS_REMOUNT) -		return superblock_has_perm(current, path->mnt->mnt_sb, +		return superblock_has_perm(cred, path->mnt->mnt_sb,  					   FILESYSTEM__REMOUNT, NULL);  	else -		return dentry_has_perm(current, path->mnt, path->dentry, +		return dentry_has_perm(cred, path->mnt, path->dentry,  				       FILE__MOUNTON);  }  static int selinux_umount(struct vfsmount *mnt, int flags)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->sb_umount(mnt, flags);  	if (rc)  		return rc; -	return superblock_has_perm(current, mnt->mnt_sb, +	return superblock_has_perm(cred, mnt->mnt_sb,  				   FILESYSTEM__UNMOUNT, NULL);  } @@ -2506,21 +2553,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,  				       char **name, void **value,  				       size_t *len)  { -	struct task_security_struct *tsec; +	const struct cred *cred = current_cred(); +	const struct task_security_struct *tsec = cred->security;  	struct inode_security_struct *dsec;  	struct superblock_security_struct *sbsec; -	u32 newsid, clen; +	u32 sid, newsid, clen;  	int rc;  	char *namep = NULL, *context; -	tsec = current->security;  	dsec = dir->i_security;  	sbsec = dir->i_sb->s_security; -	if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { -		newsid = tsec->create_sid; -	} else { -		rc = security_transition_sid(tsec->sid, dsec->sid, +	sid = tsec->sid; +	newsid = tsec->create_sid; + +	if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) { +		rc = security_transition_sid(sid, dsec->sid,  					     inode_mode_to_security_class(inode->i_mode),  					     &newsid);  		if (rc) { @@ -2623,21 +2671,25 @@ static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dent  static int selinux_inode_readlink(struct dentry *dentry)  { -	return dentry_has_perm(current, NULL, dentry, FILE__READ); +	const struct cred *cred = current_cred(); + +	return dentry_has_perm(cred, NULL, dentry, FILE__READ);  }  static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->inode_follow_link(dentry, nameidata);  	if (rc)  		return rc; -	return dentry_has_perm(current, NULL, dentry, FILE__READ); +	return dentry_has_perm(cred, NULL, dentry, FILE__READ);  }  static int selinux_inode_permission(struct inode *inode, int mask)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->inode_permission(inode, mask); @@ -2649,12 +2701,13 @@ static int selinux_inode_permission(struct inode *inode, int mask)  		return 0;  	} -	return inode_has_perm(current, inode, -			       open_file_mask_to_av(inode->i_mode, mask), NULL); +	return inode_has_perm(cred, inode, +			      file_mask_to_av(inode->i_mode, mask), NULL);  }  static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->inode_setattr(dentry, iattr); @@ -2666,18 +2719,22 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)  	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); +		return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR); -	return dentry_has_perm(current, NULL, dentry, FILE__WRITE); +	return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);  }  static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)  { -	return dentry_has_perm(current, mnt, dentry, FILE__GETATTR); +	const struct cred *cred = current_cred(); + +	return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);  }  static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)  { +	const struct cred *cred = current_cred(); +  	if (!strncmp(name, XATTR_SECURITY_PREFIX,  		     sizeof XATTR_SECURITY_PREFIX - 1)) {  		if (!strcmp(name, XATTR_NAME_CAPS)) { @@ -2692,18 +2749,17 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)  	/* Not an attribute we recognize, so just check the  	   ordinary setattr permission. */ -	return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); +	return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);  }  static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  				  const void *value, size_t size, int flags)  { -	struct task_security_struct *tsec = current->security;  	struct inode *inode = dentry->d_inode;  	struct inode_security_struct *isec = inode->i_security;  	struct superblock_security_struct *sbsec;  	struct avc_audit_data ad; -	u32 newsid; +	u32 newsid, sid = current_sid();  	int rc = 0;  	if (strcmp(name, XATTR_NAME_SELINUX)) @@ -2719,7 +2775,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  	AVC_AUDIT_DATA_INIT(&ad, FS);  	ad.u.fs.path.dentry = dentry; -	rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, +	rc = avc_has_perm(sid, isec->sid, isec->sclass,  			  FILE__RELABELFROM, &ad);  	if (rc)  		return rc; @@ -2733,12 +2789,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  	if (rc)  		return rc; -	rc = avc_has_perm(tsec->sid, newsid, isec->sclass, +	rc = avc_has_perm(sid, newsid, isec->sclass,  			  FILE__RELABELTO, &ad);  	if (rc)  		return rc; -	rc = security_validate_transition(isec->sid, newsid, tsec->sid, +	rc = security_validate_transition(isec->sid, newsid, sid,  					  isec->sclass);  	if (rc)  		return rc; @@ -2778,12 +2834,16 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,  static int selinux_inode_getxattr(struct dentry *dentry, const char *name)  { -	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); +	const struct cred *cred = current_cred(); + +	return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);  }  static int selinux_inode_listxattr(struct dentry *dentry)  { -	return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); +	const struct cred *cred = current_cred(); + +	return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);  }  static int selinux_inode_removexattr(struct dentry *dentry, const char *name) @@ -2806,7 +2866,6 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name  	u32 size;  	int error;  	char *context = NULL; -	struct task_security_struct *tsec = current->security;  	struct inode_security_struct *isec = inode->i_security;  	if (strcmp(name, XATTR_SELINUX_SUFFIX)) @@ -2821,13 +2880,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name  	 * and lack of permission just means that we fall back to the  	 * in-core context value, not a denial.  	 */ -	error = secondary_ops->capable(current, CAP_MAC_ADMIN); -	if (!error) -		error = avc_has_perm_noaudit(tsec->sid, tsec->sid, -					     SECCLASS_CAPABILITY2, -					     CAPABILITY2__MAC_ADMIN, -					     0, -					     NULL); +	error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);  	if (!error)  		error = security_sid_to_context_force(isec->sid, &context,  						      &size); @@ -2894,6 +2947,7 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)  static int selinux_revalidate_file_permission(struct file *file, int mask)  { +	const struct cred *cred = current_cred();  	int rc;  	struct inode *inode = file->f_path.dentry->d_inode; @@ -2906,7 +2960,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)  	if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))  		mask |= MAY_APPEND; -	rc = file_has_perm(current, file, +	rc = file_has_perm(cred, file,  			   file_mask_to_av(inode->i_mode, mask));  	if (rc)  		return rc; @@ -2917,16 +2971,16 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)  static int selinux_file_permission(struct file *file, int mask)  {  	struct inode *inode = file->f_path.dentry->d_inode; -	struct task_security_struct *tsec = current->security;  	struct file_security_struct *fsec = file->f_security;  	struct inode_security_struct *isec = inode->i_security; +	u32 sid = current_sid();  	if (!mask) {  		/* No permission to check.  Existence test. */  		return 0;  	} -	if (tsec->sid == fsec->sid && fsec->isid == isec->sid +	if (sid == fsec->sid && fsec->isid == isec->sid  	    && fsec->pseqno == avc_policy_seqno())  		return selinux_netlbl_inode_permission(inode, mask); @@ -2946,6 +3000,7 @@ static void selinux_file_free_security(struct file *file)  static int selinux_file_ioctl(struct file *file, unsigned int cmd,  			      unsigned long arg)  { +	const struct cred *cred = current_cred();  	u32 av = 0;  	if (_IOC_DIR(cmd) & _IOC_WRITE) @@ -2955,11 +3010,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,  	if (!av)  		av = FILE__IOCTL; -	return file_has_perm(current, file, av); +	return file_has_perm(cred, file, av);  }  static int file_map_prot_check(struct file *file, unsigned long prot, int shared)  { +	const struct cred *cred = current_cred(); +	int rc = 0; +  #ifndef CONFIG_PPC32  	if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {  		/* @@ -2967,9 +3025,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared  		 * private file mapping that will also be writable.  		 * This has an additional check.  		 */ -		int rc = task_has_perm(current, current, PROCESS__EXECMEM); +		rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);  		if (rc) -			return rc; +			goto error;  	}  #endif @@ -2984,9 +3042,11 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared  		if (prot & PROT_EXEC)  			av |= FILE__EXECUTE; -		return file_has_perm(current, file, av); +		return file_has_perm(cred, file, av);  	} -	return 0; + +error: +	return rc;  }  static int selinux_file_mmap(struct file *file, unsigned long reqprot, @@ -2994,7 +3054,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,  			     unsigned long addr, unsigned long addr_only)  {  	int rc = 0; -	u32 sid = ((struct task_security_struct *)(current->security))->sid; +	u32 sid = current_sid();  	if (addr < mmap_min_addr)  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, @@ -3013,6 +3073,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,  				 unsigned long reqprot,  				 unsigned long prot)  { +	const struct cred *cred = current_cred();  	int rc;  	rc = secondary_ops->file_mprotect(vma, reqprot, prot); @@ -3027,12 +3088,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,  		rc = 0;  		if (vma->vm_start >= vma->vm_mm->start_brk &&  		    vma->vm_end <= vma->vm_mm->brk) { -			rc = task_has_perm(current, current, -					   PROCESS__EXECHEAP); +			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) { -			rc = task_has_perm(current, current, PROCESS__EXECSTACK); +			rc = current_has_perm(current, PROCESS__EXECSTACK);  		} else if (vma->vm_file && vma->anon_vma) {  			/*  			 * We are making executable a file mapping that has @@ -3041,8 +3101,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,  			 * modified content.  This typically should only  			 * occur for text relocations.  			 */ -			rc = file_has_perm(current, vma->vm_file, -					   FILE__EXECMOD); +			rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);  		}  		if (rc)  			return rc; @@ -3054,12 +3113,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,  static int selinux_file_lock(struct file *file, unsigned int cmd)  { -	return file_has_perm(current, file, FILE__LOCK); +	const struct cred *cred = current_cred(); + +	return file_has_perm(cred, file, FILE__LOCK);  }  static int selinux_file_fcntl(struct file *file, unsigned int cmd,  			      unsigned long arg)  { +	const struct cred *cred = current_cred();  	int err = 0;  	switch (cmd) { @@ -3070,7 +3132,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  		}  		if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) { -			err = file_has_perm(current, file, FILE__WRITE); +			err = file_has_perm(cred, file, FILE__WRITE);  			break;  		}  		/* fall through */ @@ -3080,7 +3142,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  	case F_GETOWN:  	case F_GETSIG:  		/* Just check FD__USE permission */ -		err = file_has_perm(current, file, 0); +		err = file_has_perm(cred, file, 0);  		break;  	case F_GETLK:  	case F_SETLK: @@ -3094,7 +3156,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  			err = -EINVAL;  			break;  		} -		err = file_has_perm(current, file, FILE__LOCK); +		err = file_has_perm(cred, file, FILE__LOCK);  		break;  	} @@ -3103,12 +3165,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  static int selinux_file_set_fowner(struct file *file)  { -	struct task_security_struct *tsec;  	struct file_security_struct *fsec; -	tsec = current->security;  	fsec = file->f_security; -	fsec->fown_sid = tsec->sid; +	fsec->fown_sid = current_sid();  	return 0;  } @@ -3117,14 +3177,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,  				       struct fown_struct *fown, int signum)  {  	struct file *file; +	u32 sid = current_sid();  	u32 perm; -	struct task_security_struct *tsec;  	struct file_security_struct *fsec;  	/* struct fown_struct is never outside the context of a struct file */  	file = container_of(fown, struct file, f_owner); -	tsec = tsk->security;  	fsec = file->f_security;  	if (!signum) @@ -3132,20 +3191,23 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,  	else  		perm = signal_to_av(signum); -	return avc_has_perm(fsec->fown_sid, tsec->sid, +	return avc_has_perm(fsec->fown_sid, sid,  			    SECCLASS_PROCESS, perm, NULL);  }  static int selinux_file_receive(struct file *file)  { -	return file_has_perm(current, file, file_to_av(file)); +	const struct cred *cred = current_cred(); + +	return file_has_perm(cred, file, file_to_av(file));  } -static int selinux_dentry_open(struct file *file) +static int selinux_dentry_open(struct file *file, const struct cred *cred)  {  	struct file_security_struct *fsec;  	struct inode *inode;  	struct inode_security_struct *isec; +  	inode = file->f_path.dentry->d_inode;  	fsec = file->f_security;  	isec = inode->i_security; @@ -3166,7 +3228,7 @@ static int selinux_dentry_open(struct file *file)  	 * new inode label or new policy.  	 * This check is not redundant - do not remove.  	 */ -	return inode_has_perm(current, inode, file_to_av(file), NULL); +	return inode_has_perm(cred, inode, open_file_to_av(file), NULL);  }  /* task security operations */ @@ -3179,36 +3241,88 @@ static int selinux_task_create(unsigned long clone_flags)  	if (rc)  		return rc; -	return task_has_perm(current, current, PROCESS__FORK); +	return current_has_perm(current, PROCESS__FORK);  } -static int selinux_task_alloc_security(struct task_struct *tsk) +/* + * detach and free the LSM part of a set of credentials + */ +static void selinux_cred_free(struct cred *cred)  { -	struct task_security_struct *tsec1, *tsec2; -	int rc; - -	tsec1 = current->security; +	struct task_security_struct *tsec = cred->security; +	cred->security = NULL; +	kfree(tsec); +} -	rc = task_alloc_security(tsk); -	if (rc) -		return rc; -	tsec2 = tsk->security; +/* + * prepare a new set of credentials for modification + */ +static int selinux_cred_prepare(struct cred *new, const struct cred *old, +				gfp_t gfp) +{ +	const struct task_security_struct *old_tsec; +	struct task_security_struct *tsec; -	tsec2->osid = tsec1->osid; -	tsec2->sid = tsec1->sid; +	old_tsec = old->security; -	/* Retain the exec, fs, key, and sock SIDs across fork */ -	tsec2->exec_sid = tsec1->exec_sid; -	tsec2->create_sid = tsec1->create_sid; -	tsec2->keycreate_sid = tsec1->keycreate_sid; -	tsec2->sockcreate_sid = tsec1->sockcreate_sid; +	tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp); +	if (!tsec) +		return -ENOMEM; +	new->security = tsec;  	return 0;  } -static void selinux_task_free_security(struct task_struct *tsk) +/* + * commit new credentials + */ +static void selinux_cred_commit(struct cred *new, const struct cred *old) +{ +	secondary_ops->cred_commit(new, old); +} + +/* + * set the security data for a kernel service + * - all the creation contexts are set to unlabelled + */ +static int selinux_kernel_act_as(struct cred *new, u32 secid)  { -	task_free_security(tsk); +	struct task_security_struct *tsec = new->security; +	u32 sid = current_sid(); +	int ret; + +	ret = avc_has_perm(sid, secid, +			   SECCLASS_KERNEL_SERVICE, +			   KERNEL_SERVICE__USE_AS_OVERRIDE, +			   NULL); +	if (ret == 0) { +		tsec->sid = secid; +		tsec->create_sid = 0; +		tsec->keycreate_sid = 0; +		tsec->sockcreate_sid = 0; +	} +	return ret; +} + +/* + * set the file creation context in a security record to the same as the + * objective context of the specified inode + */ +static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) +{ +	struct inode_security_struct *isec = inode->i_security; +	struct task_security_struct *tsec = new->security; +	u32 sid = current_sid(); +	int ret; + +	ret = avc_has_perm(sid, isec->sid, +			   SECCLASS_KERNEL_SERVICE, +			   KERNEL_SERVICE__CREATE_FILES_AS, +			   NULL); + +	if (ret == 0) +		tsec->create_sid = isec->sid; +	return 0;  }  static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) @@ -3222,9 +3336,10 @@ static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)  	return 0;  } -static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) +static int selinux_task_fix_setuid(struct cred *new, const struct cred *old, +				   int flags)  { -	return secondary_ops->task_post_setuid(id0, id1, id2, flags); +	return secondary_ops->task_fix_setuid(new, old, flags);  }  static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags) @@ -3235,23 +3350,22 @@ static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)  static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)  { -	return task_has_perm(current, p, PROCESS__SETPGID); +	return current_has_perm(p, PROCESS__SETPGID);  }  static int selinux_task_getpgid(struct task_struct *p)  { -	return task_has_perm(current, p, PROCESS__GETPGID); +	return current_has_perm(p, PROCESS__GETPGID);  }  static int selinux_task_getsid(struct task_struct *p)  { -	return task_has_perm(current, p, PROCESS__GETSESSION); +	return current_has_perm(p, PROCESS__GETSESSION);  }  static void selinux_task_getsecid(struct task_struct *p, u32 *secid)  { -	struct task_security_struct *tsec = p->security; -	*secid = tsec->sid; +	*secid = task_sid(p);  }  static int selinux_task_setgroups(struct group_info *group_info) @@ -3268,7 +3382,7 @@ static int selinux_task_setnice(struct task_struct *p, int nice)  	if (rc)  		return rc; -	return task_has_perm(current, p, PROCESS__SETSCHED); +	return current_has_perm(p, PROCESS__SETSCHED);  }  static int selinux_task_setioprio(struct task_struct *p, int ioprio) @@ -3279,12 +3393,12 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)  	if (rc)  		return rc; -	return task_has_perm(current, p, PROCESS__SETSCHED); +	return current_has_perm(p, PROCESS__SETSCHED);  }  static int selinux_task_getioprio(struct task_struct *p)  { -	return task_has_perm(current, p, PROCESS__GETSCHED); +	return current_has_perm(p, PROCESS__GETSCHED);  }  static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) @@ -3299,9 +3413,9 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim  	/* Control the ability to change the hard limit (whether  	   lowering or raising it), so that the hard limit can  	   later be used as a safe reset point for the soft limit -	   upon context transitions. See selinux_bprm_apply_creds. */ +	   upon context transitions.  See selinux_bprm_committing_creds. */  	if (old_rlim->rlim_max != new_rlim->rlim_max) -		return task_has_perm(current, current, PROCESS__SETRLIMIT); +		return current_has_perm(current, PROCESS__SETRLIMIT);  	return 0;  } @@ -3314,17 +3428,17 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s  	if (rc)  		return rc; -	return task_has_perm(current, p, PROCESS__SETSCHED); +	return current_has_perm(p, PROCESS__SETSCHED);  }  static int selinux_task_getscheduler(struct task_struct *p)  { -	return task_has_perm(current, p, PROCESS__GETSCHED); +	return current_has_perm(p, PROCESS__GETSCHED);  }  static int selinux_task_movememory(struct task_struct *p)  { -	return task_has_perm(current, p, PROCESS__SETSCHED); +	return current_has_perm(p, PROCESS__SETSCHED);  }  static int selinux_task_kill(struct task_struct *p, struct siginfo *info, @@ -3332,7 +3446,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,  {  	u32 perm;  	int rc; -	struct task_security_struct *tsec;  	rc = secondary_ops->task_kill(p, info, sig, secid);  	if (rc) @@ -3342,11 +3455,11 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,  		perm = PROCESS__SIGNULL; /* null signal; existence test */  	else  		perm = signal_to_av(sig); -	tsec = p->security;  	if (secid) -		rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL); +		rc = avc_has_perm(secid, task_sid(p), +				  SECCLASS_PROCESS, perm, NULL);  	else -		rc = task_has_perm(current, p, perm); +		rc = current_has_perm(p, perm);  	return rc;  } @@ -3354,13 +3467,12 @@ static int selinux_task_prctl(int option,  			      unsigned long arg2,  			      unsigned long arg3,  			      unsigned long arg4, -			      unsigned long arg5, -			      long *rc_p) +			      unsigned long arg5)  {  	/* The current prctl operations do not appear to require  	   any SELinux controls since they merely observe or modify  	   the state of the current process. */ -	return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p); +	return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);  }  static int selinux_task_wait(struct task_struct *p) @@ -3368,27 +3480,14 @@ static int selinux_task_wait(struct task_struct *p)  	return task_has_perm(p, current, PROCESS__SIGCHLD);  } -static void selinux_task_reparent_to_init(struct task_struct *p) -{ -	struct task_security_struct *tsec; - -	secondary_ops->task_reparent_to_init(p); - -	tsec = p->security; -	tsec->osid = tsec->sid; -	tsec->sid = SECINITSID_KERNEL; -	return; -} -  static void selinux_task_to_inode(struct task_struct *p,  				  struct inode *inode)  { -	struct task_security_struct *tsec = p->security;  	struct inode_security_struct *isec = inode->i_security; +	u32 sid = task_sid(p); -	isec->sid = tsec->sid; +	isec->sid = sid;  	isec->initialized = 1; -	return;  }  /* Returns error only if unable to parse addresses */ @@ -3627,19 +3726,19 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,  			   u32 perms)  {  	struct inode_security_struct *isec; -	struct task_security_struct *tsec;  	struct avc_audit_data ad; +	u32 sid;  	int err = 0; -	tsec = task->security;  	isec = SOCK_INODE(sock)->i_security;  	if (isec->sid == SECINITSID_KERNEL)  		goto out; +	sid = task_sid(task);  	AVC_AUDIT_DATA_INIT(&ad, NET);  	ad.u.net.sk = sock->sk; -	err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); +	err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);  out:  	return err; @@ -3648,18 +3747,20 @@ out:  static int selinux_socket_create(int family, int type,  				 int protocol, int kern)  { +	const struct cred *cred = current_cred(); +	const struct task_security_struct *tsec = cred->security; +	u32 sid, newsid; +	u16 secclass;  	int err = 0; -	struct task_security_struct *tsec; -	u32 newsid;  	if (kern)  		goto out; -	tsec = current->security; -	newsid = tsec->sockcreate_sid ? : tsec->sid; -	err = avc_has_perm(tsec->sid, newsid, -			   socket_type_to_security_class(family, type, -			   protocol), SOCKET__CREATE, NULL); +	sid = tsec->sid; +	newsid = tsec->sockcreate_sid ?: sid; + +	secclass = socket_type_to_security_class(family, type, protocol); +	err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL);  out:  	return err; @@ -3668,18 +3769,26 @@ out:  static int selinux_socket_post_create(struct socket *sock, int family,  				      int type, int protocol, int kern)  { -	int err = 0; +	const struct cred *cred = current_cred(); +	const struct task_security_struct *tsec = cred->security;  	struct inode_security_struct *isec; -	struct task_security_struct *tsec;  	struct sk_security_struct *sksec; -	u32 newsid; +	u32 sid, newsid; +	int err = 0; + +	sid = tsec->sid; +	newsid = tsec->sockcreate_sid;  	isec = SOCK_INODE(sock)->i_security; -	tsec = current->security; -	newsid = tsec->sockcreate_sid ? : tsec->sid; +	if (kern) +		isec->sid = SECINITSID_KERNEL; +	else if (newsid) +		isec->sid = newsid; +	else +		isec->sid = sid; +  	isec->sclass = socket_type_to_security_class(family, type, protocol); -	isec->sid = kern ? SECINITSID_KERNEL : newsid;  	isec->initialized = 1;  	if (sock->sk) { @@ -3714,7 +3823,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in  	if (family == PF_INET || family == PF_INET6) {  		char *addrp;  		struct inode_security_struct *isec; -		struct task_security_struct *tsec;  		struct avc_audit_data ad;  		struct sockaddr_in *addr4 = NULL;  		struct sockaddr_in6 *addr6 = NULL; @@ -3722,7 +3830,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in  		struct sock *sk = sock->sk;  		u32 sid, node_perm; -		tsec = current->security;  		isec = SOCK_INODE(sock)->i_security;  		if (family == PF_INET) { @@ -4387,7 +4494,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)  				  "SELinux:  unrecognized netlink message"  				  " type=%hu for sclass=%hu\n",  				  nlh->nlmsg_type, isec->sclass); -			if (!selinux_enforcing) +			if (!selinux_enforcing || security_get_allow_unknown())  				err = 0;  		} @@ -4628,7 +4735,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	 * as fast and as clean as possible. */  	if (selinux_compat_net || !selinux_policycap_netpeer)  		return selinux_ip_postroute_compat(skb, ifindex, family); - +#ifdef CONFIG_XFRM  	/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec  	 * packet transformation so allow the packet to pass without any checks  	 * since we'll have another chance to perform access control checks @@ -4637,7 +4744,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	 *       is NULL, in this case go ahead and apply access control. */  	if (skb->dst != NULL && skb->dst->xfrm != NULL)  		return NF_ACCEPT; - +#endif  	secmark_active = selinux_secmark_enabled();  	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();  	if (!secmark_active && !peerlbl_active) @@ -4763,15 +4870,16 @@ static int ipc_alloc_security(struct task_struct *task,  			      struct kern_ipc_perm *perm,  			      u16 sclass)  { -	struct task_security_struct *tsec = task->security;  	struct ipc_security_struct *isec; +	u32 sid;  	isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);  	if (!isec)  		return -ENOMEM; +	sid = task_sid(task);  	isec->sclass = sclass; -	isec->sid = tsec->sid; +	isec->sid = sid;  	perm->security = isec;  	return 0; @@ -4809,17 +4917,16 @@ static void msg_msg_free_security(struct msg_msg *msg)  static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,  			u32 perms)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid(); -	tsec = current->security;  	isec = ipc_perms->security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = ipc_perms->key; -	return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); +	return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);  }  static int selinux_msg_msg_alloc_security(struct msg_msg *msg) @@ -4835,22 +4942,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)  /* message queue security operations */  static int selinux_msg_queue_alloc_security(struct msg_queue *msq)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	int rc;  	rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);  	if (rc)  		return rc; -	tsec = current->security;  	isec = msq->q_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = msq->q_perm.key; -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, +	rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,  			  MSGQ__CREATE, &ad);  	if (rc) {  		ipc_free_security(&msq->q_perm); @@ -4866,17 +4972,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)  static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid(); -	tsec = current->security;  	isec = msq->q_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = msq->q_perm.key; -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, +	return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,  			    MSGQ__ASSOCIATE, &ad);  } @@ -4910,13 +5015,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)  static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct msg_security_struct *msec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	int rc; -	tsec = current->security;  	isec = msq->q_perm.security;  	msec = msg->security; @@ -4928,9 +5032,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,  		 * Compute new sid based on current process and  		 * message queue this message will be stored in  		 */ -		rc = security_transition_sid(tsec->sid, -					     isec->sid, -					     SECCLASS_MSG, +		rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,  					     &msec->sid);  		if (rc)  			return rc; @@ -4940,16 +5042,16 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,  	ad.u.ipc_id = msq->q_perm.key;  	/* Can this process write to the queue? */ -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, +	rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,  			  MSGQ__WRITE, &ad);  	if (!rc)  		/* Can this process send the message */ -		rc = avc_has_perm(tsec->sid, msec->sid, -				  SECCLASS_MSG, MSG__SEND, &ad); +		rc = avc_has_perm(sid, msec->sid, 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, &ad); +		rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ, +				  MSGQ__ENQUEUE, &ad);  	return rc;  } @@ -4958,23 +5060,22 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,  				    struct task_struct *target,  				    long type, int mode)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct msg_security_struct *msec;  	struct avc_audit_data ad; +	u32 sid = task_sid(target);  	int rc; -	tsec = target->security;  	isec = msq->q_perm.security;  	msec = msg->security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = msq->q_perm.key; -	rc = avc_has_perm(tsec->sid, isec->sid, +	rc = avc_has_perm(sid, isec->sid,  			  SECCLASS_MSGQ, MSGQ__READ, &ad);  	if (!rc) -		rc = avc_has_perm(tsec->sid, msec->sid, +		rc = avc_has_perm(sid, msec->sid,  				  SECCLASS_MSG, MSG__RECEIVE, &ad);  	return rc;  } @@ -4982,22 +5083,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,  /* Shared Memory security operations */  static int selinux_shm_alloc_security(struct shmid_kernel *shp)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	int rc;  	rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);  	if (rc)  		return rc; -	tsec = current->security;  	isec = shp->shm_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = shp->shm_perm.key; -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, +	rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,  			  SHM__CREATE, &ad);  	if (rc) {  		ipc_free_security(&shp->shm_perm); @@ -5013,17 +5113,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)  static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid(); -	tsec = current->security;  	isec = shp->shm_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = shp->shm_perm.key; -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, +	return avc_has_perm(sid, isec->sid, SECCLASS_SHM,  			    SHM__ASSOCIATE, &ad);  } @@ -5081,22 +5180,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,  /* Semaphore security operations */  static int selinux_sem_alloc_security(struct sem_array *sma)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid();  	int rc;  	rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);  	if (rc)  		return rc; -	tsec = current->security;  	isec = sma->sem_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = sma->sem_perm.key; -	rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, +	rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,  			  SEM__CREATE, &ad);  	if (rc) {  		ipc_free_security(&sma->sem_perm); @@ -5112,17 +5210,16 @@ static void selinux_sem_free_security(struct sem_array *sma)  static int selinux_sem_associate(struct sem_array *sma, int semflg)  { -	struct task_security_struct *tsec;  	struct ipc_security_struct *isec;  	struct avc_audit_data ad; +	u32 sid = current_sid(); -	tsec = current->security;  	isec = sma->sem_perm.security;  	AVC_AUDIT_DATA_INIT(&ad, IPC);  	ad.u.ipc_id = sma->sem_perm.key; -	return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, +	return avc_has_perm(sid, isec->sid, SECCLASS_SEM,  			    SEM__ASSOCIATE, &ad);  } @@ -5212,33 +5309,35 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)  static int selinux_getprocattr(struct task_struct *p,  			       char *name, char **value)  { -	struct task_security_struct *tsec; +	const struct task_security_struct *__tsec;  	u32 sid;  	int error;  	unsigned len;  	if (current != p) { -		error = task_has_perm(current, p, PROCESS__GETATTR); +		error = current_has_perm(p, PROCESS__GETATTR);  		if (error)  			return error;  	} -	tsec = p->security; +	rcu_read_lock(); +	__tsec = __task_cred(p)->security;  	if (!strcmp(name, "current")) -		sid = tsec->sid; +		sid = __tsec->sid;  	else if (!strcmp(name, "prev")) -		sid = tsec->osid; +		sid = __tsec->osid;  	else if (!strcmp(name, "exec")) -		sid = tsec->exec_sid; +		sid = __tsec->exec_sid;  	else if (!strcmp(name, "fscreate")) -		sid = tsec->create_sid; +		sid = __tsec->create_sid;  	else if (!strcmp(name, "keycreate")) -		sid = tsec->keycreate_sid; +		sid = __tsec->keycreate_sid;  	else if (!strcmp(name, "sockcreate")) -		sid = tsec->sockcreate_sid; +		sid = __tsec->sockcreate_sid;  	else -		return -EINVAL; +		goto invalid; +	rcu_read_unlock();  	if (!sid)  		return 0; @@ -5247,6 +5346,10 @@ static int selinux_getprocattr(struct task_struct *p,  	if (error)  		return error;  	return len; + +invalid: +	rcu_read_unlock(); +	return -EINVAL;  }  static int selinux_setprocattr(struct task_struct *p, @@ -5254,7 +5357,8 @@ static int selinux_setprocattr(struct task_struct *p,  {  	struct task_security_struct *tsec;  	struct task_struct *tracer; -	u32 sid = 0; +	struct cred *new; +	u32 sid = 0, ptsid;  	int error;  	char *str = value; @@ -5270,15 +5374,15 @@ static int selinux_setprocattr(struct task_struct *p,  	 * above restriction is ever removed.  	 */  	if (!strcmp(name, "exec")) -		error = task_has_perm(current, p, PROCESS__SETEXEC); +		error = current_has_perm(p, PROCESS__SETEXEC);  	else if (!strcmp(name, "fscreate")) -		error = task_has_perm(current, p, PROCESS__SETFSCREATE); +		error = current_has_perm(p, PROCESS__SETFSCREATE);  	else if (!strcmp(name, "keycreate")) -		error = task_has_perm(current, p, PROCESS__SETKEYCREATE); +		error = current_has_perm(p, PROCESS__SETKEYCREATE);  	else if (!strcmp(name, "sockcreate")) -		error = task_has_perm(current, p, PROCESS__SETSOCKCREATE); +		error = current_has_perm(p, PROCESS__SETSOCKCREATE);  	else if (!strcmp(name, "current")) -		error = task_has_perm(current, p, PROCESS__SETCURRENT); +		error = current_has_perm(p, PROCESS__SETCURRENT);  	else  		error = -EINVAL;  	if (error) @@ -5301,87 +5405,75 @@ static int selinux_setprocattr(struct task_struct *p,  			return error;  	} +	new = prepare_creds(); +	if (!new) +		return -ENOMEM; +  	/* Permission checking based on the specified context is  	   performed during the actual operation (execve,  	   open/mkdir/...), when we know the full context of the -	   operation.  See selinux_bprm_set_security for the execve +	   operation.  See selinux_bprm_set_creds for the execve  	   checks and may_create for the file creation checks. The  	   operation will then fail if the context is not permitted. */ -	tsec = p->security; -	if (!strcmp(name, "exec")) +	tsec = new->security; +	if (!strcmp(name, "exec")) {  		tsec->exec_sid = sid; -	else if (!strcmp(name, "fscreate")) +	} else if (!strcmp(name, "fscreate")) {  		tsec->create_sid = sid; -	else if (!strcmp(name, "keycreate")) { +	} else if (!strcmp(name, "keycreate")) {  		error = may_create_key(sid, p);  		if (error) -			return error; +			goto abort_change;  		tsec->keycreate_sid = sid; -	} else if (!strcmp(name, "sockcreate")) +	} else if (!strcmp(name, "sockcreate")) {  		tsec->sockcreate_sid = sid; -	else if (!strcmp(name, "current")) { -		struct av_decision avd; - +	} else if (!strcmp(name, "current")) { +		error = -EINVAL;  		if (sid == 0) -			return -EINVAL; -		/* -		 * SELinux allows to change context in the following case only. -		 *  - Single threaded processes. -		 *  - Multi threaded processes intend to change its context into -		 *    more restricted domain (defined by TYPEBOUNDS statement). -		 */ -		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); -					error = security_bounded_transition(tsec->sid, sid); -					if (!error) -						goto boundary_ok; - -					return error; -				} -			} while_each_thread(g, t); -			read_unlock(&tasklist_lock); +			goto abort_change; + +		/* Only allow single threaded processes to change context */ +		error = -EPERM; +		if (!is_single_threaded(p)) { +			error = security_bounded_transition(tsec->sid, sid); +			if (error) +				goto abort_change;  		} -boundary_ok:  		/* Check permissions for the transition. */  		error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,  				     PROCESS__DYNTRANSITION, NULL);  		if (error) -			return error; +			goto abort_change;  		/* Check for ptracing, and update the task SID if ok.  		   Otherwise, leave SID unchanged and fail. */ +		ptsid = 0;  		task_lock(p); -		rcu_read_lock();  		tracer = tracehook_tracer_task(p); -		if (tracer != NULL) { -			struct task_security_struct *ptsec = tracer->security; -			u32 ptsid = ptsec->sid; -			rcu_read_unlock(); -			error = avc_has_perm_noaudit(ptsid, sid, -						     SECCLASS_PROCESS, -						     PROCESS__PTRACE, 0, &avd); -			if (!error) -				tsec->sid = sid; -			task_unlock(p); -			avc_audit(ptsid, sid, SECCLASS_PROCESS, -				  PROCESS__PTRACE, &avd, error, NULL); +		if (tracer) +			ptsid = task_sid(tracer); +		task_unlock(p); + +		if (tracer) { +			error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, +					     PROCESS__PTRACE, NULL);  			if (error) -				return error; -		} else { -			rcu_read_unlock(); -			tsec->sid = sid; -			task_unlock(p); +				goto abort_change;  		} -	} else -		return -EINVAL; +		tsec->sid = sid; +	} else { +		error = -EINVAL; +		goto abort_change; +	} + +	commit_creds(new);  	return size; + +abort_change: +	abort_creds(new); +	return error;  }  static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) @@ -5401,22 +5493,23 @@ static void selinux_release_secctx(char *secdata, u32 seclen)  #ifdef CONFIG_KEYS -static int selinux_key_alloc(struct key *k, struct task_struct *tsk, +static int selinux_key_alloc(struct key *k, const struct cred *cred,  			     unsigned long flags)  { -	struct task_security_struct *tsec = tsk->security; +	const struct task_security_struct *tsec;  	struct key_security_struct *ksec;  	ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);  	if (!ksec)  		return -ENOMEM; +	tsec = cred->security;  	if (tsec->keycreate_sid)  		ksec->sid = tsec->keycreate_sid;  	else  		ksec->sid = tsec->sid; -	k->security = ksec; +	k->security = ksec;  	return 0;  } @@ -5429,17 +5522,12 @@ static void selinux_key_free(struct key *k)  }  static int selinux_key_permission(key_ref_t key_ref, -			    struct task_struct *ctx, -			    key_perm_t perm) +				  const struct cred *cred, +				  key_perm_t perm)  {  	struct key *key; -	struct task_security_struct *tsec;  	struct key_security_struct *ksec; - -	key = key_ref_to_ptr(key_ref); - -	tsec = ctx->security; -	ksec = key->security; +	u32 sid;  	/* if no specific permissions are requested, we skip the  	   permission check. No serious, additional covert channels @@ -5447,8 +5535,12 @@ static int selinux_key_permission(key_ref_t key_ref,  	if (perm == 0)  		return 0; -	return avc_has_perm(tsec->sid, ksec->sid, -			    SECCLASS_KEY, perm, NULL); +	sid = cred_sid(cred); + +	key = key_ref_to_ptr(key_ref); +	ksec = key->security; + +	return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);  }  static int selinux_key_getsecurity(struct key *key, char **_buffer) @@ -5473,8 +5565,7 @@ static struct security_operations selinux_ops = {  	.ptrace_may_access =		selinux_ptrace_may_access,  	.ptrace_traceme =		selinux_ptrace_traceme,  	.capget =			selinux_capget, -	.capset_check =			selinux_capset_check, -	.capset_set =			selinux_capset_set, +	.capset =			selinux_capset,  	.sysctl =			selinux_sysctl,  	.capable =			selinux_capable,  	.quotactl =			selinux_quotactl, @@ -5485,12 +5576,10 @@ static struct security_operations selinux_ops = {  	.netlink_send =			selinux_netlink_send,  	.netlink_recv =			selinux_netlink_recv, -	.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_set_creds =		selinux_bprm_set_creds,  	.bprm_check_security =		selinux_bprm_check_security, +	.bprm_committing_creds =	selinux_bprm_committing_creds, +	.bprm_committed_creds =		selinux_bprm_committed_creds,  	.bprm_secureexec =		selinux_bprm_secureexec,  	.sb_alloc_security =		selinux_sb_alloc_security, @@ -5549,10 +5638,13 @@ static struct security_operations selinux_ops = {  	.dentry_open =			selinux_dentry_open,  	.task_create =			selinux_task_create, -	.task_alloc_security =		selinux_task_alloc_security, -	.task_free_security =		selinux_task_free_security, +	.cred_free =			selinux_cred_free, +	.cred_prepare =			selinux_cred_prepare, +	.cred_commit =			selinux_cred_commit, +	.kernel_act_as =		selinux_kernel_act_as, +	.kernel_create_files_as =	selinux_kernel_create_files_as,  	.task_setuid =			selinux_task_setuid, -	.task_post_setuid =		selinux_task_post_setuid, +	.task_fix_setuid =		selinux_task_fix_setuid,  	.task_setgid =			selinux_task_setgid,  	.task_setpgid =			selinux_task_setpgid,  	.task_getpgid =			selinux_task_getpgid, @@ -5569,7 +5661,6 @@ static struct security_operations selinux_ops = {  	.task_kill =			selinux_task_kill,  	.task_wait =			selinux_task_wait,  	.task_prctl =			selinux_task_prctl, -	.task_reparent_to_init =	selinux_task_reparent_to_init,  	.task_to_inode =		selinux_task_to_inode,  	.ipc_permission =		selinux_ipc_permission, @@ -5665,8 +5756,6 @@ static struct security_operations selinux_ops = {  static __init int selinux_init(void)  { -	struct task_security_struct *tsec; -  	if (!security_module_enable(&selinux_ops)) {  		selinux_enabled = 0;  		return 0; @@ -5680,10 +5769,7 @@ static __init int selinux_init(void)  	printk(KERN_INFO "SELinux:  Initializing.\n");  	/* Set the security state for the initial task. */ -	if (task_alloc_security(current)) -		panic("SELinux:  Failed to initialize initial task.\n"); -	tsec = current->security; -	tsec->osid = tsec->sid = SECINITSID_KERNEL; +	cred_init_security();  	sel_inode_cache = kmem_cache_create("selinux_inode_security",  					    sizeof(struct inode_security_struct),  | 
