diff options
Diffstat (limited to 'security/selinux')
| -rw-r--r-- | security/selinux/avc.c | 16 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 392 | ||||
| -rw-r--r-- | security/selinux/include/avc.h | 22 | ||||
| -rw-r--r-- | security/selinux/include/classmap.h | 2 | ||||
| -rw-r--r-- | security/selinux/include/objsec.h | 9 | ||||
| -rw-r--r-- | security/selinux/include/security.h | 18 | ||||
| -rw-r--r-- | security/selinux/include/xfrm.h | 55 | ||||
| -rw-r--r-- | security/selinux/netlabel.c | 37 | ||||
| -rw-r--r-- | security/selinux/netnode.c | 2 | ||||
| -rw-r--r-- | security/selinux/nlmsgtab.c | 4 | ||||
| -rw-r--r-- | security/selinux/selinuxfs.c | 34 | ||||
| -rw-r--r-- | security/selinux/ss/constraint.h | 1 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.c | 20 | ||||
| -rw-r--r-- | security/selinux/ss/ebitmap.h | 10 | ||||
| -rw-r--r-- | security/selinux/ss/hashtab.c | 3 | ||||
| -rw-r--r-- | security/selinux/ss/mls.c | 24 | ||||
| -rw-r--r-- | security/selinux/ss/mls_types.h | 2 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.c | 121 | ||||
| -rw-r--r-- | security/selinux/ss/policydb.h | 11 | ||||
| -rw-r--r-- | security/selinux/ss/services.c | 100 | ||||
| -rw-r--r-- | security/selinux/xfrm.c | 485 | 
21 files changed, 843 insertions, 525 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index dad36a6ab45..a18f1fa6440 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -444,11 +444,15 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)  	avc_dump_query(ab, ad->selinux_audit_data->ssid,  			   ad->selinux_audit_data->tsid,  			   ad->selinux_audit_data->tclass); +	if (ad->selinux_audit_data->denied) { +		audit_log_format(ab, " permissive=%u", +				 ad->selinux_audit_data->result ? 0 : 1); +	}  }  /* This is the slow part of avc audit with big stack footprint */  noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, -		u32 requested, u32 audited, u32 denied, +		u32 requested, u32 audited, u32 denied, int result,  		struct common_audit_data *a,  		unsigned flags)  { @@ -477,6 +481,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,  	sad.tsid = tsid;  	sad.audited = audited;  	sad.denied = denied; +	sad.result = result;  	a->selinux_audit_data = &sad; @@ -746,7 +751,6 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,   * @tclass: target security class   * @requested: requested permissions, interpreted based on @tclass   * @auditdata: auxiliary audit data - * @flags: VFS walk flags   *   * Check the AVC to determine whether the @requested permissions are granted   * for the SID pair (@ssid, @tsid), interpreting the permissions @@ -756,17 +760,15 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,   * permissions are granted, -%EACCES if any permissions are denied, or   * another -errno upon other errors.   */ -int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass, -		       u32 requested, struct common_audit_data *auditdata, -		       unsigned flags) +int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, +		 u32 requested, struct common_audit_data *auditdata)  {  	struct av_decision avd;  	int rc, rc2;  	rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); -	rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, -			flags); +	rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);  	if (rc2)  		return rc2;  	return rc; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a5091ec06aa..83d06db34d0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -53,6 +53,7 @@  #include <net/ip.h>		/* for local_port_range[] */  #include <net/sock.h>  #include <net/tcp.h>		/* struct or_callable used in sock_rcv_skb */ +#include <net/inet_connection_sock.h>  #include <net/net_namespace.h>  #include <net/netlabel.h>  #include <linux/uaccess.h> @@ -81,7 +82,6 @@  #include <linux/syslog.h>  #include <linux/user_namespace.h>  #include <linux/export.h> -#include <linux/security.h>  #include <linux/msg.h>  #include <linux/shm.h> @@ -95,8 +95,6 @@  #include "audit.h"  #include "avc_ss.h" -#define NUM_SEL_MNT_OPTS 5 -  extern struct security_operations *security_ops;  /* SECMARK reference count */ @@ -108,7 +106,7 @@ int selinux_enforcing;  static int __init enforcing_setup(char *str)  {  	unsigned long enforcing; -	if (!strict_strtoul(str, 0, &enforcing)) +	if (!kstrtoul(str, 0, &enforcing))  		selinux_enforcing = enforcing ? 1 : 0;  	return 1;  } @@ -121,7 +119,7 @@ int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;  static int __init selinux_enabled_setup(char *str)  {  	unsigned long enabled; -	if (!strict_strtoul(str, 0, &enabled)) +	if (!kstrtoul(str, 0, &enabled))  		selinux_enabled = enabled ? 1 : 0;  	return 1;  } @@ -139,12 +137,28 @@ static struct kmem_cache *sel_inode_cache;   * This function checks the SECMARK reference counter to see if any SECMARK   * targets are currently configured, if the reference counter is greater than   * zero SECMARK is considered to be enabled.  Returns true (1) if SECMARK is - * enabled, false (0) if SECMARK is disabled. + * enabled, false (0) if SECMARK is disabled.  If the always_check_network + * policy capability is enabled, SECMARK is always considered enabled.   *   */  static int selinux_secmark_enabled(void)  { -	return (atomic_read(&selinux_secmark_refcount) > 0); +	return (selinux_policycap_alwaysnetwork || atomic_read(&selinux_secmark_refcount)); +} + +/** + * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled + * + * Description: + * This function checks if NetLabel or labeled IPSEC is enabled.  Returns true + * (1) if any are enabled or false (0) if neither are enabled.  If the + * always_check_network policy capability is enabled, peer labeling + * is always considered enabled. + * + */ +static int selinux_peerlbl_enabled(void) +{ +	return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled());  }  /* @@ -219,6 +233,14 @@ static int inode_alloc_security(struct inode *inode)  	return 0;  } +static void inode_free_rcu(struct rcu_head *head) +{ +	struct inode_security_struct *isec; + +	isec = container_of(head, struct inode_security_struct, rcu); +	kmem_cache_free(sel_inode_cache, isec); +} +  static void inode_free_security(struct inode *inode)  {  	struct inode_security_struct *isec = inode->i_security; @@ -229,8 +251,16 @@ static void inode_free_security(struct inode *inode)  		list_del_init(&isec->list);  	spin_unlock(&sbsec->isec_lock); -	inode->i_security = NULL; -	kmem_cache_free(sel_inode_cache, isec); +	/* +	 * The inode may still be referenced in a path walk and +	 * a call to selinux_inode_permission() can be made +	 * after inode_free_security() is called. Ideally, the VFS +	 * wouldn't do this, but fixing that is a much harder +	 * job. For now, simply free the i_security via RCU, and +	 * leave the current inode->i_security pointer intact. +	 * The inode will be freed after the RCU grace period too. +	 */ +	call_rcu(&isec->rcu, inode_free_rcu);  }  static int file_alloc_security(struct file *file) @@ -309,8 +339,11 @@ enum {  	Opt_defcontext = 3,  	Opt_rootcontext = 4,  	Opt_labelsupport = 5, +	Opt_nextmntopt = 6,  }; +#define NUM_SEL_MNT_OPTS	(Opt_nextmntopt - 1) +  static const match_table_t tokens = {  	{Opt_context, CONTEXT_STR "%s"},  	{Opt_fscontext, FSCONTEXT_STR "%s"}, @@ -355,6 +388,29 @@ static int may_context_mount_inode_relabel(u32 sid,  	return rc;  } +static int selinux_is_sblabel_mnt(struct super_block *sb) +{ +	struct superblock_security_struct *sbsec = sb->s_security; + +	if (sbsec->behavior == SECURITY_FS_USE_XATTR || +	    sbsec->behavior == SECURITY_FS_USE_TRANS || +	    sbsec->behavior == SECURITY_FS_USE_TASK) +		return 1; + +	/* Special handling for sysfs. Is genfs but also has setxattr handler*/ +	if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) +		return 1; + +	/* +	 * Special handling for rootfs. Is genfs but supports +	 * setting SELinux context on in-core inodes. +	 */ +	if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) +		return 1; + +	return 0; +} +  static int sb_finish_set_opts(struct super_block *sb)  {  	struct superblock_security_struct *sbsec = sb->s_security; @@ -388,8 +444,6 @@ static int sb_finish_set_opts(struct super_block *sb)  		}  	} -	sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP); -  	if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))  		printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",  		       sb->s_id, sb->s_type->name); @@ -398,15 +452,9 @@ static int sb_finish_set_opts(struct super_block *sb)  		       sb->s_id, sb->s_type->name,  		       labeling_behaviors[sbsec->behavior-1]); -	if (sbsec->behavior == SECURITY_FS_USE_GENFS || -	    sbsec->behavior == SECURITY_FS_USE_MNTPOINT || -	    sbsec->behavior == SECURITY_FS_USE_NONE || -	    sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) -		sbsec->flags &= ~SE_SBLABELSUPP; - -	/* Special handling for sysfs. Is genfs but also has setxattr handler*/ -	if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0) -		sbsec->flags |= SE_SBLABELSUPP; +	sbsec->flags |= SE_SBINITIALIZED; +	if (selinux_is_sblabel_mnt(sb)) +		sbsec->flags |= SBLABEL_MNT;  	/* Initialize the root inode. */  	rc = inode_doinit_with_dentry(root_inode, root); @@ -460,15 +508,18 @@ static int selinux_get_mnt_opts(const struct super_block *sb,  	if (!ss_initialized)  		return -EINVAL; +	/* make sure we always check enough bits to cover the mask */ +	BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS)); +  	tmp = sbsec->flags & SE_MNTMASK;  	/* count the number of mount options for this sb */ -	for (i = 0; i < 8; i++) { +	for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {  		if (tmp & 0x01)  			opts->num_mnt_opts++;  		tmp >>= 1;  	}  	/* Check if the Label support flag is set */ -	if (sbsec->flags & SE_SBLABELSUPP) +	if (sbsec->flags & SBLABEL_MNT)  		opts->num_mnt_opts++;  	opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC); @@ -515,9 +566,9 @@ static int selinux_get_mnt_opts(const struct super_block *sb,  		opts->mnt_opts[i] = context;  		opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;  	} -	if (sbsec->flags & SE_SBLABELSUPP) { +	if (sbsec->flags & SBLABEL_MNT) {  		opts->mnt_opts[i] = NULL; -		opts->mnt_opts_flags[i++] = SE_SBLABELSUPP; +		opts->mnt_opts_flags[i++] = SBLABEL_MNT;  	}  	BUG_ON(i != opts->num_mnt_opts); @@ -614,10 +665,10 @@ static int selinux_set_mnt_opts(struct super_block *sb,  	for (i = 0; i < num_opts; i++) {  		u32 sid; -		if (flags[i] == SE_SBLABELSUPP) +		if (flags[i] == SBLABEL_MNT)  			continue;  		rc = security_context_to_sid(mount_options[i], -					     strlen(mount_options[i]), &sid); +					     strlen(mount_options[i]), &sid, GFP_KERNEL);  		if (rc) {  			printk(KERN_WARNING "SELinux: security_context_to_sid"  			       "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -685,9 +736,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,  		 * Determine the labeling behavior to use for this  		 * filesystem type.  		 */ -		rc = security_fs_use((sbsec->flags & SE_SBPROC) ? -					"proc" : sb->s_type->name, -					&sbsec->behavior, &sbsec->sid); +		rc = security_fs_use(sb);  		if (rc) {  			printk(KERN_WARNING  				"%s: security_fs_use(%s) returned %d\n", @@ -1037,7 +1086,7 @@ static void selinux_write_opts(struct seq_file *m,  		case DEFCONTEXT_MNT:  			prefix = DEFCONTEXT_STR;  			break; -		case SE_SBLABELSUPP: +		case SBLABEL_MNT:  			seq_putc(m, ',');  			seq_puts(m, LABELSUPP_STR);  			continue; @@ -1369,15 +1418,33 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent  		isec->sid = sbsec->sid;  		if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { -			if (opt_dentry) { -				isec->sclass = inode_mode_to_security_class(inode->i_mode); -				rc = selinux_proc_get_sid(opt_dentry, -							  isec->sclass, -							  &sid); -				if (rc) -					goto out_unlock; -				isec->sid = sid; -			} +			/* We must have a dentry to determine the label on +			 * procfs inodes */ +			if (opt_dentry) +				/* Called from d_instantiate or +				 * d_splice_alias. */ +				dentry = dget(opt_dentry); +			else +				/* Called from selinux_complete_init, try to +				 * find a dentry. */ +				dentry = d_find_alias(inode); +			/* +			 * This can be hit on boot when a file is accessed +			 * before the policy is loaded.  When we load policy we +			 * may find inodes that have no dentry on the +			 * sbsec->isec_head list.  No reason to complain as +			 * these will get fixed up the next time we go through +			 * inode_doinit() with a dentry, before these inodes +			 * could be used again by userspace. +			 */ +			if (!dentry) +				goto out_unlock; +			isec->sclass = inode_mode_to_security_class(inode->i_mode); +			rc = selinux_proc_get_sid(dentry, isec->sclass, &sid); +			dput(dentry); +			if (rc) +				goto out_unlock; +			isec->sid = sid;  		}  		break;  	} @@ -1502,7 +1569,7 @@ static int cred_has_capability(const struct cred *cred,  	rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);  	if (audit == SECURITY_CAP_AUDIT) { -		int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); +		int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);  		if (rc2)  			return rc2;  	} @@ -1525,8 +1592,7 @@ static int task_has_system(struct task_struct *tsk,  static int inode_has_perm(const struct cred *cred,  			  struct inode *inode,  			  u32 perms, -			  struct common_audit_data *adp, -			  unsigned flags) +			  struct common_audit_data *adp)  {  	struct inode_security_struct *isec;  	u32 sid; @@ -1539,7 +1605,7 @@ static int inode_has_perm(const struct cred *cred,  	sid = cred_sid(cred);  	isec = inode->i_security; -	return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); +	return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);  }  /* Same as inode_has_perm, but pass explicit audit data containing @@ -1554,7 +1620,7 @@ static inline int dentry_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_DENTRY;  	ad.u.dentry = dentry; -	return inode_has_perm(cred, inode, av, &ad, 0); +	return inode_has_perm(cred, inode, av, &ad);  }  /* Same as inode_has_perm, but pass explicit audit data containing @@ -1569,7 +1635,7 @@ static inline int path_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_PATH;  	ad.u.path = *path; -	return inode_has_perm(cred, inode, av, &ad, 0); +	return inode_has_perm(cred, inode, av, &ad);  }  /* Same as path_has_perm, but uses the inode from the file struct. */ @@ -1581,7 +1647,7 @@ static inline int file_path_has_perm(const struct cred *cred,  	ad.type = LSM_AUDIT_DATA_PATH;  	ad.u.path = file->f_path; -	return inode_has_perm(cred, file_inode(file), av, &ad, 0); +	return inode_has_perm(cred, file_inode(file), av, &ad);  }  /* Check whether a task can use an open file descriptor to @@ -1617,7 +1683,7 @@ static int file_has_perm(const struct cred *cred,  	/* av is zero if only checking access to the descriptor. */  	rc = 0;  	if (av) -		rc = inode_has_perm(cred, inode, av, &ad, 0); +		rc = inode_has_perm(cred, inode, av, &ad);  out:  	return rc; @@ -1650,7 +1716,7 @@ static int may_create(struct inode *dir,  	if (rc)  		return rc; -	if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { +	if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {  		rc = security_transition_sid(sid, dsec->sid, tclass,  					     &dentry->d_name, &newsid);  		if (rc) @@ -2057,11 +2123,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)  		new_tsec->exec_sid = 0;  		/* -		 * Minimize confusion: if no_new_privs and a transition is -		 * explicitly requested, then fail the exec. +		 * Minimize confusion: if no_new_privs or nosuid and a +		 * transition is explicitly requested, then fail the exec.  		 */  		if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)  			return -EPERM; +		if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) +			return -EACCES;  	} else {  		/* Check for a default transition on this program. */  		rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2438,10 +2506,11 @@ static int selinux_sb_remount(struct super_block *sb, void *data)  		u32 sid;  		size_t len; -		if (flags[i] == SE_SBLABELSUPP) +		if (flags[i] == SBLABEL_MNT)  			continue;  		len = strlen(mount_options[i]); -		rc = security_context_to_sid(mount_options[i], len, &sid); +		rc = security_context_to_sid(mount_options[i], len, &sid, +					     GFP_KERNEL);  		if (rc) {  			printk(KERN_WARNING "SELinux: security_context_to_sid"  			       "(%s) failed for (dev %s, type %s) errno=%d\n", @@ -2607,7 +2676,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,  	if ((sbsec->flags & SE_SBINITIALIZED) &&  	    (sbsec->behavior == SECURITY_FS_USE_MNTPOINT))  		newsid = sbsec->mntpoint_sid; -	else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) { +	else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {  		rc = security_transition_sid(sid, dsec->sid,  					     inode_mode_to_security_class(inode->i_mode),  					     qstr, &newsid); @@ -2629,7 +2698,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,  		isec->initialized = 1;  	} -	if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) +	if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))  		return -EOPNOTSUPP;  	if (name) @@ -2703,6 +2772,7 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na  static noinline int audit_inode_permission(struct inode *inode,  					   u32 perms, u32 audited, u32 denied, +					   int result,  					   unsigned flags)  {  	struct common_audit_data ad; @@ -2713,7 +2783,7 @@ static noinline int audit_inode_permission(struct inode *inode,  	ad.u.inode = inode;  	rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, -			    audited, denied, &ad, flags); +			    audited, denied, result, &ad, flags);  	if (rc)  		return rc;  	return 0; @@ -2755,7 +2825,7 @@ static int selinux_inode_permission(struct inode *inode, int mask)  	if (likely(!audited))  		return rc; -	rc2 = audit_inode_permission(inode, perms, audited, denied, flags); +	rc2 = audit_inode_permission(inode, perms, audited, denied, rc, flags);  	if (rc2)  		return rc2;  	return rc; @@ -2831,7 +2901,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  		return selinux_inode_setotherxattr(dentry, name);  	sbsec = inode->i_sb->s_security; -	if (!(sbsec->flags & SE_SBLABELSUPP)) +	if (!(sbsec->flags & SBLABEL_MNT))  		return -EOPNOTSUPP;  	if (!inode_owner_or_capable(inode)) @@ -2845,7 +2915,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,  	if (rc)  		return rc; -	rc = security_context_to_sid(value, size, &newsid); +	rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);  	if (rc == -EINVAL) {  		if (!capable(CAP_MAC_ADMIN)) {  			struct audit_buffer *ab; @@ -3002,7 +3072,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,  	if (!value || !size)  		return -EACCES; -	rc = security_context_to_sid((void *)value, size, &newsid); +	rc = security_context_to_sid((void *)value, size, &newsid, GFP_KERNEL);  	if (rc)  		return rc; @@ -3156,24 +3226,20 @@ error:  static int selinux_mmap_addr(unsigned long addr)  { -	int rc = 0; -	u32 sid = current_sid(); +	int rc; + +	/* do DAC check on address space usage */ +	rc = cap_mmap_addr(addr); +	if (rc) +		return rc; -	/* -	 * notice that we are intentionally putting the SELinux check before -	 * the secondary cap_file_mmap check.  This is such a likely attempt -	 * at bad behaviour/exploit that we always want to get the AVC, even -	 * if DAC would have also denied the operation. -	 */  	if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { +		u32 sid = current_sid();  		rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,  				  MEMPROTECT__MMAP_ZERO, NULL); -		if (rc) -			return rc;  	} -	/* do DAC check on address space usage */ -	return cap_mmap_addr(addr); +	return rc;  }  static int selinux_mmap_file(struct file *file, unsigned long reqprot, @@ -3254,6 +3320,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,  	case F_GETLK:  	case F_SETLK:  	case F_SETLKW: +	case F_OFD_GETLK: +	case F_OFD_SETLK: +	case F_OFD_SETLKW:  #if BITS_PER_LONG == 32  	case F_GETLK64:  	case F_SETLK64: @@ -3792,8 +3861,12 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)  	u32 nlbl_sid;  	u32 nlbl_type; -	selinux_skb_xfrm_sid(skb, &xfrm_sid); -	selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); +	err = selinux_xfrm_skb_sid(skb, &xfrm_sid); +	if (unlikely(err)) +		return -EACCES; +	err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); +	if (unlikely(err)) +		return -EACCES;  	err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);  	if (unlikely(err)) { @@ -3806,6 +3879,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)  	return 0;  } +/** + * selinux_conn_sid - Determine the child socket label for a connection + * @sk_sid: the parent socket's SID + * @skb_sid: the packet's SID + * @conn_sid: the resulting connection SID + * + * If @skb_sid is valid then the user:role:type information from @sk_sid is + * combined with the MLS information from @skb_sid in order to create + * @conn_sid.  If @skb_sid is not valid then then @conn_sid is simply a copy + * of @sk_sid.  Returns zero on success, negative values on failure. + * + */ +static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) +{ +	int err = 0; + +	if (skb_sid != SECSID_NULL) +		err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); +	else +		*conn_sid = sk_sid; + +	return err; +} +  /* socket security operations */  static int socket_sockcreate_sid(const struct task_security_struct *tsec, @@ -3929,7 +4026,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in  		if (snum) {  			int low, high; -			inet_get_local_port_range(&low, &high); +			inet_get_local_port_range(sock_net(sk), &low, &high);  			if (snum < max(PROT_SOCK, low) || snum > high) {  				err = sel_netport_sid(sk->sk_protocol, @@ -4247,7 +4344,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  		return selinux_sock_rcv_skb_compat(sk, skb, family);  	secmark_active = selinux_secmark_enabled(); -	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); +	peerlbl_active = selinux_peerlbl_enabled();  	if (!secmark_active && !peerlbl_active)  		return 0; @@ -4273,8 +4370,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  		}  		err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,  				   PEER__RECV, &ad); -		if (err) +		if (err) {  			selinux_netlbl_err(skb, err, 0); +			return err; +		}  	}  	if (secmark_active) { @@ -4411,27 +4510,18 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,  {  	struct sk_security_struct *sksec = sk->sk_security;  	int err; -	u16 family = sk->sk_family; -	u32 newsid; +	u16 family = req->rsk_ops->family; +	u32 connsid;  	u32 peersid; -	/* handle mapped IPv4 packets arriving via IPv6 sockets */ -	if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) -		family = PF_INET; -  	err = selinux_skb_peerlbl_sid(skb, family, &peersid);  	if (err)  		return err; -	if (peersid == SECSID_NULL) { -		req->secid = sksec->sid; -		req->peer_secid = SECSID_NULL; -	} else { -		err = security_sid_mls_copy(sksec->sid, peersid, &newsid); -		if (err) -			return err; -		req->secid = newsid; -		req->peer_secid = peersid; -	} +	err = selinux_conn_sid(sksec->sid, peersid, &connsid); +	if (err) +		return err; +	req->secid = connsid; +	req->peer_secid = peersid;  	return selinux_netlbl_inet_conn_request(req, family);  } @@ -4629,7 +4719,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,  	secmark_active = selinux_secmark_enabled();  	netlbl_active = netlbl_enabled(); -	peerlbl_active = netlbl_active || selinux_xfrm_enabled(); +	peerlbl_active = selinux_peerlbl_enabled();  	if (!secmark_active && !peerlbl_active)  		return NF_ACCEPT; @@ -4668,7 +4758,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_forward(unsigned int hooknum, +static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,  					 struct sk_buff *skb,  					 const struct net_device *in,  					 const struct net_device *out, @@ -4678,7 +4768,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum,  }  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_forward(unsigned int hooknum, +static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,  					 struct sk_buff *skb,  					 const struct net_device *in,  					 const struct net_device *out, @@ -4691,6 +4781,7 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,  static unsigned int selinux_ip_output(struct sk_buff *skb,  				      u16 family)  { +	struct sock *sk;  	u32 sid;  	if (!netlbl_enabled()) @@ -4699,8 +4790,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,  	/* we do this in the LOCAL_OUT path and not the POST_ROUTING path  	 * because we want to make sure we apply the necessary labeling  	 * before IPsec is applied so we can leverage AH protection */ -	if (skb->sk) { -		struct sk_security_struct *sksec = skb->sk->sk_security; +	sk = skb->sk; +	if (sk) { +		struct sk_security_struct *sksec; + +		if (sk->sk_state == TCP_LISTEN) +			/* if the socket is the listening state then this +			 * packet is a SYN-ACK packet which means it needs to +			 * be labeled based on the connection/request_sock and +			 * not the parent socket.  unfortunately, we can't +			 * lookup the request_sock yet as it isn't queued on +			 * the parent socket until after the SYN-ACK is sent. +			 * the "solution" is to simply pass the packet as-is +			 * as any IP option based labeling should be copied +			 * from the initial connection request (in the IP +			 * layer).  it is far from ideal, but until we get a +			 * security label in the packet itself this is the +			 * best we can do. */ +			return NF_ACCEPT; + +		/* standard practice, label using the parent socket */ +		sksec = sk->sk_security;  		sid = sksec->sid;  	} else  		sid = SECINITSID_KERNEL; @@ -4710,7 +4820,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_output(unsigned int hooknum, +static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,  					struct sk_buff *skb,  					const struct net_device *in,  					const struct net_device *out, @@ -4770,27 +4880,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	 * as fast and as clean as possible. */  	if (!selinux_policycap_netpeer)  		return selinux_ip_postroute_compat(skb, ifindex, family); + +	secmark_active = selinux_secmark_enabled(); +	peerlbl_active = selinux_peerlbl_enabled(); +	if (!secmark_active && !peerlbl_active) +		return NF_ACCEPT; + +	sk = skb->sk; +  #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  	 * when the packet is on it's final way out.  	 * NOTE: there appear to be some IPv6 multicast cases where skb->dst -	 *       is NULL, in this case go ahead and apply access control. */ -	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) +	 *       is NULL, in this case go ahead and apply access control. +	 * NOTE: if this is a local socket (skb->sk != NULL) that is in the +	 *       TCP listening state we cannot wait until the XFRM processing +	 *       is done as we will miss out on the SA label if we do; +	 *       unfortunately, this means more work, but it is only once per +	 *       connection. */ +	if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && +	    !(sk != NULL && sk->sk_state == TCP_LISTEN))  		return NF_ACCEPT;  #endif -	secmark_active = selinux_secmark_enabled(); -	peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); -	if (!secmark_active && !peerlbl_active) -		return NF_ACCEPT; -	/* if the packet is being forwarded then get the peer label from the -	 * packet itself; otherwise check to see if it is from a local -	 * application or the kernel, if from an application get the peer label -	 * from the sending socket, otherwise use the kernel's sid */ -	sk = skb->sk;  	if (sk == NULL) { +		/* Without an associated socket the packet is either coming +		 * from the kernel or it is being forwarded; check the packet +		 * to determine which and if the packet is being forwarded +		 * query the packet directly to determine the security label. */  		if (skb->skb_iif) {  			secmark_perm = PACKET__FORWARD_OUT;  			if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) @@ -4799,7 +4918,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  			secmark_perm = PACKET__SEND;  			peer_sid = SECINITSID_KERNEL;  		} +	} else if (sk->sk_state == TCP_LISTEN) { +		/* Locally generated packet but the associated socket is in the +		 * listening state which means this is a SYN-ACK packet.  In +		 * this particular case the correct security label is assigned +		 * to the connection/request_sock but unfortunately we can't +		 * query the request_sock as it isn't queued on the parent +		 * socket until after the SYN-ACK packet is sent; the only +		 * viable choice is to regenerate the label like we do in +		 * selinux_inet_conn_request().  See also selinux_ip_output() +		 * for similar problems. */ +		u32 skb_sid; +		struct sk_security_struct *sksec = sk->sk_security; +		if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) +			return NF_DROP; +		/* At this point, if the returned skb peerlbl is SECSID_NULL +		 * and the packet has been through at least one XFRM +		 * transformation then we must be dealing with the "final" +		 * form of labeled IPsec packet; since we've already applied +		 * all of our access controls on this packet we can safely +		 * pass the packet. */ +		if (skb_sid == SECSID_NULL) { +			switch (family) { +			case PF_INET: +				if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) +					return NF_ACCEPT; +				break; +			case PF_INET6: +				if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) +					return NF_ACCEPT; +			default: +				return NF_DROP_ERR(-ECONNREFUSED); +			} +		} +		if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) +			return NF_DROP; +		secmark_perm = PACKET__SEND;  	} else { +		/* Locally generated packet, fetch the security label from the +		 * associated socket. */  		struct sk_security_struct *sksec = sk->sk_security;  		peer_sid = sksec->sid;  		secmark_perm = PACKET__SEND; @@ -4837,7 +4994,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,  	return NF_ACCEPT;  } -static unsigned int selinux_ipv4_postroute(unsigned int hooknum, +static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,  					   struct sk_buff *skb,  					   const struct net_device *in,  					   const struct net_device *out, @@ -4847,7 +5004,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum,  }  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_postroute(unsigned int hooknum, +static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,  					   struct sk_buff *skb,  					   const struct net_device *in,  					   const struct net_device *out, @@ -5393,7 +5550,7 @@ static int selinux_setprocattr(struct task_struct *p,  			str[size-1] = 0;  			size--;  		} -		error = security_context_to_sid(value, size, &sid); +		error = security_context_to_sid(value, size, &sid, GFP_KERNEL);  		if (error == -EINVAL && !strcmp(name, "fscreate")) {  			if (!capable(CAP_MAC_ADMIN)) {  				struct audit_buffer *ab; @@ -5463,11 +5620,11 @@ 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; -		task_lock(p); +		rcu_read_lock();  		tracer = ptrace_parent(p);  		if (tracer)  			ptsid = task_sid(tracer); -		task_unlock(p); +		rcu_read_unlock();  		if (tracer) {  			error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, @@ -5502,7 +5659,7 @@ static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)  static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)  { -	return security_context_to_sid(secdata, seclen, secid); +	return security_context_to_sid(secdata, seclen, secid, GFP_KERNEL);  }  static void selinux_release_secctx(char *secdata, u32 seclen) @@ -5568,7 +5725,7 @@ static void selinux_key_free(struct key *k)  static int selinux_key_permission(key_ref_t key_ref,  				  const struct cred *cred, -				  key_perm_t perm) +				  unsigned perm)  {  	struct key *key;  	struct key_security_struct *ksec; @@ -5785,7 +5942,8 @@ static struct security_operations selinux_ops = {  	.xfrm_policy_clone_security =	selinux_xfrm_policy_clone,  	.xfrm_policy_free_security =	selinux_xfrm_policy_free,  	.xfrm_policy_delete_security =	selinux_xfrm_policy_delete, -	.xfrm_state_alloc_security =	selinux_xfrm_state_alloc, +	.xfrm_state_alloc =		selinux_xfrm_state_alloc, +	.xfrm_state_alloc_acquire =	selinux_xfrm_state_alloc_acquire,  	.xfrm_state_free_security =	selinux_xfrm_state_free,  	.xfrm_state_delete_security =	selinux_xfrm_state_delete,  	.xfrm_policy_lookup =		selinux_xfrm_policy_lookup, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 92d0ab561db..ddf8eec03f2 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -102,7 +102,7 @@ static inline u32 avc_audit_required(u32 requested,  }  int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, -		   u32 requested, u32 audited, u32 denied, +		   u32 requested, u32 audited, u32 denied, int result,  		   struct common_audit_data *a,  		   unsigned flags); @@ -130,15 +130,15 @@ static inline int avc_audit(u32 ssid, u32 tsid,  			    u16 tclass, u32 requested,  			    struct av_decision *avd,  			    int result, -			    struct common_audit_data *a, unsigned flags) +			    struct common_audit_data *a)  {  	u32 audited, denied;  	audited = avc_audit_required(requested, avd, result, 0, &denied);  	if (likely(!audited))  		return 0;  	return slow_avc_audit(ssid, tsid, tclass, -			      requested, audited, denied, -			      a, flags); +			      requested, audited, denied, result, +			      a, 0);  }  #define AVC_STRICT 1 /* Ignore permissive mode. */ @@ -147,17 +147,9 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,  			 unsigned flags,  			 struct av_decision *avd); -int avc_has_perm_flags(u32 ssid, u32 tsid, -		       u16 tclass, u32 requested, -		       struct common_audit_data *auditdata, -		       unsigned); - -static inline int avc_has_perm(u32 ssid, u32 tsid, -			       u16 tclass, u32 requested, -			       struct common_audit_data *auditdata) -{ -	return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0); -} +int avc_has_perm(u32 ssid, u32 tsid, +		 u16 tclass, u32 requested, +		 struct common_audit_data *auditdata);  u32 avc_policy_seqno(void); diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 14d04e63b1f..be491a74c1e 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = {  	{ "peer", { "recv", NULL } },  	{ "capability2",  	  { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", -	    NULL } }, +	    "audit_read", NULL } },  	{ "kernel_service", { "use_as_override", "create_files_as", NULL } },  	{ "tun_socket",  	  { COMMON_SOCK_PERMS, "attach_queue", NULL } }, diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index aa47bcabb5f..078e553f52f 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -38,7 +38,10 @@ struct task_security_struct {  struct inode_security_struct {  	struct inode *inode;	/* back pointer to inode object */ -	struct list_head list;	/* list of inode_security_struct */ +	union { +		struct list_head list;	/* list of inode_security_struct */ +		struct rcu_head rcu;	/* for freeing the inode_security_struct */ +	};  	u32 task_sid;		/* SID of creating task */  	u32 sid;		/* SID of this object */  	u16 sclass;		/* security class of this object */ @@ -58,8 +61,8 @@ struct superblock_security_struct {  	u32 sid;			/* SID of file system superblock */  	u32 def_sid;			/* default SID for labeling */  	u32 mntpoint_sid;		/* SECURITY_FS_USE_MNTPOINT context for files */ -	unsigned int behavior;		/* labeling behavior */ -	unsigned char flags;		/* which mount options were specified */ +	unsigned short behavior;	/* labeling behavior */ +	unsigned short flags;		/* which mount options were specified */  	struct mutex lock;  	struct list_head isec_head;  	spinlock_t isec_lock; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 8fd8e18ea34..ce7852cf526 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -33,26 +33,28 @@  #define POLICYDB_VERSION_ROLETRANS	26  #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS	27  #define POLICYDB_VERSION_DEFAULT_TYPE	28 +#define POLICYDB_VERSION_CONSTRAINT_NAMES	29  /* Range of policy versions we understand*/  #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE  #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX  #define POLICYDB_VERSION_MAX	CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE  #else -#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_DEFAULT_TYPE +#define POLICYDB_VERSION_MAX	POLICYDB_VERSION_CONSTRAINT_NAMES  #endif  /* Mask for just the mount related flags */  #define SE_MNTMASK	0x0f  /* Super block security struct flags for mount options */ +/* BE CAREFUL, these need to be the low order bits for selinux_get_mnt_opts */  #define CONTEXT_MNT	0x01  #define FSCONTEXT_MNT	0x02  #define ROOTCONTEXT_MNT	0x04  #define DEFCONTEXT_MNT	0x08 +#define SBLABEL_MNT	0x10  /* Non-mount related flags */ -#define SE_SBINITIALIZED	0x10 -#define SE_SBPROC		0x20 -#define SE_SBLABELSUPP	0x40 +#define SE_SBINITIALIZED	0x0100 +#define SE_SBPROC		0x0200  #define CONTEXT_STR	"context="  #define FSCONTEXT_STR	"fscontext=" @@ -68,12 +70,15 @@ extern int selinux_enabled;  enum {  	POLICYDB_CAPABILITY_NETPEER,  	POLICYDB_CAPABILITY_OPENPERM, +	POLICYDB_CAPABILITY_REDHAT1, +	POLICYDB_CAPABILITY_ALWAYSNETWORK,  	__POLICYDB_CAPABILITY_MAX  };  #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)  extern int selinux_policycap_netpeer;  extern int selinux_policycap_openperm; +extern int selinux_policycap_alwaysnetwork;  /*   * type_datum properties @@ -129,7 +134,7 @@ int security_sid_to_context(u32 sid, char **scontext,  int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);  int security_context_to_sid(const char *scontext, u32 scontext_len, -	u32 *out_sid); +			    u32 *out_sid, gfp_t gfp);  int security_context_to_sid_default(const char *scontext, u32 scontext_len,  				    u32 *out_sid, u32 def_sid, gfp_t gfp_flags); @@ -172,8 +177,7 @@ int security_get_allow_unknown(void);  #define SECURITY_FS_USE_NATIVE		7 /* use native label support */  #define SECURITY_FS_USE_MAX		7 /* Highest SECURITY_FS_USE_XXX */ -int security_fs_use(const char *fstype, unsigned int *behavior, -	u32 *sid); +int security_fs_use(struct super_block *sb);  int security_genfs_sid(const char *fstype, char *name, u16 sclass,  	u32 *sid); diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 6713f04e30b..1450f85b946 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -10,29 +10,22 @@  #include <net/flow.h>  int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, -			      struct xfrm_user_sec_ctx *sec_ctx); +			      struct xfrm_user_sec_ctx *uctx, +			      gfp_t gfp);  int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,  			      struct xfrm_sec_ctx **new_ctxp);  void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx);  int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx);  int selinux_xfrm_state_alloc(struct xfrm_state *x, -	struct xfrm_user_sec_ctx *sec_ctx, u32 secid); +			     struct xfrm_user_sec_ctx *uctx); +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, +				     struct xfrm_sec_ctx *polsec, u32 secid);  void selinux_xfrm_state_free(struct xfrm_state *x);  int selinux_xfrm_state_delete(struct xfrm_state *x);  int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);  int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, -			struct xfrm_policy *xp, const struct flowi *fl); - -/* - * Extract the security blob from the sock (it's actually on the socket) - */ -static inline struct inode_security_struct *get_sock_isec(struct sock *sk) -{ -	if (!sk->sk_socket) -		return NULL; - -	return SOCK_INODE(sk->sk_socket)->i_security; -} +				      struct xfrm_policy *xp, +				      const struct flowi *fl);  #ifdef CONFIG_SECURITY_NETWORK_XFRM  extern atomic_t selinux_xfrm_refcount; @@ -42,20 +35,22 @@ static inline int selinux_xfrm_enabled(void)  	return (atomic_read(&selinux_xfrm_refcount) > 0);  } -int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, -			struct common_audit_data *ad); -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad, u8 proto); +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +			      struct common_audit_data *ad); +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +				struct common_audit_data *ad, u8 proto);  int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid);  static inline void selinux_xfrm_notify_policyload(void)  {  	struct net *net; -	atomic_inc(&flow_cache_genid);  	rtnl_lock(); -	for_each_net(net) +	for_each_net(net) { +		atomic_inc(&net->xfrm.flow_cache_genid);  		rt_genid_bump_all(net); +	}  	rtnl_unlock();  }  #else @@ -64,19 +59,21 @@ static inline int selinux_xfrm_enabled(void)  	return 0;  } -static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad) +static inline int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +					    struct common_audit_data *ad)  {  	return 0;  } -static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -			struct common_audit_data *ad, u8 proto) +static inline int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +					      struct common_audit_data *ad, +					      u8 proto)  {  	return 0;  } -static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, +					      int ckall)  {  	*sid = SECSID_NULL;  	return 0; @@ -85,12 +82,12 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int  static inline void selinux_xfrm_notify_policyload(void)  {  } -#endif -static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid) +static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid)  { -	int err = selinux_xfrm_decode_session(skb, sid, 0); -	BUG_ON(err); +	*sid = SECSID_NULL; +	return 0;  } +#endif  #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index da4b8b23328..0364120d1ec 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -101,6 +101,32 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)  }  /** + * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr + * @sk: the socket + * @sid: the SID + * + * Query the socket's cached secattr and if the SID matches the cached value + * return the cache, otherwise return NULL. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr( +							const struct sock *sk, +							u32 sid) +{ +	struct sk_security_struct *sksec = sk->sk_security; +	struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr; + +	if (secattr == NULL) +		return NULL; + +	if ((secattr->flags & NETLBL_SECATTR_SECID) && +	    (secattr->attr.secid == sid)) +		return secattr; + +	return NULL; +} + +/**   * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache   *   * Description: @@ -224,7 +250,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,  		struct sk_security_struct *sksec = sk->sk_security;  		if (sksec->nlbl_state != NLBL_REQSKB)  			return 0; -		secattr = sksec->nlbl_secattr; +		secattr = selinux_netlbl_sock_getattr(sk, sid);  	}  	if (secattr == NULL) {  		secattr = &secattr_storage; @@ -410,6 +436,9 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,  	     sksec->nlbl_state == NLBL_CONNLABELED)) {  		netlbl_secattr_init(&secattr);  		lock_sock(sk); +		/* call the netlabel function directly as we want to see the +		 * on-the-wire label that is assigned via the socket's options +		 * and not the cached netlabel/lsm attributes */  		rc = netlbl_sock_getattr(sk, &secattr);  		release_sock(sk);  		if (rc == 0) @@ -442,8 +471,7 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)  	    sksec->nlbl_state != NLBL_CONNLABELED)  		return 0; -	local_bh_disable(); -	bh_lock_sock_nested(sk); +	lock_sock(sk);  	/* connected sockets are allowed to disconnect when the address family  	 * is set to AF_UNSPEC, if that is what is happening we want to reset @@ -464,7 +492,6 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)  		sksec->nlbl_state = NLBL_CONNLABELED;  socket_connect_return: -	bh_unlock_sock(sk); -	local_bh_enable(); +	release_sock(sk);  	return rc;  } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index c5454c0477c..03a72c32afd 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -166,6 +166,7 @@ static void sel_netnode_insert(struct sel_netnode *node)  		break;  	default:  		BUG(); +		return;  	}  	/* we need to impose a limit on the growth of the hash table so check @@ -225,6 +226,7 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)  		break;  	default:  		BUG(); +		ret = -EINVAL;  	}  	if (ret != 0)  		goto out; diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 855e464e92e..2df7b900e25 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -17,6 +17,7 @@  #include <linux/inet_diag.h>  #include <linux/xfrm.h>  #include <linux/audit.h> +#include <linux/sock_diag.h>  #include "flask.h"  #include "av_permissions.h" @@ -78,6 +79,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =  {  	{ TCPDIAG_GETSOCK,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ },  	{ DCCPDIAG_GETSOCK,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, +	{ SOCK_DIAG_BY_FAMILY,	NETLINK_TCPDIAG_SOCKET__NLMSG_READ },  };  static struct nlmsg_perm nlmsg_xfrm_perms[] = @@ -116,6 +118,8 @@ static struct nlmsg_perm nlmsg_audit_perms[] =  	{ AUDIT_MAKE_EQUIV,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },  	{ AUDIT_TTY_GET,	NETLINK_AUDIT_SOCKET__NLMSG_READ     },  	{ AUDIT_TTY_SET,	NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT	}, +	{ AUDIT_GET_FEATURE,	NETLINK_AUDIT_SOCKET__NLMSG_READ     }, +	{ AUDIT_SET_FEATURE,	NETLINK_AUDIT_SOCKET__NLMSG_WRITE    },  }; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index ff427733c29..c71737f6d1c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -44,7 +44,9 @@  /* Policy capability filenames */  static char *policycap_names[] = {  	"network_peer_controls", -	"open_perms" +	"open_perms", +	"redhat1", +	"always_check_network"  };  unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; @@ -52,7 +54,7 @@ unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;  static int __init checkreqprot_setup(char *str)  {  	unsigned long checkreqprot; -	if (!strict_strtoul(str, 0, &checkreqprot)) +	if (!kstrtoul(str, 0, &checkreqprot))  		selinux_checkreqprot = checkreqprot ? 1 : 0;  	return 1;  } @@ -574,7 +576,7 @@ static ssize_t sel_write_context(struct file *file, char *buf, size_t size)  	if (length)  		goto out; -	length = security_context_to_sid(buf, size, &sid); +	length = security_context_to_sid(buf, size, &sid, GFP_KERNEL);  	if (length)  		goto out; @@ -729,11 +731,13 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -815,11 +819,13 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)  		objname = namebuf;  	} -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -876,11 +882,13 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; @@ -932,7 +940,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s", con, user) != 2)  		goto out; -	length = security_context_to_sid(con, strlen(con) + 1, &sid); +	length = security_context_to_sid(con, strlen(con) + 1, &sid, GFP_KERNEL);  	if (length)  		goto out; @@ -992,11 +1000,13 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)  	if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)  		goto out; -	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid); +	length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, +					 GFP_KERNEL);  	if (length)  		goto out; -	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid); +	length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, +					 GFP_KERNEL);  	if (length)  		goto out; diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 149dda731fd..96fd947c494 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr {  	u32 op;			/* operator */  	struct ebitmap names;	/* names */ +	struct type_set *type_names;  	struct constraint_expr *next;   /* next expression */  }; diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 30f119b1d1e..820313a04d4 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -213,7 +213,12 @@ netlbl_import_failure:  }  #endif /* CONFIG_NETLABEL */ -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) +/* + * Check to see if all the bits set in e2 are also set in e1. Optionally, + * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed + * last_e2bit. + */ +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit)  {  	struct ebitmap_node *n1, *n2;  	int i; @@ -223,14 +228,25 @@ int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)  	n1 = e1->node;  	n2 = e2->node; +  	while (n1 && n2 && (n1->startbit <= n2->startbit)) {  		if (n1->startbit < n2->startbit) {  			n1 = n1->next;  			continue;  		} -		for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { +		for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; ) +			i--;	/* Skip trailing NULL map entries */ +		if (last_e2bit && (i >= 0)) { +			u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE + +					 __fls(n2->maps[i]); +			if (lastsetbit > last_e2bit) +				return 0; +		} + +		while (i >= 0) {  			if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])  				return 0; +			i--;  		}  		n1 = n1->next; diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 922f8afa89d..712c8a7b8e8 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -16,7 +16,13 @@  #include <net/netlabel.h> -#define EBITMAP_UNIT_NUMS	((32 - sizeof(void *) - sizeof(u32))	\ +#ifdef CONFIG_64BIT +#define	EBITMAP_NODE_SIZE	64 +#else +#define	EBITMAP_NODE_SIZE	32 +#endif + +#define EBITMAP_UNIT_NUMS	((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\  					/ sizeof(unsigned long))  #define EBITMAP_UNIT_SIZE	BITS_PER_LONG  #define EBITMAP_SIZE		(EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE) @@ -117,7 +123,7 @@ static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,  int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);  int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); -int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); +int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2, u32 last_e2bit);  int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);  int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);  void ebitmap_destroy(struct ebitmap *e); diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 933e735bb18..2cc49614984 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -6,6 +6,7 @@  #include <linux/kernel.h>  #include <linux/slab.h>  #include <linux/errno.h> +#include <linux/sched.h>  #include "hashtab.h"  struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key), @@ -40,6 +41,8 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum)  	u32 hvalue;  	struct hashtab_node *prev, *cur, *newnode; +	cond_resched(); +  	if (!h || h->nel == HASHTAB_MAX_NODES)  		return -EINVAL; diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 40de8d3f208..d307b37ddc2 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -160,8 +160,6 @@ void mls_sid_to_context(struct context *context,  int mls_level_isvalid(struct policydb *p, struct mls_level *l)  {  	struct level_datum *levdatum; -	struct ebitmap_node *node; -	int i;  	if (!l->sens || l->sens > p->p_levels.nprim)  		return 0; @@ -170,19 +168,13 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)  	if (!levdatum)  		return 0; -	ebitmap_for_each_positive_bit(&l->cat, node, i) { -		if (i > p->p_cats.nprim) -			return 0; -		if (!ebitmap_get_bit(&levdatum->level->cat, i)) { -			/* -			 * Category may not be associated with -			 * sensitivity. -			 */ -			return 0; -		} -	} - -	return 1; +	/* +	 * Return 1 iff all the bits set in l->cat are also be set in +	 * levdatum->level->cat and no bit in l->cat is larger than +	 * p->p_cats.nprim. +	 */ +	return ebitmap_contains(&levdatum->level->cat, &l->cat, +				p->p_cats.nprim);  }  int mls_range_isvalid(struct policydb *p, struct mls_range *r) @@ -500,6 +492,8 @@ int mls_convert_context(struct policydb *oldp,  			rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);  			if (rc)  				return rc; + +			cond_resched();  		}  		ebitmap_destroy(&c->range.level[l].cat);  		c->range.level[l].cat = bitmap; diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h index 03bed52a805..e9364877413 100644 --- a/security/selinux/ss/mls_types.h +++ b/security/selinux/ss/mls_types.h @@ -35,7 +35,7 @@ static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)  static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)  {  	return ((l1->sens >= l2->sens) && -		ebitmap_contains(&l1->cat, &l2->cat)); +		ebitmap_contains(&l1->cat, &l2->cat, 0));  }  #define mls_level_incomp(l1, l2) \ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c8adde3aff8..9c5cdc2caae 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -143,6 +143,11 @@ static struct policydb_compat_info policydb_compat[] = {  		.sym_num	= SYM_NUM,  		.ocon_num	= OCON_NUM,  	}, +	{ +		.version	= POLICYDB_VERSION_CONSTRAINT_NAMES, +		.sym_num	= SYM_NUM, +		.ocon_num	= OCON_NUM, +	},  };  static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -613,6 +618,19 @@ static int common_destroy(void *key, void *datum, void *p)  	return 0;  } +static void constraint_expr_destroy(struct constraint_expr *expr) +{ +	if (expr) { +		ebitmap_destroy(&expr->names); +		if (expr->type_names) { +			ebitmap_destroy(&expr->type_names->types); +			ebitmap_destroy(&expr->type_names->negset); +			kfree(expr->type_names); +		} +		kfree(expr); +	} +} +  static int cls_destroy(void *key, void *datum, void *p)  {  	struct class_datum *cladatum; @@ -628,10 +646,9 @@ static int cls_destroy(void *key, void *datum, void *p)  		while (constraint) {  			e = constraint->expr;  			while (e) { -				ebitmap_destroy(&e->names);  				etmp = e;  				e = e->next; -				kfree(etmp); +				constraint_expr_destroy(etmp);  			}  			ctemp = constraint;  			constraint = constraint->next; @@ -642,16 +659,14 @@ static int cls_destroy(void *key, void *datum, void *p)  		while (constraint) {  			e = constraint->expr;  			while (e) { -				ebitmap_destroy(&e->names);  				etmp = e;  				e = e->next; -				kfree(etmp); +				constraint_expr_destroy(etmp);  			}  			ctemp = constraint;  			constraint = constraint->next;  			kfree(ctemp);  		} -  		kfree(cladatum->comkey);  	}  	kfree(datum); @@ -1156,8 +1171,34 @@ bad:  	return rc;  } -static int read_cons_helper(struct constraint_node **nodep, int ncons, -			    int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ +	ebitmap_init(&t->types); +	ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ +	__le32 buf[1]; +	int rc; + +	if (ebitmap_read(&t->types, fp)) +		return -EINVAL; +	if (ebitmap_read(&t->negset, fp)) +		return -EINVAL; + +	rc = next_entry(buf, fp, sizeof(u32)); +	if (rc < 0) +		return -EINVAL; +	t->flags = le32_to_cpu(buf[0]); + +	return 0; +} + + +static int read_cons_helper(struct policydb *p, +				struct constraint_node **nodep, +				int ncons, int allowxtarget, void *fp)  {  	struct constraint_node *c, *lc;  	struct constraint_expr *e, *le; @@ -1225,6 +1266,18 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,  				rc = ebitmap_read(&e->names, fp);  				if (rc)  					return rc; +				if (p->policyvers >= +					POLICYDB_VERSION_CONSTRAINT_NAMES) { +						e->type_names = kzalloc(sizeof +						(*e->type_names), +						GFP_KERNEL); +					if (!e->type_names) +						return -ENOMEM; +					type_set_init(e->type_names); +					rc = type_set_read(e->type_names, fp); +					if (rc) +						return rc; +				}  				break;  			default:  				return -EINVAL; @@ -1301,7 +1354,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)  			goto bad;  	} -	rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); +	rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);  	if (rc)  		goto bad; @@ -1311,7 +1364,8 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)  		if (rc)  			goto bad;  		ncons = le32_to_cpu(buf[0]); -		rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); +		rc = read_cons_helper(p, &cladatum->validatetrans, +				ncons, 1, fp);  		if (rc)  			goto bad;  	} @@ -1941,7 +1995,19 @@ static int filename_trans_read(struct policydb *p, void *fp)  		if (rc)  			goto out; -		hashtab_insert(p->filename_trans, ft, otype); +		rc = hashtab_insert(p->filename_trans, ft, otype); +		if (rc) { +			/* +			 * Do not return -EEXIST to the caller, or the system +			 * will not boot. +			 */ +			if (rc != -EEXIST) +				goto out; +			/* But free memory to avoid memory leak. */ +			kfree(ft); +			kfree(name); +			kfree(otype); +		}  	}  	hash_eval(p->filename_trans, "filenametr");  	return 0; @@ -2753,6 +2819,24 @@ static int common_write(void *vkey, void *datum, void *ptr)  	return 0;  } +static int type_set_write(struct type_set *t, void *fp) +{ +	int rc; +	__le32 buf[1]; + +	if (ebitmap_write(&t->types, fp)) +		return -EINVAL; +	if (ebitmap_write(&t->negset, fp)) +		return -EINVAL; + +	buf[0] = cpu_to_le32(t->flags); +	rc = put_entry(buf, sizeof(u32), 1, fp); +	if (rc) +		return -EINVAL; + +	return 0; +} +  static int write_cons_helper(struct policydb *p, struct constraint_node *node,  			     void *fp)  { @@ -2784,6 +2868,12 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node,  				rc = ebitmap_write(&e->names, fp);  				if (rc)  					return rc; +				if (p->policyvers >= +					POLICYDB_VERSION_CONSTRAINT_NAMES) { +					rc = type_set_write(e->type_names, fp); +					if (rc) +						return rc; +				}  				break;  			default:  				break; @@ -3203,9 +3293,8 @@ static int range_write_helper(void *key, void *data, void *ptr)  static int range_write(struct policydb *p, void *fp)  { -	size_t nel;  	__le32 buf[1]; -	int rc; +	int rc, nel;  	struct policy_data pd;  	pd.p = p; @@ -3249,10 +3338,10 @@ static int filename_write_helper(void *key, void *data, void *ptr)  	if (rc)  		return rc; -	buf[0] = ft->stype; -	buf[1] = ft->ttype; -	buf[2] = ft->tclass; -	buf[3] = otype->otype; +	buf[0] = cpu_to_le32(ft->stype); +	buf[1] = cpu_to_le32(ft->ttype); +	buf[2] = cpu_to_le32(ft->tclass); +	buf[3] = cpu_to_le32(otype->otype);  	rc = put_entry(buf, sizeof(u32), 4, fp);  	if (rc) diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index da637471d4c..725d5945a97 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -154,6 +154,17 @@ struct cond_bool_datum {  struct cond_node;  /* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { +	struct ebitmap types; +	struct ebitmap negset; +	u32 flags; +}; + +/*   * The configuration data includes security contexts for   * initial SIDs, unlabeled file systems, TCP and UDP port numbers,   * network interfaces, and nodes.  This structure stores the diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b4feecc3fe0..4bca49414a4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -72,6 +72,7 @@  int selinux_policycap_netpeer;  int selinux_policycap_openperm; +int selinux_policycap_alwaysnetwork;  static DEFINE_RWLOCK(policy_rwlock); @@ -1231,6 +1232,10 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,  	struct context context;  	int rc = 0; +	/* An empty security context is never valid. */ +	if (!scontext_len) +		return -EINVAL; +  	if (!ss_initialized) {  		int i; @@ -1284,16 +1289,18 @@ out:   * @scontext: security context   * @scontext_len: length in bytes   * @sid: security identifier, SID + * @gfp: context for the allocation   *   * Obtains a SID associated with the security context that   * has the string representation specified by @scontext.   * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient   * memory is available, or 0 on success.   */ -int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid) +int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, +			    gfp_t gfp)  {  	return security_context_to_sid_core(scontext, scontext_len, -					    sid, SECSID_NULL, GFP_KERNEL, 0); +					    sid, SECSID_NULL, gfp, 0);  }  /** @@ -1812,6 +1819,8 @@ static void security_load_policycaps(void)  						  POLICYDB_CAPABILITY_NETPEER);  	selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,  						  POLICYDB_CAPABILITY_OPENPERM); +	selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps, +						  POLICYDB_CAPABILITY_ALWAYSNETWORK);  }  static int security_preserve_bools(struct policydb *p); @@ -1828,7 +1837,7 @@ static int security_preserve_bools(struct policydb *p);   */  int security_load_policy(void *data, size_t len)  { -	struct policydb oldpolicydb, newpolicydb; +	struct policydb *oldpolicydb, *newpolicydb;  	struct sidtab oldsidtab, newsidtab;  	struct selinux_mapping *oldmap, *map = NULL;  	struct convert_context_args args; @@ -1837,12 +1846,19 @@ int security_load_policy(void *data, size_t len)  	int rc = 0;  	struct policy_file file = { data, len }, *fp = &file; +	oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); +	if (!oldpolicydb) { +		rc = -ENOMEM; +		goto out; +	} +	newpolicydb = oldpolicydb + 1; +  	if (!ss_initialized) {  		avtab_cache_init();  		rc = policydb_read(&policydb, fp);  		if (rc) {  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		policydb.len = len; @@ -1852,14 +1868,14 @@ int security_load_policy(void *data, size_t len)  		if (rc) {  			policydb_destroy(&policydb);  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		rc = policydb_load_isids(&policydb, &sidtab);  		if (rc) {  			policydb_destroy(&policydb);  			avtab_cache_destroy(); -			return rc; +			goto out;  		}  		security_load_policycaps(); @@ -1871,36 +1887,36 @@ int security_load_policy(void *data, size_t len)  		selinux_status_update_policyload(seqno);  		selinux_netlbl_cache_invalidate();  		selinux_xfrm_notify_policyload(); -		return 0; +		goto out;  	}  #if 0  	sidtab_hash_eval(&sidtab, "sids");  #endif -	rc = policydb_read(&newpolicydb, fp); +	rc = policydb_read(newpolicydb, fp);  	if (rc) -		return rc; +		goto out; -	newpolicydb.len = len; +	newpolicydb->len = len;  	/* If switching between different policy types, log MLS status */ -	if (policydb.mls_enabled && !newpolicydb.mls_enabled) +	if (policydb.mls_enabled && !newpolicydb->mls_enabled)  		printk(KERN_INFO "SELinux: Disabling MLS support...\n"); -	else if (!policydb.mls_enabled && newpolicydb.mls_enabled) +	else if (!policydb.mls_enabled && newpolicydb->mls_enabled)  		printk(KERN_INFO "SELinux: Enabling MLS support...\n"); -	rc = policydb_load_isids(&newpolicydb, &newsidtab); +	rc = policydb_load_isids(newpolicydb, &newsidtab);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to load the initial SIDs\n"); -		policydb_destroy(&newpolicydb); -		return rc; +		policydb_destroy(newpolicydb); +		goto out;  	} -	rc = selinux_set_mapping(&newpolicydb, secclass_map, &map, &map_size); +	rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size);  	if (rc)  		goto err; -	rc = security_preserve_bools(&newpolicydb); +	rc = security_preserve_bools(newpolicydb);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to preserve booleans\n");  		goto err; @@ -1918,7 +1934,7 @@ int security_load_policy(void *data, size_t len)  	 * in the new SID table.  	 */  	args.oldp = &policydb; -	args.newp = &newpolicydb; +	args.newp = newpolicydb;  	rc = sidtab_map(&newsidtab, convert_context, &args);  	if (rc) {  		printk(KERN_ERR "SELinux:  unable to convert the internal" @@ -1928,12 +1944,12 @@ int security_load_policy(void *data, size_t len)  	}  	/* Save the old policydb and SID table to free later. */ -	memcpy(&oldpolicydb, &policydb, sizeof policydb); +	memcpy(oldpolicydb, &policydb, sizeof(policydb));  	sidtab_set(&oldsidtab, &sidtab);  	/* Install the new policydb and SID table. */  	write_lock_irq(&policy_rwlock); -	memcpy(&policydb, &newpolicydb, sizeof policydb); +	memcpy(&policydb, newpolicydb, sizeof(policydb));  	sidtab_set(&sidtab, &newsidtab);  	security_load_policycaps();  	oldmap = current_mapping; @@ -1943,7 +1959,7 @@ int security_load_policy(void *data, size_t len)  	write_unlock_irq(&policy_rwlock);  	/* Free the old policydb and SID table. */ -	policydb_destroy(&oldpolicydb); +	policydb_destroy(oldpolicydb);  	sidtab_destroy(&oldsidtab);  	kfree(oldmap); @@ -1953,14 +1969,17 @@ int security_load_policy(void *data, size_t len)  	selinux_netlbl_cache_invalidate();  	selinux_xfrm_notify_policyload(); -	return 0; +	rc = 0; +	goto out;  err:  	kfree(map);  	sidtab_destroy(&newsidtab); -	policydb_destroy(&newpolicydb); -	return rc; +	policydb_destroy(newpolicydb); +out: +	kfree(oldpolicydb); +	return rc;  }  size_t security_policydb_len(void) @@ -2323,17 +2342,14 @@ out:  /**   * security_fs_use - Determine how to handle labeling for a filesystem. - * @fstype: filesystem type - * @behavior: labeling behavior - * @sid: SID for filesystem (superblock) + * @sb: superblock in question   */ -int security_fs_use( -	const char *fstype, -	unsigned int *behavior, -	u32 *sid) +int security_fs_use(struct super_block *sb)  {  	int rc = 0;  	struct ocontext *c; +	struct superblock_security_struct *sbsec = sb->s_security; +	const char *fstype = sb->s_type->name;  	read_lock(&policy_rwlock); @@ -2345,21 +2361,21 @@ int security_fs_use(  	}  	if (c) { -		*behavior = c->v.behavior; +		sbsec->behavior = c->v.behavior;  		if (!c->sid[0]) {  			rc = sidtab_context_to_sid(&sidtab, &c->context[0],  						   &c->sid[0]);  			if (rc)  				goto out;  		} -		*sid = c->sid[0]; +		sbsec->sid = c->sid[0];  	} else { -		rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); +		rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, &sbsec->sid);  		if (rc) { -			*behavior = SECURITY_FS_USE_NONE; +			sbsec->behavior = SECURITY_FS_USE_NONE;  			rc = 0;  		} else { -			*behavior = SECURITY_FS_USE_GENFS; +			sbsec->behavior = SECURITY_FS_USE_GENFS;  		}  	} @@ -2938,25 +2954,21 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,  	struct selinux_audit_rule *rule = vrule;  	int match = 0; -	if (!rule) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: missing rule\n"); +	if (unlikely(!rule)) { +		WARN_ONCE(1, "selinux_audit_rule_match: missing rule\n");  		return -ENOENT;  	}  	read_lock(&policy_rwlock);  	if (rule->au_seqno < latest_granting) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: stale rule\n");  		match = -ESTALE;  		goto out;  	}  	ctxt = sidtab_search(&sidtab, sid); -	if (!ctxt) { -		audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, -			  "selinux_audit_rule_match: unrecognized SID %d\n", +	if (unlikely(!ctxt)) { +		WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",  			  sid);  		match = -ENOENT;  		goto out; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index d0308188621..98b042630a9 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -56,7 +56,7 @@  atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);  /* - * Returns true if an LSM/SELinux context + * Returns true if the context is an LSM/SELinux context.   */  static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  { @@ -66,7 +66,7 @@ static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)  }  /* - * Returns true if the xfrm contains a security blob for SELinux + * Returns true if the xfrm contains a security blob for SELinux.   */  static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  { @@ -74,48 +74,112 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)  }  /* - * LSM hook implementation that authorizes that a flow can use - * a xfrm policy rule. + * Allocates a xfrm_sec_state and populates it using the supplied security + * xfrm_user_sec_ctx context.   */ -int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, +				   struct xfrm_user_sec_ctx *uctx, +				   gfp_t gfp)  {  	int rc; -	u32 sel_sid; +	const struct task_security_struct *tsec = current_security(); +	struct xfrm_sec_ctx *ctx = NULL; +	u32 str_len; -	/* Context sid is either set to label or ANY_ASSOC */ -	if (ctx) { -		if (!selinux_authorizable_ctx(ctx)) -			return -EINVAL; - -		sel_sid = ctx->ctx_sid; -	} else -		/* -		 * All flows should be treated as polmatch'ing an -		 * otherwise applicable "non-labeled" policy. This -		 * would prevent inadvertent "leaks". -		 */ -		return 0; +	if (ctxp == NULL || uctx == NULL || +	    uctx->ctx_doi != XFRM_SC_DOI_LSM || +	    uctx->ctx_alg != XFRM_SC_ALG_SELINUX) +		return -EINVAL; + +	str_len = uctx->ctx_len; +	if (str_len >= PAGE_SIZE) +		return -ENOMEM; + +	ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp); +	if (!ctx) +		return -ENOMEM; -	rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__POLMATCH, -			  NULL); +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, &uctx[1], str_len); +	ctx->ctx_str[str_len] = '\0'; +	rc = security_context_to_sid(ctx->ctx_str, str_len, &ctx->ctx_sid, gfp); +	if (rc) +		goto err; -	if (rc == -EACCES) -		return -ESRCH; +	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); +	if (rc) +		goto err; +	*ctxp = ctx; +	atomic_inc(&selinux_xfrm_refcount); +	return 0; + +err: +	kfree(ctx);  	return rc;  }  /* + * Free the xfrm_sec_ctx structure. + */ +static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) +{ +	if (!ctx) +		return; + +	atomic_dec(&selinux_xfrm_refcount); +	kfree(ctx); +} + +/* + * Authorize the deletion of a labeled SA or policy rule. + */ +static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) +{ +	const struct task_security_struct *tsec = current_security(); + +	if (!ctx) +		return 0; + +	return avc_has_perm(tsec->sid, ctx->ctx_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, +			    NULL); +} + +/* + * LSM hook implementation that authorizes that a flow can use a xfrm policy + * rule. + */ +int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir) +{ +	int rc; + +	/* All flows should be treated as polmatch'ing an otherwise applicable +	 * "non-labeled" policy. This would prevent inadvertent "leaks". */ +	if (!ctx) +		return 0; + +	/* Context sid is either set to label or ANY_ASSOC */ +	if (!selinux_authorizable_ctx(ctx)) +		return -EINVAL; + +	rc = avc_has_perm(fl_secid, ctx->ctx_sid, +			  SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, NULL); +	return (rc == -EACCES ? -ESRCH : rc); +} + +/*   * LSM hook implementation that authorizes that a state matches   * the given policy, flow combo.   */ - -int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, -			const struct flowi *fl) +int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, +				      struct xfrm_policy *xp, +				      const struct flowi *fl)  {  	u32 state_sid; -	int rc;  	if (!xp->security)  		if (x->security) @@ -138,187 +202,112 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *  	if (fl->flowi_secid != state_sid)  		return 0; -	rc = avc_has_perm(fl->flowi_secid, state_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, -			  NULL)? 0:1; - -	/* -	 * We don't need a separate SA Vs. policy polmatch check -	 * since the SA is now of the same label as the flow and -	 * a flow Vs. policy polmatch check had already happened -	 * in selinux_xfrm_policy_lookup() above. -	 */ - -	return rc; +	/* We don't need a separate SA Vs. policy polmatch check since the SA +	 * is now of the same label as the flow and a flow Vs. policy polmatch +	 * check had already happened in selinux_xfrm_policy_lookup() above. */ +	return (avc_has_perm(fl->flowi_secid, state_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, +			    NULL) ? 0 : 1);  } -/* - * LSM hook implementation that checks and/or returns the xfrm sid for the - * incoming packet. - */ - -int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb)  { -	struct sec_path *sp; +	struct dst_entry *dst = skb_dst(skb); +	struct xfrm_state *x; -	*sid = SECSID_NULL; +	if (dst == NULL) +		return SECSID_NULL; +	x = dst->xfrm; +	if (x == NULL || !selinux_authorizable_xfrm(x)) +		return SECSID_NULL; -	if (skb == NULL) -		return 0; +	return x->security->ctx_sid; +} + +static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, +					u32 *sid, int ckall) +{ +	u32 sid_session = SECSID_NULL; +	struct sec_path *sp = skb->sp; -	sp = skb->sp;  	if (sp) { -		int i, sid_set = 0; +		int i; -		for (i = sp->len-1; i >= 0; i--) { +		for (i = sp->len - 1; i >= 0; i--) {  			struct xfrm_state *x = sp->xvec[i];  			if (selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				if (!sid_set) { -					*sid = ctx->ctx_sid; -					sid_set = 1; - +				if (sid_session == SECSID_NULL) { +					sid_session = ctx->ctx_sid;  					if (!ckall) -						break; -				} else if (*sid != ctx->ctx_sid) +						goto out; +				} else if (sid_session != ctx->ctx_sid) { +					*sid = SECSID_NULL;  					return -EINVAL; +				}  			}  		}  	} +out: +	*sid = sid_session;  	return 0;  }  /* - * Security blob allocation for xfrm_policy and xfrm_state - * CTX does not have a meaningful value on input + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet.   */ -static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, -	struct xfrm_user_sec_ctx *uctx, u32 sid) +int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)  { -	int rc = 0; -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = NULL; -	char *ctx_str = NULL; -	u32 str_len; - -	BUG_ON(uctx && sid); - -	if (!uctx) -		goto not_from_user; - -	if (uctx->ctx_alg != XFRM_SC_ALG_SELINUX) -		return -EINVAL; - -	str_len = uctx->ctx_len; -	if (str_len >= PAGE_SIZE) -		return -ENOMEM; - -	*ctxp = ctx = kmalloc(sizeof(*ctx) + -			      str_len + 1, -			      GFP_KERNEL); - -	if (!ctx) -		return -ENOMEM; - -	ctx->ctx_doi = uctx->ctx_doi; -	ctx->ctx_len = str_len; -	ctx->ctx_alg = uctx->ctx_alg; - -	memcpy(ctx->ctx_str, -	       uctx+1, -	       str_len); -	ctx->ctx_str[str_len] = 0; -	rc = security_context_to_sid(ctx->ctx_str, -				     str_len, -				     &ctx->ctx_sid); - -	if (rc) -		goto out; - -	/* -	 * Does the subject have permission to set security context? -	 */ -	rc = avc_has_perm(tsec->sid, ctx->ctx_sid, -			  SECCLASS_ASSOCIATION, -			  ASSOCIATION__SETCONTEXT, NULL); -	if (rc) -		goto out; - -	return rc; - -not_from_user: -	rc = security_sid_to_context(sid, &ctx_str, &str_len); -	if (rc) -		goto out; - -	*ctxp = ctx = kmalloc(sizeof(*ctx) + -			      str_len, -			      GFP_ATOMIC); - -	if (!ctx) { -		rc = -ENOMEM; -		goto out; +	if (skb == NULL) { +		*sid = SECSID_NULL; +		return 0;  	} +	return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); +} -	ctx->ctx_doi = XFRM_SC_DOI_LSM; -	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; -	ctx->ctx_sid = sid; -	ctx->ctx_len = str_len; -	memcpy(ctx->ctx_str, -	       ctx_str, -	       str_len); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) +{ +	int rc; -	goto out2; +	rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); +	if (rc == 0 && *sid == SECSID_NULL) +		*sid = selinux_xfrm_skb_sid_egress(skb); -out: -	*ctxp = NULL; -	kfree(ctx); -out2: -	kfree(ctx_str);  	return rc;  }  /* - * LSM hook implementation that allocs and transfers uctx spec to - * xfrm_policy. + * LSM hook implementation that allocs and transfers uctx spec to xfrm_policy.   */  int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, -			      struct xfrm_user_sec_ctx *uctx) +			      struct xfrm_user_sec_ctx *uctx, +			      gfp_t gfp)  { -	int err; - -	BUG_ON(!uctx); - -	err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); - -	return err; +	return selinux_xfrm_alloc_user(ctxp, uctx, gfp);  } -  /* - * LSM hook implementation that copies security data structure from old to - * new for policy cloning. + * LSM hook implementation that copies security data structure from old to new + * for policy cloning.   */  int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,  			      struct xfrm_sec_ctx **new_ctxp)  {  	struct xfrm_sec_ctx *new_ctx; -	if (old_ctx) { -		new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len, -				  GFP_ATOMIC); -		if (!new_ctx) -			return -ENOMEM; +	if (!old_ctx) +		return 0; + +	new_ctx = kmemdup(old_ctx, sizeof(*old_ctx) + old_ctx->ctx_len, +			  GFP_ATOMIC); +	if (!new_ctx) +		return -ENOMEM; +	atomic_inc(&selinux_xfrm_refcount); +	*new_ctxp = new_ctx; -		memcpy(new_ctx, old_ctx, sizeof(*new_ctx)); -		memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len); -		atomic_inc(&selinux_xfrm_refcount); -		*new_ctxp = new_ctx; -	}  	return 0;  } @@ -327,8 +316,7 @@ int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,   */  void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(ctx); +	selinux_xfrm_free(ctx);  }  /* @@ -336,31 +324,58 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)   */  int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)  { -	const struct task_security_struct *tsec = current_security(); - -	if (!ctx) -		return 0; +	return selinux_xfrm_delete(ctx); +} -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +/* + * LSM hook implementation that allocates a xfrm_sec_state, populates it using + * the supplied security context, and assigns it to the xfrm_state. + */ +int selinux_xfrm_state_alloc(struct xfrm_state *x, +			     struct xfrm_user_sec_ctx *uctx) +{ +	return selinux_xfrm_alloc_user(&x->security, uctx, GFP_KERNEL);  }  /* - * LSM hook implementation that allocs and transfers sec_ctx spec to - * xfrm_state. + * LSM hook implementation that allocates a xfrm_sec_state and populates based + * on a secid.   */ -int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx, -		u32 secid) +int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, +				     struct xfrm_sec_ctx *polsec, u32 secid)  { -	int err; +	int rc; +	struct xfrm_sec_ctx *ctx; +	char *ctx_str = NULL; +	int str_len; + +	if (!polsec) +		return 0; + +	if (secid == 0) +		return -EINVAL; + +	rc = security_sid_to_context(secid, &ctx_str, &str_len); +	if (rc) +		return rc; -	BUG_ON(!x); +	ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); +	if (!ctx) { +		rc = -ENOMEM; +		goto out; +	} -	err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid); -	if (err == 0) -		atomic_inc(&selinux_xfrm_refcount); -	return err; +	ctx->ctx_doi = XFRM_SC_DOI_LSM; +	ctx->ctx_alg = XFRM_SC_ALG_SELINUX; +	ctx->ctx_sid = secid; +	ctx->ctx_len = str_len; +	memcpy(ctx->ctx_str, ctx_str, str_len); + +	x->security = ctx; +	atomic_inc(&selinux_xfrm_refcount); +out: +	kfree(ctx_str); +	return rc;  }  /* @@ -368,24 +383,15 @@ int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uct   */  void selinux_xfrm_state_free(struct xfrm_state *x)  { -	atomic_dec(&selinux_xfrm_refcount); -	kfree(x->security); +	selinux_xfrm_free(x->security);  } - /* -  * LSM hook implementation that authorizes deletion of labeled SAs. -  */ +/* + * LSM hook implementation that authorizes deletion of labeled SAs. + */  int selinux_xfrm_state_delete(struct xfrm_state *x)  { -	const struct task_security_struct *tsec = current_security(); -	struct xfrm_sec_ctx *ctx = x->security; - -	if (!ctx) -		return 0; - -	return avc_has_perm(tsec->sid, ctx->ctx_sid, -			    SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, -			    NULL); +	return selinux_xfrm_delete(x->security);  }  /* @@ -395,14 +401,12 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)   * we need to check for unlabelled access since this may not have   * gone thru the IPSec process.   */ -int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, -				struct common_audit_data *ad) +int selinux_xfrm_sock_rcv_skb(u32 sk_sid, struct sk_buff *skb, +			      struct common_audit_data *ad)  { -	int i, rc = 0; -	struct sec_path *sp; -	u32 sel_sid = SECINITSID_UNLABELED; - -	sp = skb->sp; +	int i; +	struct sec_path *sp = skb->sp; +	u32 peer_sid = SECINITSID_UNLABELED;  	if (sp) {  		for (i = 0; i < sp->len; i++) { @@ -410,23 +414,17 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,  			if (x && selinux_authorizable_xfrm(x)) {  				struct xfrm_sec_ctx *ctx = x->security; -				sel_sid = ctx->ctx_sid; +				peer_sid = ctx->ctx_sid;  				break;  			}  		}  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ - -	rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION, -			  ASSOCIATION__RECVFROM, ad); - -	return rc; +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, peer_sid, +			    SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, ad);  }  /* @@ -436,49 +434,38 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,   * If we do have a authorizable security association, then it has already been   * checked in the selinux_xfrm_state_pol_flow_match hook above.   */ -int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, -					struct common_audit_data *ad, u8 proto) +int selinux_xfrm_postroute_last(u32 sk_sid, struct sk_buff *skb, +				struct common_audit_data *ad, u8 proto)  {  	struct dst_entry *dst; -	int rc = 0; - -	dst = skb_dst(skb); - -	if (dst) { -		struct dst_entry *dst_test; - -		for (dst_test = dst; dst_test != NULL; -		     dst_test = dst_test->child) { -			struct xfrm_state *x = dst_test->xfrm; - -			if (x && selinux_authorizable_xfrm(x)) -				goto out; -		} -	}  	switch (proto) {  	case IPPROTO_AH:  	case IPPROTO_ESP:  	case IPPROTO_COMP: -		/* -		 * We should have already seen this packet once before -		 * it underwent xfrm(s). No need to subject it to the -		 * unlabeled check. -		 */ -		goto out; +		/* We should have already seen this packet once before it +		 * underwent xfrm(s). No need to subject it to the unlabeled +		 * check. */ +		return 0;  	default:  		break;  	} -	/* -	 * This check even when there's no association involved is -	 * intended, according to Trent Jaeger, to make sure a -	 * process can't engage in non-ipsec communication unless -	 * explicitly allowed by policy. -	 */ +	dst = skb_dst(skb); +	if (dst) { +		struct dst_entry *iter; -	rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, -			  ASSOCIATION__SENDTO, ad); -out: -	return rc; +		for (iter = dst; iter != NULL; iter = iter->child) { +			struct xfrm_state *x = iter->xfrm; + +			if (x && selinux_authorizable_xfrm(x)) +				return 0; +		} +	} + +	/* This check even when there's no association involved is intended, +	 * according to Trent Jaeger, to make sure a process can't engage in +	 * non-IPsec communication unless explicitly allowed by policy. */ +	return avc_has_perm(sk_sid, SECINITSID_UNLABELED, +			    SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, ad);  }  | 
