diff options
26 files changed, 1229 insertions, 423 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index df18d87c483..2193be53e77 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -315,3 +315,15 @@ When:	2.6.29 (ideally) or 2.6.30 (more likely)  Why:	Deprecated by the new (standard) device driver binding model. Use  	i2c_driver->probe() and ->remove() instead.  Who:	Jean Delvare <khali@linux-fr.org> + +--------------------------- + +What:	SELinux "compat_net" functionality +When:	2.6.30 at the earliest +Why:	In 2.6.18 the Secmark concept was introduced to replace the "compat_net" +	network access control functionality of SELinux.  Secmark offers both +	better performance and greater flexibility than the "compat_net" +	mechanism.  Now that the major Linux distributions have moved to +	Secmark, it is time to deprecate the older mechanism and start the +	process of removing the old code. +Who:	Paul Moore <paul.moore@hp.com> diff --git a/include/linux/capability.h b/include/linux/capability.h index e22f48c2a46..02bdb768d43 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -529,8 +529,21 @@ extern const kernel_cap_t __cap_init_eff_set;   *   * Note that this does not set PF_SUPERPRIV on the task.   */ -#define has_capability(t, cap) (security_capable((t), (cap)) == 0) -#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0) +#define has_capability(t, cap) (security_real_capable((t), (cap)) == 0) + +/** + * has_capability_noaudit - Determine if a task has a superior capability available (unaudited) + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect, false if not, but don't write an audit message for the + * check. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +#define has_capability_noaudit(t, cap) \ +	(security_real_capable_noaudit((t), (cap)) == 0)  extern int capable(int cap); diff --git a/include/linux/security.h b/include/linux/security.h index b92b5e453f6..1f2ab6353c0 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -48,7 +48,8 @@ struct audit_krule;   * These functions are in security/capability.c and are used   * as the default capabilities functions   */ -extern int cap_capable(struct task_struct *tsk, int cap, int audit); +extern int cap_capable(struct task_struct *tsk, const struct cred *cred, +		       int cap, int audit);  extern int cap_settime(struct timespec *ts, struct timezone *tz);  extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode);  extern int cap_ptrace_traceme(struct task_struct *parent); @@ -1251,9 +1252,12 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)   *	@permitted contains the permitted capability set.   *	Return 0 and update @new if permission is granted.   * @capable: - *	Check whether the @tsk process has the @cap capability. + *	Check whether the @tsk process has the @cap capability in the indicated + *	credentials.   *	@tsk contains the task_struct for the process. + *	@cred contains the credentials to use.   *	@cap contains the capability <include/linux/capability.h>. + *	@audit: Whether to write an audit message or not   *	Return 0 if the capability is granted for @tsk.   * @acct:   *	Check permission before enabling or disabling process accounting.  If @@ -1346,7 +1350,8 @@ struct security_operations {  		       const kernel_cap_t *effective,  		       const kernel_cap_t *inheritable,  		       const kernel_cap_t *permitted); -	int (*capable) (struct task_struct *tsk, int cap, int audit); +	int (*capable) (struct task_struct *tsk, const struct cred *cred, +			int cap, int audit);  	int (*acct) (struct file *file);  	int (*sysctl) (struct ctl_table *table, int op);  	int (*quotactl) (int cmds, int type, int id, struct super_block *sb); @@ -1628,8 +1633,9 @@ int security_capset(struct cred *new, const struct cred *old,  		    const kernel_cap_t *effective,  		    const kernel_cap_t *inheritable,  		    const kernel_cap_t *permitted); -int security_capable(struct task_struct *tsk, int cap); -int security_capable_noaudit(struct task_struct *tsk, int cap); +int security_capable(int cap); +int security_real_capable(struct task_struct *tsk, int cap); +int security_real_capable_noaudit(struct task_struct *tsk, int cap);  int security_acct(struct file *file);  int security_sysctl(struct ctl_table *table, int op);  int security_quotactl(int cmds, int type, int id, struct super_block *sb); @@ -1826,14 +1832,31 @@ static inline int security_capset(struct cred *new,  	return cap_capset(new, old, effective, inheritable, permitted);  } -static inline int security_capable(struct task_struct *tsk, int cap) +static inline int security_capable(int cap)  { -	return cap_capable(tsk, cap, SECURITY_CAP_AUDIT); +	return cap_capable(current, current_cred(), cap, SECURITY_CAP_AUDIT);  } -static inline int security_capable_noaudit(struct task_struct *tsk, int cap) +static inline int security_real_capable(struct task_struct *tsk, int cap)  { -	return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT); +	int ret; + +	rcu_read_lock(); +	ret = cap_capable(tsk, __task_cred(tsk), cap, SECURITY_CAP_AUDIT); +	rcu_read_unlock(); +	return ret; +} + +static inline +int security_real_capable_noaudit(struct task_struct *tsk, int cap) +{ +	int ret; + +	rcu_read_lock(); +	ret = cap_capable(tsk, __task_cred(tsk), cap, +			       SECURITY_CAP_NOAUDIT); +	rcu_read_unlock(); +	return ret;  }  static inline int security_acct(struct file *file) diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index 9909774eb99..bedc7f62e35 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -131,7 +131,8 @@ extern int cipso_v4_rbm_strictvalid;   */  #ifdef CONFIG_NETLABEL -int cipso_v4_doi_add(struct cipso_v4_doi *doi_def); +int cipso_v4_doi_add(struct cipso_v4_doi *doi_def, +		     struct netlbl_audit *audit_info);  void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);  int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);  struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi); @@ -140,7 +141,8 @@ int cipso_v4_doi_walk(u32 *skip_cnt,  		     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),  	             void *cb_arg);  #else -static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) +static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def, +				   struct netlbl_audit *audit_info)  {  	return -ENOSYS;  } diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 17c442a4514..749011eedc0 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -33,6 +33,8 @@  #include <linux/types.h>  #include <linux/net.h>  #include <linux/skbuff.h> +#include <linux/in.h> +#include <linux/in6.h>  #include <net/netlink.h>  #include <asm/atomic.h> @@ -353,13 +355,37 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr)  /*   * LSM configuration operations   */ -int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info); -int netlbl_cfg_unlbl_add_map(const char *domain, +int netlbl_cfg_map_del(const char *domain, +		       u16 family, +		       const void *addr, +		       const void *mask, +		       struct netlbl_audit *audit_info); +int netlbl_cfg_unlbl_map_add(const char *domain, +			     u16 family, +			     const void *addr, +			     const void *mask,  			     struct netlbl_audit *audit_info); -int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, +int netlbl_cfg_unlbl_static_add(struct net *net, +				const char *dev_name, +				const void *addr, +				const void *mask, +				u16 family, +				u32 secid, +				struct netlbl_audit *audit_info); +int netlbl_cfg_unlbl_static_del(struct net *net, +				const char *dev_name, +				const void *addr, +				const void *mask, +				u16 family, +				struct netlbl_audit *audit_info); +int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, +			   struct netlbl_audit *audit_info); +void netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info); +int netlbl_cfg_cipsov4_map_add(u32 doi,  			       const char *domain, +			       const struct in_addr *addr, +			       const struct in_addr *mask,  			       struct netlbl_audit *audit_info); -  /*   * LSM security attribute operations   */ @@ -401,19 +427,62 @@ void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);  void netlbl_cache_invalidate(void);  int netlbl_cache_add(const struct sk_buff *skb,  		     const struct netlbl_lsm_secattr *secattr); + +/* + * Protocol engine operations + */ +struct audit_buffer *netlbl_audit_start(int type, +					struct netlbl_audit *audit_info);  #else  static inline int netlbl_cfg_map_del(const char *domain, +				     u16 family, +				     const void *addr, +				     const void *mask,  				     struct netlbl_audit *audit_info)  {  	return -ENOSYS;  } -static inline int netlbl_cfg_unlbl_add_map(const char *domain, +static inline int netlbl_cfg_unlbl_map_add(const char *domain, +					   u16 family, +					   void *addr, +					   void *mask,  					   struct netlbl_audit *audit_info)  {  	return -ENOSYS;  } -static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, +static inline int netlbl_cfg_unlbl_static_add(struct net *net, +					      const char *dev_name, +					      const void *addr, +					      const void *mask, +					      u16 family, +					      u32 secid, +					      struct netlbl_audit *audit_info) +{ +	return -ENOSYS; +} +static inline int netlbl_cfg_unlbl_static_del(struct net *net, +					      const char *dev_name, +					      const void *addr, +					      const void *mask, +					      u16 family, +					      struct netlbl_audit *audit_info) +{ +	return -ENOSYS; +} +static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, +					 struct netlbl_audit *audit_info) +{ +	return -ENOSYS; +} +static inline void netlbl_cfg_cipsov4_del(u32 doi, +					  struct netlbl_audit *audit_info) +{ +	return; +} +static inline int netlbl_cfg_cipsov4_map_add(u32 doi,  					     const char *domain, +					     const struct in_addr *addr, +					     const struct in_addr *mask,  					     struct netlbl_audit *audit_info)  {  	return -ENOSYS; @@ -495,6 +564,11 @@ static inline int netlbl_cache_add(const struct sk_buff *skb,  {  	return 0;  } +static inline struct audit_buffer *netlbl_audit_start(int type, +						struct netlbl_audit *audit_info) +{ +	return NULL; +}  #endif /* CONFIG_NETLABEL */  #endif /* _NETLABEL_H */ diff --git a/kernel/capability.c b/kernel/capability.c index c598d9d5be4..688926e496b 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -306,7 +306,7 @@ int capable(int cap)  		BUG();  	} -	if (has_capability(current, cap)) { +	if (security_capable(cap) == 0) {  		current->flags |= PF_SUPERPRIV;  		return 1;  	} diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index e52799047a5..6bb2635b5de 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -38,6 +38,7 @@  #include <linux/spinlock.h>  #include <linux/string.h>  #include <linux/jhash.h> +#include <linux/audit.h>  #include <net/ip.h>  #include <net/icmp.h>  #include <net/tcp.h> @@ -449,6 +450,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)  /**   * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine   * @doi_def: the DOI structure + * @audit_info: NetLabel audit information   *   * Description:   * The caller defines a new DOI for use by the CIPSO engine and calls this @@ -458,50 +460,78 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)   * zero on success and non-zero on failure.   *   */ -int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) +int cipso_v4_doi_add(struct cipso_v4_doi *doi_def, +		     struct netlbl_audit *audit_info)  { +	int ret_val = -EINVAL;  	u32 iter; +	u32 doi; +	u32 doi_type; +	struct audit_buffer *audit_buf; + +	doi = doi_def->doi; +	doi_type = doi_def->type;  	if (doi_def == NULL || doi_def->doi == CIPSO_V4_DOI_UNKNOWN) -		return -EINVAL; +		goto doi_add_return;  	for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {  		switch (doi_def->tags[iter]) {  		case CIPSO_V4_TAG_RBITMAP:  			break;  		case CIPSO_V4_TAG_RANGE: -			if (doi_def->type != CIPSO_V4_MAP_PASS) -				return -EINVAL; -			break; -		case CIPSO_V4_TAG_INVALID: -			if (iter == 0) -				return -EINVAL; -			break;  		case CIPSO_V4_TAG_ENUM:  			if (doi_def->type != CIPSO_V4_MAP_PASS) -				return -EINVAL; +				goto doi_add_return;  			break;  		case CIPSO_V4_TAG_LOCAL:  			if (doi_def->type != CIPSO_V4_MAP_LOCAL) -				return -EINVAL; +				goto doi_add_return; +			break; +		case CIPSO_V4_TAG_INVALID: +			if (iter == 0) +				goto doi_add_return;  			break;  		default: -			return -EINVAL; +			goto doi_add_return;  		}  	}  	atomic_set(&doi_def->refcount, 1);  	spin_lock(&cipso_v4_doi_list_lock); -	if (cipso_v4_doi_search(doi_def->doi) != NULL) -		goto doi_add_failure; +	if (cipso_v4_doi_search(doi_def->doi) != NULL) { +		spin_unlock(&cipso_v4_doi_list_lock); +		ret_val = -EEXIST; +		goto doi_add_return; +	}  	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);  	spin_unlock(&cipso_v4_doi_list_lock); +	ret_val = 0; -	return 0; +doi_add_return: +	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info); +	if (audit_buf != NULL) { +		const char *type_str; +		switch (doi_type) { +		case CIPSO_V4_MAP_TRANS: +			type_str = "trans"; +			break; +		case CIPSO_V4_MAP_PASS: +			type_str = "pass"; +			break; +		case CIPSO_V4_MAP_LOCAL: +			type_str = "local"; +			break; +		default: +			type_str = "(unknown)"; +		} +		audit_log_format(audit_buf, +				 " cipso_doi=%u cipso_type=%s res=%u", +				 doi, type_str, ret_val == 0 ? 1 : 0); +		audit_log_end(audit_buf); +	} -doi_add_failure: -	spin_unlock(&cipso_v4_doi_list_lock); -	return -EEXIST; +	return ret_val;  }  /** @@ -559,25 +589,39 @@ static void cipso_v4_doi_free_rcu(struct rcu_head *entry)   */  int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)  { +	int ret_val;  	struct cipso_v4_doi *doi_def; +	struct audit_buffer *audit_buf;  	spin_lock(&cipso_v4_doi_list_lock);  	doi_def = cipso_v4_doi_search(doi);  	if (doi_def == NULL) {  		spin_unlock(&cipso_v4_doi_list_lock); -		return -ENOENT; +		ret_val = -ENOENT; +		goto doi_remove_return;  	}  	if (!atomic_dec_and_test(&doi_def->refcount)) {  		spin_unlock(&cipso_v4_doi_list_lock); -		return -EBUSY; +		ret_val = -EBUSY; +		goto doi_remove_return;  	}  	list_del_rcu(&doi_def->list);  	spin_unlock(&cipso_v4_doi_list_lock);  	cipso_v4_cache_invalidate();  	call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); +	ret_val = 0; -	return 0; +doi_remove_return: +	audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info); +	if (audit_buf != NULL) { +		audit_log_format(audit_buf, +				 " cipso_doi=%u res=%u", +				 doi, ret_val == 0 ? 1 : 0); +		audit_log_end(audit_buf); +	} + +	return ret_val;  }  /** diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index fff32b70efa..bf1ab1a6790 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -130,6 +130,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,  /**   * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition   * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information   *   * Description:   * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD @@ -137,7 +138,8 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,   * non-zero on error.   *   */ -static int netlbl_cipsov4_add_std(struct genl_info *info) +static int netlbl_cipsov4_add_std(struct genl_info *info, +				  struct netlbl_audit *audit_info)  {  	int ret_val = -EINVAL;  	struct cipso_v4_doi *doi_def = NULL; @@ -316,7 +318,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)  			}  	} -	ret_val = cipso_v4_doi_add(doi_def); +	ret_val = cipso_v4_doi_add(doi_def, audit_info);  	if (ret_val != 0)  		goto add_std_failure;  	return 0; @@ -330,6 +332,7 @@ add_std_failure:  /**   * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition   * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information   *   * Description:   * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message @@ -337,7 +340,8 @@ add_std_failure:   * error.   *   */ -static int netlbl_cipsov4_add_pass(struct genl_info *info) +static int netlbl_cipsov4_add_pass(struct genl_info *info, +				   struct netlbl_audit *audit_info)  {  	int ret_val;  	struct cipso_v4_doi *doi_def = NULL; @@ -354,7 +358,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)  	if (ret_val != 0)  		goto add_pass_failure; -	ret_val = cipso_v4_doi_add(doi_def); +	ret_val = cipso_v4_doi_add(doi_def, audit_info);  	if (ret_val != 0)  		goto add_pass_failure;  	return 0; @@ -367,6 +371,7 @@ add_pass_failure:  /**   * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition   * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information   *   * Description:   * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD @@ -374,7 +379,8 @@ add_pass_failure:   * non-zero on error.   *   */ -static int netlbl_cipsov4_add_local(struct genl_info *info) +static int netlbl_cipsov4_add_local(struct genl_info *info, +				    struct netlbl_audit *audit_info)  {  	int ret_val;  	struct cipso_v4_doi *doi_def = NULL; @@ -391,7 +397,7 @@ static int netlbl_cipsov4_add_local(struct genl_info *info)  	if (ret_val != 0)  		goto add_local_failure; -	ret_val = cipso_v4_doi_add(doi_def); +	ret_val = cipso_v4_doi_add(doi_def, audit_info);  	if (ret_val != 0)  		goto add_local_failure;  	return 0; @@ -415,48 +421,31 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)  {  	int ret_val = -EINVAL; -	u32 type; -	u32 doi;  	const char *type_str = "(unknown)"; -	struct audit_buffer *audit_buf;  	struct netlbl_audit audit_info;  	if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||  	    !info->attrs[NLBL_CIPSOV4_A_MTYPE])  		return -EINVAL; -	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);  	netlbl_netlink_auditinfo(skb, &audit_info); - -	type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]); -	switch (type) { +	switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {  	case CIPSO_V4_MAP_TRANS:  		type_str = "trans"; -		ret_val = netlbl_cipsov4_add_std(info); +		ret_val = netlbl_cipsov4_add_std(info, &audit_info);  		break;  	case CIPSO_V4_MAP_PASS:  		type_str = "pass"; -		ret_val = netlbl_cipsov4_add_pass(info); +		ret_val = netlbl_cipsov4_add_pass(info, &audit_info);  		break;  	case CIPSO_V4_MAP_LOCAL:  		type_str = "local"; -		ret_val = netlbl_cipsov4_add_local(info); +		ret_val = netlbl_cipsov4_add_local(info, &audit_info);  		break;  	}  	if (ret_val == 0)  		atomic_inc(&netlabel_mgmt_protocount); -	audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, -					      &audit_info); -	if (audit_buf != NULL) { -		audit_log_format(audit_buf, -				 " cipso_doi=%u cipso_type=%s res=%u", -				 doi, -				 type_str, -				 ret_val == 0 ? 1 : 0); -		audit_log_end(audit_buf); -	} -  	return ret_val;  } @@ -725,9 +714,7 @@ static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)  static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)  {  	int ret_val = -EINVAL; -	u32 doi = 0;  	struct netlbl_domhsh_walk_arg cb_arg; -	struct audit_buffer *audit_buf;  	struct netlbl_audit audit_info;  	u32 skip_bkt = 0;  	u32 skip_chain = 0; @@ -735,29 +722,17 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)  	if (!info->attrs[NLBL_CIPSOV4_A_DOI])  		return -EINVAL; -	doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);  	netlbl_netlink_auditinfo(skb, &audit_info); - -	cb_arg.doi = doi; +	cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);  	cb_arg.audit_info = &audit_info;  	ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,  				     netlbl_cipsov4_remove_cb, &cb_arg);  	if (ret_val == 0 || ret_val == -ENOENT) { -		ret_val = cipso_v4_doi_remove(doi, &audit_info); +		ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);  		if (ret_val == 0)  			atomic_dec(&netlabel_mgmt_protocount);  	} -	audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL, -					      &audit_info); -	if (audit_buf != NULL) { -		audit_log_format(audit_buf, -				 " cipso_doi=%u res=%u", -				 doi, -				 ret_val == 0 ? 1 : 0); -		audit_log_end(audit_buf); -	} -  	return ret_val;  } diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 5fadf10e5dd..7a10bbe02c1 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -483,6 +483,73 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,  }  /** + * netlbl_domhsh_remove_af4 - Removes an address selector entry + * @domain: the domain + * @addr: IPv4 address + * @mask: IPv4 address mask + * @audit_info: NetLabel audit information + * + * Description: + * Removes an individual address selector from a domain mapping and potentially + * the entire mapping if it is empty.  Returns zero on success, negative values + * on failure. + * + */ +int netlbl_domhsh_remove_af4(const char *domain, +			     const struct in_addr *addr, +			     const struct in_addr *mask, +			     struct netlbl_audit *audit_info) +{ +	struct netlbl_dom_map *entry_map; +	struct netlbl_af4list *entry_addr; +	struct netlbl_af4list *iter4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +	struct netlbl_af6list *iter6; +#endif /* IPv6 */ +	struct netlbl_domaddr4_map *entry; + +	rcu_read_lock(); + +	if (domain) +		entry_map = netlbl_domhsh_search(domain); +	else +		entry_map = netlbl_domhsh_search_def(domain); +	if (entry_map == NULL || entry_map->type != NETLBL_NLTYPE_ADDRSELECT) +		goto remove_af4_failure; + +	spin_lock(&netlbl_domhsh_lock); +	entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr, +					   &entry_map->type_def.addrsel->list4); +	spin_unlock(&netlbl_domhsh_lock); + +	if (entry_addr == NULL) +		goto remove_af4_failure; +	netlbl_af4list_foreach_rcu(iter4, &entry_map->type_def.addrsel->list4) +		goto remove_af4_single_addr; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +	netlbl_af6list_foreach_rcu(iter6, &entry_map->type_def.addrsel->list6) +		goto remove_af4_single_addr; +#endif /* IPv6 */ +	/* the domain mapping is empty so remove it from the mapping table */ +	netlbl_domhsh_remove_entry(entry_map, audit_info); + +remove_af4_single_addr: +	rcu_read_unlock(); +	/* yick, we can't use call_rcu here because we don't have a rcu head +	 * pointer but hopefully this should be a rare case so the pause +	 * shouldn't be a problem */ +	synchronize_rcu(); +	entry = netlbl_domhsh_addr4_entry(entry_addr); +	cipso_v4_doi_putdef(entry->type_def.cipsov4); +	kfree(entry); +	return 0; + +remove_af4_failure: +	rcu_read_unlock(); +	return -ENOENT; +} + +/**   * netlbl_domhsh_remove - Removes an entry from the domain hash table   * @domain: the domain to remove   * @audit_info: NetLabel audit information diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index bfcb6763a1a..0261dda3f2d 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -90,6 +90,10 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,  			      struct netlbl_audit *audit_info);  int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,  			       struct netlbl_audit *audit_info); +int netlbl_domhsh_remove_af4(const char *domain, +			     const struct in_addr *addr, +			     const struct in_addr *mask, +			     struct netlbl_audit *audit_info);  int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);  int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);  struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain); diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index b32eceb3ab0..fd9229db075 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -31,7 +31,10 @@  #include <linux/init.h>  #include <linux/types.h>  #include <linux/audit.h> +#include <linux/in.h> +#include <linux/in6.h>  #include <net/ip.h> +#include <net/ipv6.h>  #include <net/netlabel.h>  #include <net/cipso_ipv4.h>  #include <asm/bug.h> @@ -42,6 +45,7 @@  #include "netlabel_cipso_v4.h"  #include "netlabel_user.h"  #include "netlabel_mgmt.h" +#include "netlabel_addrlist.h"  /*   * Configuration Functions @@ -50,6 +54,9 @@  /**   * netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping   * @domain: the domain mapping to remove + * @family: address family + * @addr: IP address + * @mask: IP address mask   * @audit_info: NetLabel audit information   *   * Description: @@ -58,14 +65,32 @@   * values on failure.   *   */ -int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info) +int netlbl_cfg_map_del(const char *domain, +		       u16 family, +		       const void *addr, +		       const void *mask, +		       struct netlbl_audit *audit_info)  { -	return netlbl_domhsh_remove(domain, audit_info); +	if (addr == NULL && mask == NULL) { +		return netlbl_domhsh_remove(domain, audit_info); +	} else if (addr != NULL && mask != NULL) { +		switch (family) { +		case AF_INET: +			return netlbl_domhsh_remove_af4(domain, addr, mask, +							audit_info); +		default: +			return -EPFNOSUPPORT; +		} +	} else +		return -EINVAL;  }  /** - * netlbl_cfg_unlbl_add_map - Add an unlabeled NetLabel/LSM domain mapping + * netlbl_cfg_unlbl_map_add - Add a new unlabeled mapping   * @domain: the domain mapping to add + * @family: address family + * @addr: IP address + * @mask: IP address mask   * @audit_info: NetLabel audit information   *   * Description: @@ -74,11 +99,19 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)   * negative values on failure.   *   */ -int netlbl_cfg_unlbl_add_map(const char *domain, +int netlbl_cfg_unlbl_map_add(const char *domain, +			     u16 family, +			     const void *addr, +			     const void *mask,  			     struct netlbl_audit *audit_info)  {  	int ret_val = -ENOMEM;  	struct netlbl_dom_map *entry; +	struct netlbl_domaddr_map *addrmap = NULL; +	struct netlbl_domaddr4_map *map4 = NULL; +	struct netlbl_domaddr6_map *map6 = NULL; +	const struct in_addr *addr4, *mask4; +	const struct in6_addr *addr6, *mask6;  	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);  	if (entry == NULL) @@ -86,49 +119,225 @@ int netlbl_cfg_unlbl_add_map(const char *domain,  	if (domain != NULL) {  		entry->domain = kstrdup(domain, GFP_ATOMIC);  		if (entry->domain == NULL) -			goto cfg_unlbl_add_map_failure; +			goto cfg_unlbl_map_add_failure; +	} + +	if (addr == NULL && mask == NULL) +		entry->type = NETLBL_NLTYPE_UNLABELED; +	else if (addr != NULL && mask != NULL) { +		addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC); +		if (addrmap == NULL) +			goto cfg_unlbl_map_add_failure; +		INIT_LIST_HEAD(&addrmap->list4); +		INIT_LIST_HEAD(&addrmap->list6); + +		switch (family) { +		case AF_INET: +			addr4 = addr; +			mask4 = mask; +			map4 = kzalloc(sizeof(*map4), GFP_ATOMIC); +			if (map4 == NULL) +				goto cfg_unlbl_map_add_failure; +			map4->type = NETLBL_NLTYPE_UNLABELED; +			map4->list.addr = addr4->s_addr & mask4->s_addr; +			map4->list.mask = mask4->s_addr; +			map4->list.valid = 1; +			ret_val = netlbl_af4list_add(&map4->list, +						     &addrmap->list4); +			if (ret_val != 0) +				goto cfg_unlbl_map_add_failure; +			break; +		case AF_INET6: +			addr6 = addr; +			mask6 = mask; +			map6 = kzalloc(sizeof(*map6), GFP_ATOMIC); +			if (map4 == NULL) +				goto cfg_unlbl_map_add_failure; +			map6->type = NETLBL_NLTYPE_UNLABELED; +			ipv6_addr_copy(&map6->list.addr, addr6); +			map6->list.addr.s6_addr32[0] &= mask6->s6_addr32[0]; +			map6->list.addr.s6_addr32[1] &= mask6->s6_addr32[1]; +			map6->list.addr.s6_addr32[2] &= mask6->s6_addr32[2]; +			map6->list.addr.s6_addr32[3] &= mask6->s6_addr32[3]; +			ipv6_addr_copy(&map6->list.mask, mask6); +			map6->list.valid = 1; +			ret_val = netlbl_af4list_add(&map4->list, +						     &addrmap->list4); +			if (ret_val != 0) +				goto cfg_unlbl_map_add_failure; +			break; +		default: +			goto cfg_unlbl_map_add_failure; +			break; +		} + +		entry->type_def.addrsel = addrmap; +		entry->type = NETLBL_NLTYPE_ADDRSELECT; +	} else { +		ret_val = -EINVAL; +		goto cfg_unlbl_map_add_failure;  	} -	entry->type = NETLBL_NLTYPE_UNLABELED;  	ret_val = netlbl_domhsh_add(entry, audit_info);  	if (ret_val != 0) -		goto cfg_unlbl_add_map_failure; +		goto cfg_unlbl_map_add_failure;  	return 0; -cfg_unlbl_add_map_failure: +cfg_unlbl_map_add_failure:  	if (entry != NULL)  		kfree(entry->domain);  	kfree(entry); +	kfree(addrmap); +	kfree(map4); +	kfree(map6);  	return ret_val;  } + +/** + * netlbl_cfg_unlbl_static_add - Adds a new static label + * @net: network namespace + * @dev_name: interface name + * @addr: IP address in network byte order (struct in[6]_addr) + * @mask: address mask in network byte order (struct in[6]_addr) + * @family: address family + * @secid: LSM secid value for the entry + * @audit_info: NetLabel audit information + * + * Description: + * Adds a new NetLabel static label to be used when protocol provided labels + * are not present on incoming traffic.  If @dev_name is NULL then the default + * interface will be used.  Returns zero on success, negative values on failure. + * + */ +int netlbl_cfg_unlbl_static_add(struct net *net, +				const char *dev_name, +				const void *addr, +				const void *mask, +				u16 family, +				u32 secid, +				struct netlbl_audit *audit_info) +{ +	u32 addr_len; + +	switch (family) { +	case AF_INET: +		addr_len = sizeof(struct in_addr); +		break; +	case AF_INET6: +		addr_len = sizeof(struct in6_addr); +		break; +	default: +		return -EPFNOSUPPORT; +	} + +	return netlbl_unlhsh_add(net, +				 dev_name, addr, mask, addr_len, +				 secid, audit_info); +} + +/** + * netlbl_cfg_unlbl_static_del - Removes an existing static label + * @net: network namespace + * @dev_name: interface name + * @addr: IP address in network byte order (struct in[6]_addr) + * @mask: address mask in network byte order (struct in[6]_addr) + * @family: address family + * @secid: LSM secid value for the entry + * @audit_info: NetLabel audit information + * + * Description: + * Removes an existing NetLabel static label used when protocol provided labels + * are not present on incoming traffic.  If @dev_name is NULL then the default + * interface will be used.  Returns zero on success, negative values on failure. + * + */ +int netlbl_cfg_unlbl_static_del(struct net *net, +				const char *dev_name, +				const void *addr, +				const void *mask, +				u16 family, +				struct netlbl_audit *audit_info) +{ +	u32 addr_len; + +	switch (family) { +	case AF_INET: +		addr_len = sizeof(struct in_addr); +		break; +	case AF_INET6: +		addr_len = sizeof(struct in6_addr); +		break; +	default: +		return -EPFNOSUPPORT; +	} + +	return netlbl_unlhsh_remove(net, +				    dev_name, addr, mask, addr_len, +				    audit_info); +} +  /** - * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping - * @doi_def: the DOI definition + * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition + * @doi_def: CIPSO DOI definition + * @audit_info: NetLabel audit information + * + * Description: + * Add a new CIPSO DOI definition as defined by @doi_def.  Returns zero on + * success and negative values on failure. + * + */ +int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, +			   struct netlbl_audit *audit_info) +{ +	return cipso_v4_doi_add(doi_def, audit_info); +} + +/** + * netlbl_cfg_cipsov4_del - Remove an existing CIPSOv4 DOI definition + * @doi: CIPSO DOI + * @audit_info: NetLabel audit information + * + * Description: + * Remove an existing CIPSO DOI definition matching @doi.  Returns zero on + * success and negative values on failure. + * + */ +void netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info) +{ +	cipso_v4_doi_remove(doi, audit_info); +} + +/** + * netlbl_cfg_cipsov4_map_add - Add a new CIPSOv4 DOI mapping + * @doi: the CIPSO DOI   * @domain: the domain mapping to add + * @addr: IP address + * @mask: IP address mask   * @audit_info: NetLabel audit information   *   * Description: - * Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this - * new DOI definition to the NetLabel subsystem.  A @domain value of NULL adds - * a new default domain mapping.  Returns zero on success, negative values on - * failure. + * Add a new NetLabel/LSM domain mapping for the given CIPSO DOI to the NetLabel + * subsystem.  A @domain value of NULL adds a new default domain mapping. + * Returns zero on success, negative values on failure.   *   */ -int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, +int netlbl_cfg_cipsov4_map_add(u32 doi,  			       const char *domain, +			       const struct in_addr *addr, +			       const struct in_addr *mask,  			       struct netlbl_audit *audit_info)  {  	int ret_val = -ENOMEM; -	u32 doi; -	u32 doi_type; +	struct cipso_v4_doi *doi_def;  	struct netlbl_dom_map *entry; -	const char *type_str; -	struct audit_buffer *audit_buf; +	struct netlbl_domaddr_map *addrmap = NULL; +	struct netlbl_domaddr4_map *addrinfo = NULL; -	doi = doi_def->doi; -	doi_type = doi_def->type; +	doi_def = cipso_v4_doi_getdef(doi); +	if (doi_def == NULL) +		return -ENOENT;  	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);  	if (entry == NULL) @@ -136,56 +345,52 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,  	if (domain != NULL) {  		entry->domain = kstrdup(domain, GFP_ATOMIC);  		if (entry->domain == NULL) -			goto cfg_cipsov4_add_map_failure; +			goto cfg_cipsov4_map_add_failure;  	} -	ret_val = cipso_v4_doi_add(doi_def); -	if (ret_val != 0) -		goto cfg_cipsov4_add_map_failure_remove_doi; -	entry->type = NETLBL_NLTYPE_CIPSOV4; -	entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi); -	if (entry->type_def.cipsov4 == NULL) { -		ret_val = -ENOENT; -		goto cfg_cipsov4_add_map_failure_remove_doi; +	if (addr == NULL && mask == NULL) { +		entry->type_def.cipsov4 = doi_def; +		entry->type = NETLBL_NLTYPE_CIPSOV4; +	} else if (addr != NULL && mask != NULL) { +		addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC); +		if (addrmap == NULL) +			goto cfg_cipsov4_map_add_failure; +		INIT_LIST_HEAD(&addrmap->list4); +		INIT_LIST_HEAD(&addrmap->list6); + +		addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC); +		if (addrinfo == NULL) +			goto cfg_cipsov4_map_add_failure; +		addrinfo->type_def.cipsov4 = doi_def; +		addrinfo->type = NETLBL_NLTYPE_CIPSOV4; +		addrinfo->list.addr = addr->s_addr & mask->s_addr; +		addrinfo->list.mask = mask->s_addr; +		addrinfo->list.valid = 1; +		ret_val = netlbl_af4list_add(&addrinfo->list, &addrmap->list4); +		if (ret_val != 0) +			goto cfg_cipsov4_map_add_failure; + +		entry->type_def.addrsel = addrmap; +		entry->type = NETLBL_NLTYPE_ADDRSELECT; +	} else { +		ret_val = -EINVAL; +		goto cfg_cipsov4_map_add_failure;  	} +  	ret_val = netlbl_domhsh_add(entry, audit_info);  	if (ret_val != 0) -		goto cfg_cipsov4_add_map_failure_release_doi; - -cfg_cipsov4_add_map_return: -	audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, -					      audit_info); -	if (audit_buf != NULL) { -		switch (doi_type) { -		case CIPSO_V4_MAP_TRANS: -			type_str = "trans"; -			break; -		case CIPSO_V4_MAP_PASS: -			type_str = "pass"; -			break; -		case CIPSO_V4_MAP_LOCAL: -			type_str = "local"; -			break; -		default: -			type_str = "(unknown)"; -		} -		audit_log_format(audit_buf, -				 " cipso_doi=%u cipso_type=%s res=%u", -				 doi, type_str, ret_val == 0 ? 1 : 0); -		audit_log_end(audit_buf); -	} +		goto cfg_cipsov4_map_add_failure; -	return ret_val; +	return 0; -cfg_cipsov4_add_map_failure_release_doi: +cfg_cipsov4_map_add_failure:  	cipso_v4_doi_putdef(doi_def); -cfg_cipsov4_add_map_failure_remove_doi: -	cipso_v4_doi_remove(doi, audit_info); -cfg_cipsov4_add_map_failure:  	if (entry != NULL)  		kfree(entry->domain);  	kfree(entry); -	goto cfg_cipsov4_add_map_return; +	kfree(addrmap); +	kfree(addrinfo); +	return ret_val;  }  /* @@ -691,6 +896,28 @@ int netlbl_cache_add(const struct sk_buff *skb,  }  /* + * Protocol Engine Functions + */ + +/** + * netlbl_audit_start - Start an audit message + * @type: audit message type + * @audit_info: NetLabel audit information + * + * Description: + * Start an audit message using the type specified in @type and fill the audit + * message with some fields common to all NetLabel audit messages.  This + * function should only be used by protocol engines, not LSMs.  Returns a + * pointer to the audit buffer on success, NULL on failure. + * + */ +struct audit_buffer *netlbl_audit_start(int type, +					struct netlbl_audit *audit_info) +{ +	return netlbl_audit_start_common(type, audit_info); +} + +/*   * Setup Functions   */ diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 8c030803217..f3c5c68c684 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -450,13 +450,13 @@ add_iface_failure:   * success, negative values on failure.   *   */ -static int netlbl_unlhsh_add(struct net *net, -			     const char *dev_name, -			     const void *addr, -			     const void *mask, -			     u32 addr_len, -			     u32 secid, -			     struct netlbl_audit *audit_info) +int netlbl_unlhsh_add(struct net *net, +		      const char *dev_name, +		      const void *addr, +		      const void *mask, +		      u32 addr_len, +		      u32 secid, +		      struct netlbl_audit *audit_info)  {  	int ret_val;  	int ifindex; @@ -720,12 +720,12 @@ unlhsh_condremove_failure:   * Returns zero on success, negative values on failure.   *   */ -static int netlbl_unlhsh_remove(struct net *net, -				const char *dev_name, -				const void *addr, -				const void *mask, -				u32 addr_len, -				struct netlbl_audit *audit_info) +int netlbl_unlhsh_remove(struct net *net, +			 const char *dev_name, +			 const void *addr, +			 const void *mask, +			 u32 addr_len, +			 struct netlbl_audit *audit_info)  {  	int ret_val;  	struct net_device *dev; diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h index 06b1301ac07..7aba6359513 100644 --- a/net/netlabel/netlabel_unlabeled.h +++ b/net/netlabel/netlabel_unlabeled.h @@ -221,6 +221,21 @@ int netlbl_unlabel_genl_init(void);  /* General Unlabeled init function */  int netlbl_unlabel_init(u32 size); +/* Static/Fallback label management functions */ +int netlbl_unlhsh_add(struct net *net, +		      const char *dev_name, +		      const void *addr, +		      const void *mask, +		      u32 addr_len, +		      u32 secid, +		      struct netlbl_audit *audit_info); +int netlbl_unlhsh_remove(struct net *net, +			 const char *dev_name, +			 const void *addr, +			 const void *mask, +			 u32 addr_len, +			 struct netlbl_audit *audit_info); +  /* Process Unlabeled incoming network packets */  int netlbl_unlabel_getattr(const struct sk_buff *skb,  			   u16 family, diff --git a/security/commoncap.c b/security/commoncap.c index 69fc9952650..7cd61a5f520 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -45,26 +45,22 @@ EXPORT_SYMBOL(cap_netlink_recv);  /**   * cap_capable - Determine whether a task has a particular effective capability   * @tsk: The task to query + * @cred: The credentials to use   * @cap: The capability to check for   * @audit: Whether to write an audit message or not   *   * Determine whether the nominated task has the specified capability amongst   * its effective set, returning 0 if it does, -ve if it does not.   * - * NOTE WELL: cap_capable() cannot be used like the kernel's capable() - * function.  That is, it has the reverse semantics: cap_capable() returns 0 - * when a task has a capability, but the kernel's capable() returns 1 for this - * case. + * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() + * and has_capability() functions.  That is, it has the reverse semantics: + * cap_has_capability() returns 0 when a task has a capability, but the + * kernel's capable() and has_capability() returns 1 for this case.   */ -int cap_capable(struct task_struct *tsk, int cap, int audit) +int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, +		int audit)  { -	__u32 cap_raised; - -	/* Derived from include/linux/sched.h:capable. */ -	rcu_read_lock(); -	cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); -	rcu_read_unlock(); -	return cap_raised ? 0 : -EPERM; +	return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;  }  /** @@ -160,7 +156,8 @@ static inline int cap_inh_is_capped(void)  	/* they are so limited unless the current task has the CAP_SETPCAP  	 * capability  	 */ -	if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) +	if (cap_capable(current, current_cred(), CAP_SETPCAP, +			SECURITY_CAP_AUDIT) == 0)  		return 0;  #endif  	return 1; @@ -869,7 +866,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,  		     & (new->securebits ^ arg2))			/*[1]*/  		    || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/  		    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))	/*[3]*/ -		    || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ +		    || (cap_capable(current, current_cred(), CAP_SETPCAP, +				    SECURITY_CAP_AUDIT) != 0)		/*[4]*/  			/*  			 * [1] no changing of bits that are locked  			 * [2] no unlocking of locks @@ -950,7 +948,8 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)  {  	int cap_sys_admin = 0; -	if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0) +	if (cap_capable(current, current_cred(), CAP_SYS_ADMIN, +			SECURITY_CAP_NOAUDIT) == 0)  		cap_sys_admin = 1;  	return __vm_enough_memory(mm, pages, cap_sys_admin);  } diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6688765bd8b..09796797d12 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1294,7 +1294,7 @@ asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,  	case KEYCTL_GET_SECURITY:  		return keyctl_get_security((key_serial_t) arg2, -					   (char *) arg3, +					   (char __user *) arg3,  					   (size_t) arg4);  	default: diff --git a/security/security.c b/security/security.c index 678d4d07b85..c3586c0d97e 100644 --- a/security/security.c +++ b/security/security.c @@ -154,14 +154,32 @@ int security_capset(struct cred *new, const struct cred *old,  				    effective, inheritable, permitted);  } -int security_capable(struct task_struct *tsk, int cap) +int security_capable(int cap)  { -	return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT); +	return security_ops->capable(current, current_cred(), cap, +				     SECURITY_CAP_AUDIT);  } -int security_capable_noaudit(struct task_struct *tsk, int cap) +int security_real_capable(struct task_struct *tsk, int cap)  { -	return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT); +	const struct cred *cred; +	int ret; + +	cred = get_task_cred(tsk); +	ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_AUDIT); +	put_cred(cred); +	return ret; +} + +int security_real_capable_noaudit(struct task_struct *tsk, int cap) +{ +	const struct cred *cred; +	int ret; + +	cred = get_task_cred(tsk); +	ret = security_ops->capable(tsk, cred, cap, SECURITY_CAP_NOAUDIT); +	put_cred(cred); +	return ret;  }  int security_acct(struct file *file) diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index 26301dd651d..bca1b74a4a2 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -94,33 +94,6 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE  	  If you are unsure how to answer this question, answer 1. -config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT -	bool "NSA SELinux enable new secmark network controls by default" -	depends on SECURITY_SELINUX -	default n -	help -	  This option determines whether the new secmark-based network -	  controls will be enabled by default.  If not, the old internal -	  per-packet controls will be enabled by default, preserving -	  old behavior. - -	  If you enable the new controls, you will need updated -	  SELinux userspace libraries, tools and policy.  Typically, -	  your distribution will provide these and enable the new controls -	  in the kernel they also distribute. - -	  Note that this option can be overridden at boot with the -	  selinux_compat_net parameter, and after boot via -	  /selinux/compat_net.  See Documentation/kernel-parameters.txt -	  for details on this parameter. - -	  If you enable the new network controls, you will likely -	  also require the SECMARK and CONNSECMARK targets, as -	  well as any conntrack helpers for protocols which you -	  wish to control. - -	  If you are unsure what to do here, select N. -  config SECURITY_SELINUX_POLICYDB_VERSION_MAX  	bool "NSA SELinux maximum supported policy format version"  	depends on SECURITY_SELINUX diff --git a/security/selinux/avc.c b/security/selinux/avc.c index d43bd6baeea..eb41f43e277 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -53,18 +53,20 @@ static const char *class_to_string[] = {  #undef S_  static const struct av_inherit av_inherit[] = { -#define S_(c, i, b) { c, common_##i##_perm_to_string, b }, +#define S_(c, i, b) {	.tclass = c,\ +			.common_pts = common_##i##_perm_to_string,\ +			.common_base =  b },  #include "av_inherit.h"  #undef S_  };  const struct selinux_class_perm selinux_class_perm = { -	av_perm_to_string, -	ARRAY_SIZE(av_perm_to_string), -	class_to_string, -	ARRAY_SIZE(class_to_string), -	av_inherit, -	ARRAY_SIZE(av_inherit) +	.av_perm_to_string = av_perm_to_string, +	.av_pts_len = ARRAY_SIZE(av_perm_to_string), +	.class_to_string = class_to_string, +	.cts_len = ARRAY_SIZE(class_to_string), +	.av_inherit = av_inherit, +	.av_inherit_len = ARRAY_SIZE(av_inherit)  };  #define AVC_CACHE_SLOTS			512 diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index dbeaa783b2a..00815973d41 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1433,12 +1433,13 @@ static int current_has_perm(const struct task_struct *tsk,  /* Check whether a task is allowed to use a capability. */  static int task_has_capability(struct task_struct *tsk, +			       const struct cred *cred,  			       int cap, int audit)  {  	struct avc_audit_data ad;  	struct av_decision avd;  	u16 sclass; -	u32 sid = task_sid(tsk); +	u32 sid = cred_sid(cred);  	u32 av = CAP_TO_MASK(cap);  	int rc; @@ -1865,15 +1866,16 @@ static int selinux_capset(struct cred *new, const struct cred *old,  	return cred_has_perm(old, new, PROCESS__SETCAP);  } -static int selinux_capable(struct task_struct *tsk, int cap, int audit) +static int selinux_capable(struct task_struct *tsk, const struct cred *cred, +			   int cap, int audit)  {  	int rc; -	rc = secondary_ops->capable(tsk, cap, audit); +	rc = secondary_ops->capable(tsk, cred, cap, audit);  	if (rc)  		return rc; -	return task_has_capability(tsk, cap, audit); +	return task_has_capability(tsk, cred, cap, audit);  }  static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) @@ -2037,7 +2039,8 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)  {  	int rc, cap_sys_admin = 0; -	rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT); +	rc = selinux_capable(current, current_cred(), CAP_SYS_ADMIN, +			     SECURITY_CAP_NOAUDIT);  	if (rc == 0)  		cap_sys_admin = 1; @@ -2880,7 +2883,8 @@ 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 = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT); +	error = selinux_capable(current, current_cred(), CAP_MAC_ADMIN, +				SECURITY_CAP_NOAUDIT);  	if (!error)  		error = security_sid_to_context_force(isec->sid, &context,  						      &size); @@ -4185,7 +4189,7 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,  static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,  				       u16 family)  { -	int err; +	int err = 0;  	struct sk_security_struct *sksec = sk->sk_security;  	u32 peer_sid;  	u32 sk_sid = sksec->sid; @@ -4202,7 +4206,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,  	if (selinux_compat_net)  		err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,  							   family, addrp); -	else +	else if (selinux_secmark_enabled())  		err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,  				   PACKET__RECV, &ad);  	if (err) @@ -4705,7 +4709,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,  		if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,  							 &ad, family, addrp))  			return NF_DROP; -	} else { +	} else if (selinux_secmark_enabled()) {  		if (avc_has_perm(sksec->sid, skb->secmark,  				 SECCLASS_PACKET, PACKET__SEND, &ad))  			return NF_DROP; diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h index c0d314d9f8e..bb1ec801bdf 100644 --- a/security/selinux/include/avc_ss.h +++ b/security/selinux/include/avc_ss.h @@ -17,16 +17,16 @@ struct av_perm_to_string {  };  struct av_inherit { -	u16 tclass;  	const char **common_pts;  	u32 common_base; +	u16 tclass;  };  struct selinux_class_perm {  	const struct av_perm_to_string *av_perm_to_string;  	u32 av_pts_len; -	const char **class_to_string;  	u32 cts_len; +	const char **class_to_string;  	const struct av_inherit *av_inherit;  	u32 av_inherit_len;  }; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 8f612c8becb..01ec6d2c6b9 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -47,13 +47,7 @@ static char *policycap_names[] = {  unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; -#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT -#define SELINUX_COMPAT_NET_VALUE 0 -#else -#define SELINUX_COMPAT_NET_VALUE 1 -#endif - -int selinux_compat_net = SELINUX_COMPAT_NET_VALUE; +int selinux_compat_net = 0;  static int __init checkreqprot_setup(char *str)  { @@ -494,7 +488,13 @@ static ssize_t sel_write_compat_net(struct file *file, const char __user *buf,  	if (sscanf(page, "%d", &new_value) != 1)  		goto out; -	selinux_compat_net = new_value ? 1 : 0; +	if (new_value) { +		printk(KERN_NOTICE +		       "SELinux: compat_net is deprecated, please use secmark" +		       " instead\n"); +		selinux_compat_net = 1; +	} else +		selinux_compat_net = 0;  	length = count;  out:  	free_page((unsigned long) page); diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 658c2bd17da..d9dd7a2f6a8 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -27,9 +27,9 @@ struct context {  	u32 user;  	u32 role;  	u32 type; +	u32 len;        /* length of string in bytes */  	struct mls_range range;  	char *str;	/* string representation if context cannot be mapped. */ -	u32 len;        /* length of string in bytes */  };  static inline void mls_context_init(struct context *c) diff --git a/security/smack/smack.h b/security/smack/smack.h index 31dce559595..b79582e4fbf 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -16,6 +16,7 @@  #include <linux/capability.h>  #include <linux/spinlock.h>  #include <linux/security.h> +#include <linux/in.h>  #include <net/netlabel.h>  /* @@ -39,6 +40,7 @@ struct superblock_smack {  struct socket_smack {  	char		*smk_out;			/* outbound label */  	char		*smk_in;			/* inbound label */ +	int		smk_labeled;			/* label scheme */  	char		smk_packet[SMK_LABELLEN];	/* TCP peer label */  }; @@ -80,6 +82,16 @@ struct smack_cipso {  };  /* + * An entry in the table identifying hosts. + */ +struct smk_netlbladdr { +	struct smk_netlbladdr	*smk_next; +	struct sockaddr_in	smk_host;	/* network address */ +	struct in_addr		smk_mask;	/* network mask */ +	char			*smk_label;	/* label */ +}; + +/*   * This is the repository for labels seen so that it is   * not necessary to keep allocating tiny chuncks of memory   * and so that they can be shared. @@ -127,6 +139,20 @@ struct smack_known {  #define XATTR_NAME_SMACKIPOUT	XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT  /* + * How communications on this socket are treated. + * Usually it's determined by the underlying netlabel code + * but there are certain cases, including single label hosts + * and potentially single label interfaces for which the + * treatment can not be known in advance. + * + * The possibility of additional labeling schemes being + * introduced in the future exists as well. + */ +#define SMACK_UNLABELED_SOCKET	0 +#define SMACK_CIPSO_SOCKET	1 + +/* + * smackfs magic number   * smackfs macic number   */  #define SMACK_MAGIC	0x43415d53 /* "SMAC" */ @@ -141,6 +167,7 @@ struct smack_known {   * CIPSO defaults.   */  #define SMACK_CIPSO_DOI_DEFAULT		3	/* Historical */ +#define SMACK_CIPSO_DOI_INVALID		-1	/* Not a DOI */  #define SMACK_CIPSO_DIRECT_DEFAULT	250	/* Arbitrary */  #define SMACK_CIPSO_MAXCATVAL		63	/* Bigger gets harder */  #define SMACK_CIPSO_MAXLEVEL            255     /* CIPSO 2.2 standard */ @@ -176,7 +203,6 @@ u32 smack_to_secid(const char *);   * Shared data.   */  extern int smack_cipso_direct; -extern int smack_net_nltype;  extern char *smack_net_ambient;  extern char *smack_onlycap; @@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat;  extern struct smack_known smack_known_huh;  extern struct smack_known smack_known_invalid;  extern struct smack_known smack_known_star; -extern struct smack_known smack_known_unset; +extern struct smack_known smack_known_web;  extern struct smk_list_entry *smack_list; +extern struct smk_netlbladdr *smack_netlbladdrs;  extern struct security_operations smack_ops;  /* diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 247cec3b5a4..2e0b83e77ff 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -15,15 +15,8 @@  #include <linux/sched.h>  #include "smack.h" -struct smack_known smack_known_unset = { -	.smk_next	= NULL, -	.smk_known	= "UNSET", -	.smk_secid	= 1, -	.smk_cipso	= NULL, -}; -  struct smack_known smack_known_huh = { -	.smk_next	= &smack_known_unset, +	.smk_next	= NULL,  	.smk_known	= "?",  	.smk_secid	= 2,  	.smk_cipso	= NULL, @@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = {  	.smk_cipso	= NULL,  }; -struct smack_known *smack_known = &smack_known_invalid; +struct smack_known smack_known_web = { +	.smk_next	= &smack_known_invalid, +	.smk_known	= "@", +	.smk_secid	= 7, +	.smk_cipso	= NULL, +}; + +struct smack_known *smack_known = &smack_known_web;  /*   * The initial value needs to be bigger than any of the @@ -99,6 +99,16 @@ int smk_access(char *subject_label, char *object_label, int request)  	    strcmp(subject_label, smack_known_star.smk_known) == 0)  		return -EACCES;  	/* +	 * An internet object can be accessed by any subject. +	 * Tasks cannot be assigned the internet label. +	 * An internet subject can access any object. +	 */ +	if (object_label == smack_known_web.smk_known || +	    subject_label == smack_known_web.smk_known || +	    strcmp(object_label, smack_known_web.smk_known) == 0 || +	    strcmp(subject_label, smack_known_web.smk_known) == 0) +		return 0; +	/*  	 * A star object can be accessed by any subject.  	 */  	if (object_label == smack_known_star.smk_known || diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 848212fd484..0278bc08304 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1277,6 +1277,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)  	ssp->smk_in = csp;  	ssp->smk_out = csp; +	ssp->smk_labeled = SMACK_CIPSO_SOCKET;  	ssp->smk_packet[0] = '\0';  	sk->sk_security = ssp; @@ -1341,45 +1342,69 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)  	struct smack_cipso cipso;  	int rc; -	switch (smack_net_nltype) { -	case NETLBL_NLTYPE_CIPSOV4: -		nlsp->domain = smack; -		nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; +	nlsp->domain = smack; +	nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; -		rc = smack_to_cipso(smack, &cipso); -		if (rc == 0) { -			nlsp->attr.mls.lvl = cipso.smk_level; -			smack_set_catset(cipso.smk_catset, nlsp); -		} else { -			nlsp->attr.mls.lvl = smack_cipso_direct; -			smack_set_catset(smack, nlsp); -		} -		break; -	default: -		break; +	rc = smack_to_cipso(smack, &cipso); +	if (rc == 0) { +		nlsp->attr.mls.lvl = cipso.smk_level; +		smack_set_catset(cipso.smk_catset, nlsp); +	} else { +		nlsp->attr.mls.lvl = smack_cipso_direct; +		smack_set_catset(smack, nlsp);  	}  }  /**   * smack_netlabel - Set the secattr on a socket   * @sk: the socket + * @labeled: socket label scheme   *   * Convert the outbound smack value (smk_out) to a   * secattr and attach it to the socket.   *   * Returns 0 on success or an error code   */ -static int smack_netlabel(struct sock *sk) +static int smack_netlabel(struct sock *sk, int labeled)  {  	struct socket_smack *ssp;  	struct netlbl_lsm_secattr secattr; -	int rc; +	int rc = 0;  	ssp = sk->sk_security; -	netlbl_secattr_init(&secattr); -	smack_to_secattr(ssp->smk_out, &secattr); -	rc = netlbl_sock_setattr(sk, &secattr); -	netlbl_secattr_destroy(&secattr); +	/* +	 * Usually the netlabel code will handle changing the +	 * packet labeling based on the label. +	 * The case of a single label host is different, because +	 * a single label host should never get a labeled packet +	 * even though the label is usually associated with a packet +	 * label. +	 */ +	local_bh_disable(); +	bh_lock_sock_nested(sk); + +	if (ssp->smk_out == smack_net_ambient || +	    labeled == SMACK_UNLABELED_SOCKET) +		netlbl_sock_delattr(sk); +	else { +		netlbl_secattr_init(&secattr); +		smack_to_secattr(ssp->smk_out, &secattr); +		rc = netlbl_sock_setattr(sk, &secattr); +		netlbl_secattr_destroy(&secattr); +	} + +	bh_unlock_sock(sk); +	local_bh_enable(); +	/* +	 * Remember the label scheme used so that it is not +	 * necessary to do the netlabel setting if it has not +	 * changed the next time through. +	 * +	 * The -EDESTADDRREQ case is an indication that there's +	 * a single level host involved. +	 */ +	if (rc == 0) +		ssp->smk_labeled = labeled;  	return rc;  } @@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,  		ssp->smk_in = sp;  	else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {  		ssp->smk_out = sp; -		rc = smack_netlabel(sock->sk); +		rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);  		if (rc != 0)  			printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",  			       __func__, -rc); @@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family,  	/*  	 * Set the outbound netlbl.  	 */ -	return smack_netlabel(sock->sk); +	return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); +} + + +/** + * smack_host_label - check host based restrictions + * @sip: the object end + * + * looks for host based access restrictions + * + * This version will only be appropriate for really small + * sets of single label hosts. Because of the masking + * it cannot shortcut out on the first match. There are + * numerious ways to address the problem, but none of them + * have been applied here. + * + * Returns the label of the far end or NULL if it's not special. + */ +static char *smack_host_label(struct sockaddr_in *sip) +{ +	struct smk_netlbladdr *snp; +	char *bestlabel = NULL; +	struct in_addr *siap = &sip->sin_addr; +	struct in_addr *liap; +	struct in_addr *miap; +	struct in_addr bestmask; + +	if (siap->s_addr == 0) +		return NULL; + +	bestmask.s_addr = 0; + +	for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) { +		liap = &snp->smk_host.sin_addr; +		miap = &snp->smk_mask; +		/* +		 * If the addresses match after applying the list entry mask +		 * the entry matches the address. If it doesn't move along to +		 * the next entry. +		 */ +		if ((liap->s_addr & miap->s_addr) != +		    (siap->s_addr & miap->s_addr)) +			continue; +		/* +		 * If the list entry mask identifies a single address +		 * it can't get any more specific. +		 */ +		if (miap->s_addr == 0xffffffff) +			return snp->smk_label; +		/* +		 * If the list entry mask is less specific than the best +		 * already found this entry is uninteresting. +		 */ +		if ((miap->s_addr | bestmask.s_addr) == bestmask.s_addr) +			continue; +		/* +		 * This is better than any entry found so far. +		 */ +		bestmask.s_addr = miap->s_addr; +		bestlabel = snp->smk_label; +	} + +	return bestlabel; +} + +/** + * smack_socket_connect - connect access check + * @sock: the socket + * @sap: the other end + * @addrlen: size of sap + * + * Verifies that a connection may be possible + * + * Returns 0 on success, and error code otherwise + */ +static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, +				int addrlen) +{ +	struct socket_smack *ssp = sock->sk->sk_security; +	char *hostsp; +	int rc; + +	if (sock->sk == NULL || sock->sk->sk_family != PF_INET) +		return 0; + +	if (addrlen < sizeof(struct sockaddr_in)) +		return -EINVAL; + +	hostsp = smack_host_label((struct sockaddr_in *)sap); +	if (hostsp == NULL) { +		if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) +			return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); +		return 0; +	} + +	rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); +	if (rc != 0) +		return rc; + +	if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) +		return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); +	return 0;  }  /** @@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name,  	if (newsmack == NULL)  		return -EINVAL; +	/* +	 * No process is ever allowed the web ("@") label. +	 */ +	if (newsmack == smack_known_web.smk_known) +		return -EPERM; +  	new = prepare_creds(); -	if (!new) +	if (new == NULL)  		return -ENOMEM;  	new->security = newsmack;  	commit_creds(new); @@ -2144,6 +2276,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)  }  /** + * smack_socket_sendmsg - Smack check based on destination host + * @sock: the socket + * @msghdr: the message + * @size: the size of the message + * + * Return 0 if the current subject can write to the destination + * host. This is only a question if the destination is a single + * label host. + */ +static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, +				int size) +{ +	struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; +	struct socket_smack *ssp = sock->sk->sk_security; +	char *hostsp; +	int rc; + +	/* +	 * Perfectly reasonable for this to be NULL +	 */ +	if (sip == NULL || sip->sin_family != PF_INET) +		return 0; + +	hostsp = smack_host_label(sip); +	if (hostsp == NULL) { +		if (ssp->smk_labeled != SMACK_CIPSO_SOCKET) +			return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); +		return 0; +	} + +	rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE); +	if (rc != 0) +		return rc; + +	if (ssp->smk_labeled != SMACK_UNLABELED_SOCKET) +		return smack_netlabel(sock->sk, SMACK_UNLABELED_SOCKET); + +	return 0; + +} + + +/**   * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat   * 	pair to smack   * @sap: netlabel secattr @@ -2154,44 +2329,66 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)  static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)  {  	char smack[SMK_LABELLEN]; +	char *sp;  	int pcat; -	if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) { +	if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {  		/* +		 * Looks like a CIPSO packet.  		 * If there are flags but no level netlabel isn't  		 * behaving the way we expect it to.  		 * +		 * Get the categories, if any  		 * Without guidance regarding the smack value  		 * for the packet fall back on the network  		 * ambient value.  		 */ -		strncpy(sip, smack_net_ambient, SMK_MAXLEN); +		memset(smack, '\0', SMK_LABELLEN); +		if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) +			for (pcat = -1;;) { +				pcat = netlbl_secattr_catmap_walk( +					sap->attr.mls.cat, pcat + 1); +				if (pcat < 0) +					break; +				smack_catset_bit(pcat, smack); +			} +		/* +		 * If it is CIPSO using smack direct mapping +		 * we are already done. WeeHee. +		 */ +		if (sap->attr.mls.lvl == smack_cipso_direct) { +			memcpy(sip, smack, SMK_MAXLEN); +			return; +		} +		/* +		 * Look it up in the supplied table if it is not +		 * a direct mapping. +		 */ +		smack_from_cipso(sap->attr.mls.lvl, smack, sip);  		return;  	} -	/* -	 * Get the categories, if any -	 */ -	memset(smack, '\0', SMK_LABELLEN); -	if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) -		for (pcat = -1;;) { -			pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat, -							  pcat + 1); -			if (pcat < 0) -				break; -			smack_catset_bit(pcat, smack); -		} -	/* -	 * If it is CIPSO using smack direct mapping -	 * we are already done. WeeHee. -	 */ -	if (sap->attr.mls.lvl == smack_cipso_direct) { -		memcpy(sip, smack, SMK_MAXLEN); +	if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { +		/* +		 * Looks like a fallback, which gives us a secid. +		 */ +		sp = smack_from_secid(sap->attr.secid); +		/* +		 * This has got to be a bug because it is +		 * impossible to specify a fallback without +		 * specifying the label, which will ensure +		 * it has a secid, and the only way to get a +		 * secid is from a fallback. +		 */ +		BUG_ON(sp == NULL); +		strncpy(sip, sp, SMK_MAXLEN);  		return;  	}  	/* -	 * Look it up in the supplied table if it is not a direct mapping. +	 * Without guidance regarding the smack value +	 * for the packet fall back on the network +	 * ambient value.  	 */ -	smack_from_cipso(sap->attr.mls.lvl, smack, sip); +	strncpy(sip, smack_net_ambient, SMK_MAXLEN);  	return;  } @@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	struct netlbl_lsm_secattr secattr;  	struct socket_smack *ssp = sk->sk_security;  	char smack[SMK_LABELLEN]; +	char *csp;  	int rc;  	if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) @@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)  	/*  	 * Translate what netlabel gave us.  	 */ -	memset(smack, '\0', SMK_LABELLEN);  	netlbl_secattr_init(&secattr); +  	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); -	if (rc == 0) +	if (rc == 0) {  		smack_from_secattr(&secattr, smack); -	else -		strncpy(smack, smack_net_ambient, SMK_MAXLEN); +		csp = smack; +	} else +		csp = smack_net_ambient; +  	netlbl_secattr_destroy(&secattr); +  	/*  	 * Receiving a packet requires that the other end  	 * be able to write here. Read access is not required.  	 * This is the simplist possible security model  	 * for networking.  	 */ -	rc = smk_access(smack, ssp->smk_in, MAY_WRITE); +	rc = smk_access(csp, ssp->smk_in, MAY_WRITE);  	if (rc != 0)  		netlbl_skbuff_err(skb, rc, 0);  	return rc; @@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,  	/*  	 * Translate what netlabel gave us.  	 */ -	memset(smack, '\0', SMK_LABELLEN);  	netlbl_secattr_init(&secattr);  	rc = netlbl_skbuff_getattr(skb, family, &secattr);  	if (rc == 0) @@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)  	ssp->smk_in = ssp->smk_out = current_security();  	ssp->smk_packet[0] = '\0'; -	rc = smack_netlabel(sk); +	rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET);  	if (rc != 0)  		printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",  		       __func__, -rc); @@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,  	if (skb == NULL)  		return -EACCES; -	memset(smack, '\0', SMK_LABELLEN);  	netlbl_secattr_init(&skb_secattr);  	rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);  	if (rc == 0) @@ -2732,6 +2931,8 @@ struct security_operations smack_ops = {  	.unix_may_send = 		smack_unix_may_send,  	.socket_post_create = 		smack_socket_post_create, +	.socket_connect =		smack_socket_connect, +	.socket_sendmsg =		smack_socket_sendmsg,  	.socket_sock_rcv_skb = 		smack_socket_sock_rcv_skb,  	.socket_getpeersec_stream =	smack_socket_getpeersec_stream,  	.socket_getpeersec_dgram =	smack_socket_getpeersec_dgram, @@ -2783,7 +2984,6 @@ static __init int smack_init(void)  	/*  	 * Initialize locks  	 */ -	spin_lock_init(&smack_known_unset.smk_cipsolock);  	spin_lock_init(&smack_known_huh.smk_cipsolock);  	spin_lock_init(&smack_known_hat.smk_cipsolock);  	spin_lock_init(&smack_known_star.smk_cipsolock); diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 247dc9ebbc7..bf107a389ac 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -20,6 +20,7 @@  #include <linux/vmalloc.h>  #include <linux/security.h>  #include <linux/mutex.h> +#include <net/net_namespace.h>  #include <net/netlabel.h>  #include <net/cipso_ipv4.h>  #include <linux/seq_file.h> @@ -38,7 +39,7 @@ enum smk_inos {  	SMK_DOI		= 5,	/* CIPSO DOI */  	SMK_DIRECT	= 6,	/* CIPSO level indicating direct label */  	SMK_AMBIENT	= 7,	/* internet ambient label */ -	SMK_NLTYPE	= 8,	/* label scheme to use by default */ +	SMK_NETLBLADDR	= 8,	/* single label hosts */  	SMK_ONLYCAP	= 9,	/* the only "capable" label */  }; @@ -48,6 +49,7 @@ enum smk_inos {  static DEFINE_MUTEX(smack_list_lock);  static DEFINE_MUTEX(smack_cipso_lock);  static DEFINE_MUTEX(smack_ambient_lock); +static DEFINE_MUTEX(smk_netlbladdr_lock);  /*   * This is the "ambient" label for network traffic. @@ -57,12 +59,6 @@ static DEFINE_MUTEX(smack_ambient_lock);  char *smack_net_ambient = smack_known_floor.smk_known;  /* - * This is the default packet marking scheme for network traffic. - * It can be reset via smackfs/nltype - */ -int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4; - -/*   * This is the level in a CIPSO header that indicates a   * smack label is contained directly in the category set.   * It can be reset via smackfs/direct @@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;   */  char *smack_onlycap; +/* + * Certain IP addresses may be designated as single label hosts. + * Packets are sent there unlabeled, but only from tasks that + * can write to the specified label. + */ +struct smk_netlbladdr *smack_netlbladdrs; +  static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;  struct smk_list_entry *smack_list; @@ -104,6 +107,24 @@ struct smk_list_entry *smack_list;  #define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)  #define SMK_LOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) +/** + * smk_netlabel_audit_set - fill a netlbl_audit struct + * @nap: structure to fill + */ +static void smk_netlabel_audit_set(struct netlbl_audit *nap) +{ +	nap->loginuid = audit_get_loginuid(current); +	nap->sessionid = audit_get_sessionid(current); +	nap->secid = smack_to_secid(current_security()); +} + +/* + * Values for parsing single label host rules + * "1.2.3.4 X" + * "192.168.138.129/32 abcdefghijklmnopqrstuvw" + */ +#define SMK_NETLBLADDRMIN	9 +#define SMK_NETLBLADDRMAX	42  /*   * Seq_file read operations for /smack/load @@ -344,13 +365,11 @@ static void smk_cipso_doi(void)  {  	int rc;  	struct cipso_v4_doi *doip; -	struct netlbl_audit audit_info; +	struct netlbl_audit nai; -	audit_info.loginuid = audit_get_loginuid(current); -	audit_info.sessionid = audit_get_sessionid(current); -	audit_info.secid = smack_to_secid(current_security()); +	smk_netlabel_audit_set(&nai); -	rc = netlbl_cfg_map_del(NULL, &audit_info); +	rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);  	if (rc != 0)  		printk(KERN_WARNING "%s:%d remove rc = %d\n",  		       __func__, __LINE__, rc); @@ -365,11 +384,19 @@ static void smk_cipso_doi(void)  	for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)  		doip->tags[rc] = CIPSO_V4_TAG_INVALID; -	rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info); +	rc = netlbl_cfg_cipsov4_add(doip, &nai);  	if (rc != 0) { -		printk(KERN_WARNING "%s:%d add rc = %d\n", +		printk(KERN_WARNING "%s:%d cipso add rc = %d\n",  		       __func__, __LINE__, rc);  		kfree(doip); +		return; +	} +	rc = netlbl_cfg_cipsov4_map_add(doip->doi, NULL, NULL, NULL, &nai); +	if (rc != 0) { +		printk(KERN_WARNING "%s:%d map add rc = %d\n", +		       __func__, __LINE__, rc); +		kfree(doip); +		return;  	}  } @@ -379,20 +406,19 @@ static void smk_cipso_doi(void)  static void smk_unlbl_ambient(char *oldambient)  {  	int rc; -	struct netlbl_audit audit_info; +	struct netlbl_audit nai; -	audit_info.loginuid = audit_get_loginuid(current); -	audit_info.sessionid = audit_get_sessionid(current); -	audit_info.secid = smack_to_secid(current_security()); +	smk_netlabel_audit_set(&nai);  	if (oldambient != NULL) { -		rc = netlbl_cfg_map_del(oldambient, &audit_info); +		rc = netlbl_cfg_map_del(oldambient, PF_INET, NULL, NULL, &nai);  		if (rc != 0)  			printk(KERN_WARNING "%s:%d remove rc = %d\n",  			       __func__, __LINE__, rc);  	} -	rc = netlbl_cfg_unlbl_add_map(smack_net_ambient, &audit_info); +	rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, +				      NULL, NULL, &nai);  	if (rc != 0)  		printk(KERN_WARNING "%s:%d add rc = %d\n",  		       __func__, __LINE__, rc); @@ -603,6 +629,201 @@ static const struct file_operations smk_cipso_ops = {  	.release        = seq_release,  }; +/* + * Seq_file read operations for /smack/netlabel + */ + +static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos) +{ +	if (*pos == SEQ_READ_FINISHED) +		return NULL; + +	return smack_netlbladdrs; +} + +static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ +	struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next; + +	if (skp == NULL) +		*pos = SEQ_READ_FINISHED; + +	return skp; +} +/* +#define BEMASK	0x80000000 +*/ +#define BEMASK	0x00000001 +#define BEBITS	(sizeof(__be32) * 8) + +/* + * Print host/label pairs + */ +static int netlbladdr_seq_show(struct seq_file *s, void *v) +{ +	struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v; +	unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr; +	__be32 bebits; +	int maskn = 0; + +	for (bebits = BEMASK; bebits != 0; maskn++, bebits <<= 1) +		if ((skp->smk_mask.s_addr & bebits) == 0) +			break; + +	seq_printf(s, "%u.%u.%u.%u/%d %s\n", +		hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label); + +	return 0; +} + +static void netlbladdr_seq_stop(struct seq_file *s, void *v) +{ +	/* No-op */ +} + +static struct seq_operations netlbladdr_seq_ops = { +	.start = netlbladdr_seq_start, +	.stop  = netlbladdr_seq_stop, +	.next  = netlbladdr_seq_next, +	.show  = netlbladdr_seq_show, +}; + +/** + * smk_open_netlbladdr - open() for /smack/netlabel + * @inode: inode structure representing file + * @file: "netlabel" file pointer + * + * Connect our netlbladdr_seq_* operations with /smack/netlabel + * file_operations + */ +static int smk_open_netlbladdr(struct inode *inode, struct file *file) +{ +	return seq_open(file, &netlbladdr_seq_ops); +} + +/** + * smk_write_netlbladdr - write() for /smack/netlabel + * @filp: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Accepts only one netlbladdr per write call. + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, +				size_t count, loff_t *ppos) +{ +	struct smk_netlbladdr *skp; +	struct sockaddr_in newname; +	char smack[SMK_LABELLEN]; +	char *sp; +	char data[SMK_NETLBLADDRMAX]; +	char *host = (char *)&newname.sin_addr.s_addr; +	int rc; +	struct netlbl_audit audit_info; +	struct in_addr mask; +	unsigned int m; +	__be32 bebits = BEMASK; +	__be32 nsa; + +	/* +	 * Must have privilege. +	 * No partial writes. +	 * Enough data must be present. +	 * "<addr/mask, as a.b.c.d/e><space><label>" +	 * "<addr, as a.b.c.d><space><label>" +	 */ +	if (!capable(CAP_MAC_ADMIN)) +		return -EPERM; +	if (*ppos != 0) +		return -EINVAL; +	if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX) +		return -EINVAL; +	if (copy_from_user(data, buf, count) != 0) +		return -EFAULT; + +	data[count] = '\0'; + +	rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s", +		&host[0], &host[1], &host[2], &host[3], &m, smack); +	if (rc != 6) { +		rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", +			&host[0], &host[1], &host[2], &host[3], smack); +		if (rc != 5) +			return -EINVAL; +		m = BEBITS; +	} +	if (m > BEBITS) +		return -EINVAL; + +	sp = smk_import(smack, 0); +	if (sp == NULL) +		return -EINVAL; + +	for (mask.s_addr = 0; m > 0; m--) { +		mask.s_addr |= bebits; +		bebits <<= 1; +	} +	/* +	 * Only allow one writer at a time. Writes should be +	 * quite rare and small in any case. +	 */ +	mutex_lock(&smk_netlbladdr_lock); + +	nsa = newname.sin_addr.s_addr; +	for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next) +		if (skp->smk_host.sin_addr.s_addr == nsa && +		    skp->smk_mask.s_addr == mask.s_addr) +			break; + +	smk_netlabel_audit_set(&audit_info); + +	if (skp == NULL) { +		skp = kzalloc(sizeof(*skp), GFP_KERNEL); +		if (skp == NULL) +			rc = -ENOMEM; +		else { +			rc = 0; +			skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr; +			skp->smk_mask.s_addr = mask.s_addr; +			skp->smk_next = smack_netlbladdrs; +			skp->smk_label = sp; +			smack_netlbladdrs = skp; +		} +	} else { +		rc = netlbl_cfg_unlbl_static_del(&init_net, NULL, +			&skp->smk_host.sin_addr, &skp->smk_mask, +			PF_INET, &audit_info); +		skp->smk_label = sp; +	} + +	/* +	 * Now tell netlabel about the single label nature of +	 * this host so that incoming packets get labeled. +	 */ + +	if (rc == 0) +		rc = netlbl_cfg_unlbl_static_add(&init_net, NULL, +			&skp->smk_host.sin_addr, &skp->smk_mask, PF_INET, +			smack_to_secid(skp->smk_label), &audit_info); + +	if (rc == 0) +		rc = count; + +	mutex_unlock(&smk_netlbladdr_lock); + +	return rc; +} + +static const struct file_operations smk_netlbladdr_ops = { +	.open           = smk_open_netlbladdr, +	.read		= seq_read, +	.llseek         = seq_lseek, +	.write		= smk_write_netlbladdr, +	.release        = seq_release, +}; +  /**   * smk_read_doi - read() for /smack/doi   * @filp: file pointer, not actually used @@ -891,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = {  	.write		= smk_write_onlycap,  }; -struct option_names { -	int	o_number; -	char	*o_name; -	char	*o_alias; -}; - -static struct option_names netlbl_choices[] = { -	{ NETLBL_NLTYPE_RIPSO, -		NETLBL_NLTYPE_RIPSO_NAME,	"ripso" }, -	{ NETLBL_NLTYPE_CIPSOV4, -		NETLBL_NLTYPE_CIPSOV4_NAME,	"cipsov4" }, -	{ NETLBL_NLTYPE_CIPSOV4, -		NETLBL_NLTYPE_CIPSOV4_NAME,	"cipso" }, -	{ NETLBL_NLTYPE_CIPSOV6, -		NETLBL_NLTYPE_CIPSOV6_NAME,	"cipsov6" }, -	{ NETLBL_NLTYPE_UNLABELED, -		NETLBL_NLTYPE_UNLABELED_NAME,	"unlabeled" }, -}; - -/** - * smk_read_nltype - read() for /smack/nltype - * @filp: file pointer, not actually used - * @buf: where to put the result - * @count: maximum to send along - * @ppos: where to start - * - * Returns number of bytes read or error code, as appropriate - */ -static ssize_t smk_read_nltype(struct file *filp, char __user *buf, -			       size_t count, loff_t *ppos) -{ -	char bound[40]; -	ssize_t rc; -	int i; - -	if (count < SMK_LABELLEN) -		return -EINVAL; - -	if (*ppos != 0) -		return 0; - -	sprintf(bound, "unknown"); - -	for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++) -		if (smack_net_nltype == netlbl_choices[i].o_number) { -			sprintf(bound, "%s", netlbl_choices[i].o_name); -			break; -		} - -	rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound)); - -	return rc; -} - -/** - * smk_write_nltype - write() for /smack/nltype - * @filp: file pointer, not actually used - * @buf: where to get the data from - * @count: bytes sent - * @ppos: where to start - * - * Returns number of bytes written or error code, as appropriate - */ -static ssize_t smk_write_nltype(struct file *file, const char __user *buf, -				size_t count, loff_t *ppos) -{ -	char bound[40]; -	char *cp; -	int i; - -	if (!capable(CAP_MAC_ADMIN)) -		return -EPERM; - -	if (count >= 40) -		return -EINVAL; - -	if (copy_from_user(bound, buf, count) != 0) -		return -EFAULT; - -	bound[count] = '\0'; -	cp = strchr(bound, ' '); -	if (cp != NULL) -		*cp = '\0'; -	cp = strchr(bound, '\n'); -	if (cp != NULL) -		*cp = '\0'; - -	for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++) -		if (strcmp(bound, netlbl_choices[i].o_name) == 0 || -		    strcmp(bound, netlbl_choices[i].o_alias) == 0) { -			smack_net_nltype = netlbl_choices[i].o_number; -			return count; -		} -	/* -	 * Not a valid choice. -	 */ -	return -EINVAL; -} - -static const struct file_operations smk_nltype_ops = { -	.read		= smk_read_nltype, -	.write		= smk_write_nltype, -}; -  /**   * smk_fill_super - fill the /smackfs superblock   * @sb: the empty superblock @@ -1021,8 +1138,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)  			{"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},  		[SMK_AMBIENT]	=  			{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, -		[SMK_NLTYPE]	= -			{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR}, +		[SMK_NETLBLADDR] = +			{"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},  		[SMK_ONLYCAP]	=  			{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},  		/* last one */ {""}  |