diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-07 01:11:39 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-07 01:11:39 (GMT) |
commit | c861ea2cb2c25c1698734d9b0540a09e253690a1 (patch) | |
tree | b83e5313ca07a3efbcbcdd7fe33e0f6ad6284493 | |
parent | 3610639d1fceb09cb418c65fcbe9136c31eee03a (diff) | |
parent | ac8cc0fa5395fe2278e305a4cbed48e90d88d878 (diff) | |
download | linux-fsl-qoriq-c861ea2cb2c25c1698734d9b0540a09e253690a1.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6:
CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #3]
Revert "CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #2]"
SELinux: shrink sizeof av_inhert selinux_class_perm and context
CRED: Fix regression in cap_capable() as shown up by sys_faccessat() [ver #2]
keys: fix sparse warning by adding __user annotation to cast
smack: Add support for unlabeled network hosts and networks
selinux: Deprecate and schedule the removal of the the compat_net functionality
netlabel: Update kernel configuration API
26 files changed, 1229 insertions, 423 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index df18d87..2193be5 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 e22f48c..02bdb76 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 b92b5e4..1f2ab63 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 9909774..bedc7f6 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 17c442a..749011e 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 c598d9d..688926e 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 e527990..6bb2635 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; + +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 0; + return ret_val; } /** diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index fff32b7..bf1ab1a 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 5fadf10..7a10bbe 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 bfcb676..0261dda 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 b32eceb..fd9229d 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 - 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_add_map - Add a new CIPSOv4 DOI definition and mapping - * @doi_def: the DOI definition + * 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 8c03080..f3c5c68 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 06b1301..7aba635 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 69fc995..7cd61a5 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 6688765..0979679 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 678d4d0..c3586c0 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 26301dd..bca1b74 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 d43bd6b..eb41f43 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 dbeaa78..0081597 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 c0d314d..bb1ec80 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 8f612c8..01ec6d2 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 658c2bd..d9dd7a2 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 31dce55..b79582e 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 247cec3..2e0b83e 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 848212f..0278bc0 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 247dc9e..bf107a3 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 */ {""} |