Commit 5c8c40be authored by James Morris's avatar James Morris

Merge branch 'master' of git://git.infradead.org/users/pcmoore/lblnet-2.6_next into next

parents 90bd49ab 6d3dc07c
...@@ -324,3 +324,15 @@ When: 2.6.29 (ideally) or 2.6.30 (more likely) ...@@ -324,3 +324,15 @@ When: 2.6.29 (ideally) or 2.6.30 (more likely)
Why: Deprecated by the new (standard) device driver binding model. Use Why: Deprecated by the new (standard) device driver binding model. Use
i2c_driver->probe() and ->remove() instead. i2c_driver->probe() and ->remove() instead.
Who: Jean Delvare <khali@linux-fr.org> 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>
...@@ -131,7 +131,8 @@ extern int cipso_v4_rbm_strictvalid; ...@@ -131,7 +131,8 @@ extern int cipso_v4_rbm_strictvalid;
*/ */
#ifdef CONFIG_NETLABEL #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); void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info); int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi); struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi);
...@@ -140,7 +141,8 @@ int cipso_v4_doi_walk(u32 *skip_cnt, ...@@ -140,7 +141,8 @@ int cipso_v4_doi_walk(u32 *skip_cnt,
int (*callback) (struct cipso_v4_doi *doi_def, void *arg), int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
void *cb_arg); void *cb_arg);
#else #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; return -ENOSYS;
} }
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/net.h> #include <linux/net.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <asm/atomic.h> #include <asm/atomic.h>
...@@ -353,13 +355,37 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr) ...@@ -353,13 +355,37 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr)
/* /*
* LSM configuration operations * LSM configuration operations
*/ */
int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info); int netlbl_cfg_map_del(const char *domain,
int netlbl_cfg_unlbl_add_map(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_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); struct netlbl_audit *audit_info);
int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, 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 char *domain,
const struct in_addr *addr,
const struct in_addr *mask,
struct netlbl_audit *audit_info); struct netlbl_audit *audit_info);
/* /*
* LSM security attribute operations * LSM security attribute operations
*/ */
...@@ -401,19 +427,62 @@ void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway); ...@@ -401,19 +427,62 @@ void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);
void netlbl_cache_invalidate(void); void netlbl_cache_invalidate(void);
int netlbl_cache_add(const struct sk_buff *skb, int netlbl_cache_add(const struct sk_buff *skb,
const struct netlbl_lsm_secattr *secattr); const struct netlbl_lsm_secattr *secattr);
/*
* Protocol engine operations
*/
struct audit_buffer *netlbl_audit_start(int type,
struct netlbl_audit *audit_info);
#else #else
static inline int netlbl_cfg_map_del(const char *domain, 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_map_add(const char *domain,
u16 family,
void *addr,
void *mask,
struct netlbl_audit *audit_info)
{
return -ENOSYS;
}
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) struct netlbl_audit *audit_info)
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int netlbl_cfg_unlbl_add_map(const char *domain, static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
struct netlbl_audit *audit_info) struct netlbl_audit *audit_info)
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, 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 char *domain,
const struct in_addr *addr,
const struct in_addr *mask,
struct netlbl_audit *audit_info) struct netlbl_audit *audit_info)
{ {
return -ENOSYS; return -ENOSYS;
...@@ -495,6 +564,11 @@ static inline int netlbl_cache_add(const struct sk_buff *skb, ...@@ -495,6 +564,11 @@ static inline int netlbl_cache_add(const struct sk_buff *skb,
{ {
return 0; return 0;
} }
static inline struct audit_buffer *netlbl_audit_start(int type,
struct netlbl_audit *audit_info)
{
return NULL;
}
#endif /* CONFIG_NETLABEL */ #endif /* CONFIG_NETLABEL */
#endif /* _NETLABEL_H */ #endif /* _NETLABEL_H */
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/audit.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/tcp.h> #include <net/tcp.h>
...@@ -449,6 +450,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) ...@@ -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 * cipso_v4_doi_add - Add a new DOI to the CIPSO protocol engine
* @doi_def: the DOI structure * @doi_def: the DOI structure
* @audit_info: NetLabel audit information
* *
* Description: * Description:
* The caller defines a new DOI for use by the CIPSO engine and calls this * 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) ...@@ -458,50 +460,78 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
* zero on success and non-zero on failure. * 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 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) 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++) { for (iter = 0; iter < CIPSO_V4_TAG_MAXCNT; iter++) {
switch (doi_def->tags[iter]) { switch (doi_def->tags[iter]) {
case CIPSO_V4_TAG_RBITMAP: case CIPSO_V4_TAG_RBITMAP:
break; break;
case CIPSO_V4_TAG_RANGE: 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: case CIPSO_V4_TAG_ENUM:
if (doi_def->type != CIPSO_V4_MAP_PASS) if (doi_def->type != CIPSO_V4_MAP_PASS)
return -EINVAL; goto doi_add_return;
break; break;
case CIPSO_V4_TAG_LOCAL: case CIPSO_V4_TAG_LOCAL:
if (doi_def->type != CIPSO_V4_MAP_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; break;
default: default:
return -EINVAL; goto doi_add_return;
} }
} }
atomic_set(&doi_def->refcount, 1); atomic_set(&doi_def->refcount, 1);
spin_lock(&cipso_v4_doi_list_lock); spin_lock(&cipso_v4_doi_list_lock);
if (cipso_v4_doi_search(doi_def->doi) != NULL) if (cipso_v4_doi_search(doi_def->doi) != NULL) {
goto doi_add_failure; 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); list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
spin_unlock(&cipso_v4_doi_list_lock); 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: return ret_val;
spin_unlock(&cipso_v4_doi_list_lock);
return -EEXIST;
} }
/** /**
...@@ -559,25 +589,39 @@ static void cipso_v4_doi_free_rcu(struct rcu_head *entry) ...@@ -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 cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
{ {
int ret_val;
struct cipso_v4_doi *doi_def; struct cipso_v4_doi *doi_def;
struct audit_buffer *audit_buf;
spin_lock(&cipso_v4_doi_list_lock); spin_lock(&cipso_v4_doi_list_lock);
doi_def = cipso_v4_doi_search(doi); doi_def = cipso_v4_doi_search(doi);
if (doi_def == NULL) { if (doi_def == NULL) {
spin_unlock(&cipso_v4_doi_list_lock); spin_unlock(&cipso_v4_doi_list_lock);
return -ENOENT; ret_val = -ENOENT;
goto doi_remove_return;
} }
if (!atomic_dec_and_test(&doi_def->refcount)) { if (!atomic_dec_and_test(&doi_def->refcount)) {
spin_unlock(&cipso_v4_doi_list_lock); spin_unlock(&cipso_v4_doi_list_lock);
return -EBUSY; ret_val = -EBUSY;
goto doi_remove_return;
} }
list_del_rcu(&doi_def->list); list_del_rcu(&doi_def->list);
spin_unlock(&cipso_v4_doi_list_lock); spin_unlock(&cipso_v4_doi_list_lock);
cipso_v4_cache_invalidate(); cipso_v4_cache_invalidate();
call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); 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;
} }
/** /**
......
...@@ -130,6 +130,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info, ...@@ -130,6 +130,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
/** /**
* netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block * @info: the Generic NETLINK info block
* @audit_info: NetLabel audit information
* *
* Description: * Description:
* Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD * 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, ...@@ -137,7 +138,8 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
* non-zero on error. * 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; int ret_val = -EINVAL;
struct cipso_v4_doi *doi_def = NULL; struct cipso_v4_doi *doi_def = NULL;
...@@ -316,7 +318,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) ...@@ -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) if (ret_val != 0)
goto add_std_failure; goto add_std_failure;
return 0; return 0;
...@@ -330,6 +332,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) ...@@ -330,6 +332,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
/** /**
* netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block * @info: the Generic NETLINK info block
* @audit_info: NetLabel audit information
* *
* Description: * Description:
* Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
...@@ -337,7 +340,8 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) ...@@ -337,7 +340,8 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
* error. * 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; int ret_val;
struct cipso_v4_doi *doi_def = NULL; struct cipso_v4_doi *doi_def = NULL;
...@@ -354,7 +358,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info) ...@@ -354,7 +358,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
if (ret_val != 0) if (ret_val != 0)
goto add_pass_failure; 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) if (ret_val != 0)
goto add_pass_failure; goto add_pass_failure;
return 0; return 0;
...@@ -367,6 +371,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info) ...@@ -367,6 +371,7 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
/** /**
* netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block * @info: the Generic NETLINK info block
* @audit_info: NetLabel audit information
* *
* Description: * Description:
* Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
...@@ -374,7 +379,8 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info) ...@@ -374,7 +379,8 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
* non-zero on error. * 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; int ret_val;
struct cipso_v4_doi *doi_def = NULL; struct cipso_v4_doi *doi_def = NULL;
...@@ -391,7 +397,7 @@ static int netlbl_cipsov4_add_local(struct genl_info *info) ...@@ -391,7 +397,7 @@ static int netlbl_cipsov4_add_local(struct genl_info *info)
if (ret_val != 0) if (ret_val != 0)
goto add_local_failure; 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) if (ret_val != 0)
goto add_local_failure; goto add_local_failure;
return 0; return 0;
...@@ -415,48 +421,31 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info) ...@@ -415,48 +421,31 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
{ {
int ret_val = -EINVAL; int ret_val = -EINVAL;
u32 type;
u32 doi;
const char *type_str = "(unknown)"; const char *type_str = "(unknown)";
struct audit_buffer *audit_buf;
struct netlbl_audit audit_info; struct netlbl_audit audit_info;
if (!info->attrs[NLBL_CIPSOV4_A_DOI] || if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
!info->attrs[NLBL_CIPSOV4_A_MTYPE]) !info->attrs[NLBL_CIPSOV4_A_MTYPE])
return -EINVAL; return -EINVAL;
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
netlbl_netlink_auditinfo(skb, &audit_info); netlbl_netlink_auditinfo(skb, &audit_info);
switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
switch (type) {
case CIPSO_V4_MAP_TRANS: case CIPSO_V4_MAP_TRANS:
type_str = "trans"; type_str = "trans";
ret_val = netlbl_cipsov4_add_std(info); ret_val = netlbl_cipsov4_add_std(info, &audit_info);
break; break;
case CIPSO_V4_MAP_PASS: case CIPSO_V4_MAP_PASS:
type_str = "pass"; type_str = "pass";
ret_val = netlbl_cipsov4_add_pass(info); ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
break; break;
case CIPSO_V4_MAP_LOCAL: case CIPSO_V4_MAP_LOCAL:
type_str = "local"; type_str = "local";
ret_val = netlbl_cipsov4_add_local(info); ret_val = netlbl_cipsov4_add_local(info, &audit_info);
break; break;
} }
if (ret_val == 0) if (ret_val == 0)
atomic_inc(&netlabel_mgmt_protocount); 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; return ret_val;
} }
...@@ -725,9 +714,7 @@ static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg) ...@@ -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) static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
{ {
int ret_val = -EINVAL; int ret_val = -EINVAL;
u32 doi = 0;
struct netlbl_domhsh_walk_arg cb_arg; struct netlbl_domhsh_walk_arg cb_arg;
struct audit_buffer *audit_buf;
struct netlbl_audit audit_info; struct netlbl_audit audit_info;
u32 skip_bkt = 0; u32 skip_bkt = 0;
u32 skip_chain = 0; u32 skip_chain = 0;
...@@ -735,29 +722,17 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) ...@@ -735,29 +722,17 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NLBL_CIPSOV4_A_DOI]) if (!info->attrs[NLBL_CIPSOV4_A_DOI])
return -EINVAL; return -EINVAL;
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
netlbl_netlink_auditinfo(skb, &audit_info); netlbl_netlink_auditinfo(skb, &audit_info);
cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
cb_arg.doi = doi;
cb_arg.audit_info = &audit_info; cb_arg.audit_info = &audit_info;
ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
netlbl_cipsov4_remove_cb, &cb_arg); netlbl_cipsov4_remove_cb, &cb_arg);
if (ret_val == 0 || ret_val == -ENOENT) { 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) if (ret_val == 0)
atomic_dec(&netlabel_mgmt_protocount); 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; return ret_val;
} }
......
...@@ -482,6 +482,73 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, ...@@ -482,6 +482,73 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
return ret_val; return ret_val;
} }
/**
* 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 * netlbl_domhsh_remove - Removes an entry from the domain hash table
* @domain: the domain to remove * @domain: the domain to remove
......
...@@ -90,6 +90,10 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry, ...@@ -90,6 +90,10 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info); struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info); 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(const char *domain, struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_default(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); struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
......
...@@ -31,7 +31,10 @@ ...@@ -31,7 +31,10 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h>
#include <net/netlabel.h> #include <net/netlabel.h>
#include <net/cipso_ipv4.h> #include <net/cipso_ipv4.h>
#include <asm/bug.h> #include <asm/bug.h>
...@@ -42,6 +45,7 @@ ...@@ -42,6 +45,7 @@
#include "netlabel_cipso_v4.h" #include "netlabel_cipso_v4.h"
#include "netlabel_user.h" #include "netlabel_user.h"
#include "netlabel_mgmt.h" #include "netlabel_mgmt.h"
#include "netlabel_addrlist.h"
/* /*
* Configuration Functions * Configuration Functions
...@@ -50,6 +54,9 @@ ...@@ -50,6 +54,9 @@
/** /**
* netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping * netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping
* @domain: the domain mapping to remove * @domain: the domain mapping to remove
* @family: address family
* @addr: IP address
* @mask: IP address mask
* @audit_info: NetLabel audit information * @audit_info: NetLabel audit information
* *
* Description: * Description:
...@@ -58,14 +65,32 @@ ...@@ -58,14 +65,32 @@
* values on failure. * 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)
{ {
if (addr == NULL && mask == NULL) {
return netlbl_domhsh_remove(domain, audit_info); 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 * @domain: the domain mapping to add
* @family: address family
* @addr: IP address
* @mask: IP address mask
* @audit_info: NetLabel audit information * @audit_info: NetLabel audit information
* *
* Description: * Description:
...@@ -74,11 +99,19 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info) ...@@ -74,11 +99,19 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
* negative values on failure. * 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) struct netlbl_audit *audit_info)
{ {
int ret_val = -ENOMEM; int ret_val = -ENOMEM;
struct netlbl_dom_map *entry; 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); entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL) if (entry == NULL)
...@@ -86,49 +119,225 @@ int netlbl_cfg_unlbl_add_map(const char *domain, ...@@ -86,49 +119,225 @@ int netlbl_cfg_unlbl_add_map(const char *domain,
if (domain != NULL) { if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC); entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL) 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; 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;
}
ret_val = netlbl_domhsh_add(entry, audit_info); ret_val = netlbl_domhsh_add(entry, audit_info);
if (ret_val != 0) if (ret_val != 0)
goto cfg_unlbl_add_map_failure; goto cfg_unlbl_map_add_failure;
return 0; return 0;
cfg_unlbl_add_map_failure: cfg_unlbl_map_add_failure:
if (entry != NULL) if (entry != NULL)
kfree(entry->domain); kfree(entry->domain);
kfree(entry); kfree(entry);
kfree(addrmap);
kfree(map4);
kfree(map6);
return ret_val; 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_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping * netlbl_cfg_unlbl_static_del - Removes an existing static label
* @doi_def: the DOI definition * @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_map_add - Add a new CIPSOv4 DOI mapping
* @doi: the CIPSO DOI
* @domain: the domain mapping to add * @domain: the domain mapping to add
* @addr: IP address
* @mask: IP address mask
* @audit_info: NetLabel audit information * @audit_info: NetLabel audit information
* *
* Description: * Description:
* Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this * Add a new NetLabel/LSM domain mapping for the given CIPSO DOI to the NetLabel
* new DOI definition to the NetLabel subsystem. A @domain value of NULL adds * subsystem. A @domain value of NULL adds a new default domain mapping.
* a new default domain mapping. Returns zero on success, negative values on * Returns zero on success, negative values on failure.
* 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 char *domain,
const struct in_addr *addr,
const struct in_addr *mask,
struct netlbl_audit *audit_info) struct netlbl_audit *audit_info)
{ {
int ret_val = -ENOMEM; int ret_val = -ENOMEM;
u32 doi; struct cipso_v4_doi *doi_def;
u32 doi_type;
struct netlbl_dom_map *entry; struct netlbl_dom_map *entry;
const char *type_str; struct netlbl_domaddr_map *addrmap = NULL;
struct audit_buffer *audit_buf; struct netlbl_domaddr4_map *addrinfo = NULL;
doi = doi_def->doi; doi_def = cipso_v4_doi_getdef(doi);
doi_type = doi_def->type; if (doi_def == NULL)
return -ENOENT;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL) if (entry == NULL)
...@@ -136,56 +345,52 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, ...@@ -136,56 +345,52 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
if (domain != NULL) { if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC); entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL) 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 (addr == NULL && mask == NULL) {
if (ret_val != 0) entry->type_def.cipsov4 = doi_def;
goto cfg_cipsov4_add_map_failure_remove_doi;
entry->type = NETLBL_NLTYPE_CIPSOV4; entry->type = NETLBL_NLTYPE_CIPSOV4;
entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi); } else if (addr != NULL && mask != NULL) {
if (entry->type_def.cipsov4 == NULL) { addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
ret_val = -ENOENT; if (addrmap == NULL)
goto cfg_cipsov4_add_map_failure_remove_doi; goto cfg_cipsov4_map_add_failure;
} INIT_LIST_HEAD(&addrmap->list4);
ret_val = netlbl_domhsh_add(entry, audit_info); 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) if (ret_val != 0)
goto cfg_cipsov4_add_map_failure_release_doi; goto cfg_cipsov4_map_add_failure;
cfg_cipsov4_add_map_return: entry->type_def.addrsel = addrmap;
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, entry->type = NETLBL_NLTYPE_ADDRSELECT;
audit_info); } else {
if (audit_buf != NULL) { ret_val = -EINVAL;
switch (doi_type) { goto cfg_cipsov4_map_add_failure;
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);
} }
return ret_val; ret_val = netlbl_domhsh_add(entry, audit_info);
if (ret_val != 0)
goto cfg_cipsov4_map_add_failure;
cfg_cipsov4_add_map_failure_release_doi: return 0;
cfg_cipsov4_map_add_failure:
cipso_v4_doi_putdef(doi_def); 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) if (entry != NULL)
kfree(entry->domain); kfree(entry->domain);
kfree(entry); kfree(entry);
goto cfg_cipsov4_add_map_return; kfree(addrmap);
kfree(addrinfo);
return ret_val;
} }
/* /*
...@@ -690,6 +895,28 @@ int netlbl_cache_add(const struct sk_buff *skb, ...@@ -690,6 +895,28 @@ int netlbl_cache_add(const struct sk_buff *skb,
return -ENOMSG; return -ENOMSG;
} }
/*
* 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 * Setup Functions
*/ */
......
...@@ -450,7 +450,7 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex) ...@@ -450,7 +450,7 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
* success, negative values on failure. * success, negative values on failure.
* *
*/ */
static int netlbl_unlhsh_add(struct net *net, int netlbl_unlhsh_add(struct net *net,
const char *dev_name, const char *dev_name,
const void *addr, const void *addr,
const void *mask, const void *mask,
...@@ -720,7 +720,7 @@ static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface) ...@@ -720,7 +720,7 @@ static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
* Returns zero on success, negative values on failure. * Returns zero on success, negative values on failure.
* *
*/ */
static int netlbl_unlhsh_remove(struct net *net, int netlbl_unlhsh_remove(struct net *net,
const char *dev_name, const char *dev_name,
const void *addr, const void *addr,
const void *mask, const void *mask,
......
...@@ -221,6 +221,21 @@ int netlbl_unlabel_genl_init(void); ...@@ -221,6 +221,21 @@ int netlbl_unlabel_genl_init(void);
/* General Unlabeled init function */ /* General Unlabeled init function */
int netlbl_unlabel_init(u32 size); 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 */ /* Process Unlabeled incoming network packets */
int netlbl_unlabel_getattr(const struct sk_buff *skb, int netlbl_unlabel_getattr(const struct sk_buff *skb,
u16 family, u16 family,
......
...@@ -94,33 +94,6 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE ...@@ -94,33 +94,6 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
If you are unsure how to answer this question, answer 1. 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 config SECURITY_SELINUX_POLICYDB_VERSION_MAX
bool "NSA SELinux maximum supported policy format version" bool "NSA SELinux maximum supported policy format version"
depends on SECURITY_SELINUX depends on SECURITY_SELINUX
......
...@@ -4185,7 +4185,7 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, ...@@ -4185,7 +4185,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, static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
u16 family) u16 family)
{ {
int err; int err = 0;
struct sk_security_struct *sksec = sk->sk_security; struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid; u32 peer_sid;
u32 sk_sid = sksec->sid; u32 sk_sid = sksec->sid;
...@@ -4202,7 +4202,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, ...@@ -4202,7 +4202,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
if (selinux_compat_net) if (selinux_compat_net)
err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad, err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,
family, addrp); family, addrp);
else else if (selinux_secmark_enabled())
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad); PACKET__RECV, &ad);
if (err) if (err)
...@@ -4705,7 +4705,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, ...@@ -4705,7 +4705,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
&ad, family, addrp)) &ad, family, addrp))
return NF_DROP; return NF_DROP;
} else { } else if (selinux_secmark_enabled()) {
if (avc_has_perm(sksec->sid, skb->secmark, if (avc_has_perm(sksec->sid, skb->secmark,
SECCLASS_PACKET, PACKET__SEND, &ad)) SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP; return NF_DROP;
......
...@@ -47,13 +47,7 @@ static char *policycap_names[] = { ...@@ -47,13 +47,7 @@ static char *policycap_names[] = {
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT int selinux_compat_net = 0;
#define SELINUX_COMPAT_NET_VALUE 0
#else
#define SELINUX_COMPAT_NET_VALUE 1
#endif
int selinux_compat_net = SELINUX_COMPAT_NET_VALUE;
static int __init checkreqprot_setup(char *str) 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, ...@@ -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) if (sscanf(page, "%d", &new_value) != 1)
goto out; 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; length = count;
out: out:
free_page((unsigned long) page); free_page((unsigned long) page);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/in.h>
#include <net/netlabel.h> #include <net/netlabel.h>
/* /*
...@@ -39,6 +40,7 @@ struct superblock_smack { ...@@ -39,6 +40,7 @@ struct superblock_smack {
struct socket_smack { struct socket_smack {
char *smk_out; /* outbound label */ char *smk_out; /* outbound label */
char *smk_in; /* inbound label */ char *smk_in; /* inbound label */
int smk_labeled; /* label scheme */
char smk_packet[SMK_LABELLEN]; /* TCP peer label */ char smk_packet[SMK_LABELLEN]; /* TCP peer label */
}; };
...@@ -79,6 +81,16 @@ struct smack_cipso { ...@@ -79,6 +81,16 @@ struct smack_cipso {
char smk_catset[SMK_LABELLEN]; char smk_catset[SMK_LABELLEN];
}; };
/*
* 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 * This is the repository for labels seen so that it is
* not necessary to keep allocating tiny chuncks of memory * not necessary to keep allocating tiny chuncks of memory
...@@ -127,6 +139,20 @@ struct smack_known { ...@@ -127,6 +139,20 @@ struct smack_known {
#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT #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 * smackfs macic number
*/ */
#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ #define SMACK_MAGIC 0x43415d53 /* "SMAC" */
...@@ -141,6 +167,7 @@ struct smack_known { ...@@ -141,6 +167,7 @@ struct smack_known {
* CIPSO defaults. * CIPSO defaults.
*/ */
#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ #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_DIRECT_DEFAULT 250 /* Arbitrary */
#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */ #define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
...@@ -176,7 +203,6 @@ u32 smack_to_secid(const char *); ...@@ -176,7 +203,6 @@ u32 smack_to_secid(const char *);
* Shared data. * Shared data.
*/ */
extern int smack_cipso_direct; extern int smack_cipso_direct;
extern int smack_net_nltype;
extern char *smack_net_ambient; extern char *smack_net_ambient;
extern char *smack_onlycap; extern char *smack_onlycap;
...@@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat; ...@@ -186,9 +212,10 @@ extern struct smack_known smack_known_hat;
extern struct smack_known smack_known_huh; extern struct smack_known smack_known_huh;
extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_invalid;
extern struct smack_known smack_known_star; 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_list_entry *smack_list;
extern struct smk_netlbladdr *smack_netlbladdrs;
extern struct security_operations smack_ops; extern struct security_operations smack_ops;
/* /*
......
...@@ -15,15 +15,8 @@ ...@@ -15,15 +15,8 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "smack.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 = { struct smack_known smack_known_huh = {
.smk_next = &smack_known_unset, .smk_next = NULL,
.smk_known = "?", .smk_known = "?",
.smk_secid = 2, .smk_secid = 2,
.smk_cipso = NULL, .smk_cipso = NULL,
...@@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = { ...@@ -57,7 +50,14 @@ struct smack_known smack_known_invalid = {
.smk_cipso = NULL, .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 * The initial value needs to be bigger than any of the
...@@ -98,6 +98,16 @@ int smk_access(char *subject_label, char *object_label, int request) ...@@ -98,6 +98,16 @@ int smk_access(char *subject_label, char *object_label, int request)
if (subject_label == smack_known_star.smk_known || if (subject_label == smack_known_star.smk_known ||
strcmp(subject_label, smack_known_star.smk_known) == 0) strcmp(subject_label, smack_known_star.smk_known) == 0)
return -EACCES; 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. * A star object can be accessed by any subject.
*/ */
......
...@@ -1277,6 +1277,7 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ...@@ -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_in = csp;
ssp->smk_out = csp; ssp->smk_out = csp;
ssp->smk_labeled = SMACK_CIPSO_SOCKET;
ssp->smk_packet[0] = '\0'; ssp->smk_packet[0] = '\0';
sk->sk_security = ssp; sk->sk_security = ssp;
...@@ -1341,8 +1342,6 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) ...@@ -1341,8 +1342,6 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
struct smack_cipso cipso; struct smack_cipso cipso;
int rc; int rc;
switch (smack_net_nltype) {
case NETLBL_NLTYPE_CIPSOV4:
nlsp->domain = smack; nlsp->domain = smack;
nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
...@@ -1354,32 +1353,58 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) ...@@ -1354,32 +1353,58 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
nlsp->attr.mls.lvl = smack_cipso_direct; nlsp->attr.mls.lvl = smack_cipso_direct;
smack_set_catset(smack, nlsp); smack_set_catset(smack, nlsp);
} }
break;
default:
break;
}
} }
/** /**
* smack_netlabel - Set the secattr on a socket * smack_netlabel - Set the secattr on a socket
* @sk: the socket * @sk: the socket
* @labeled: socket label scheme
* *
* Convert the outbound smack value (smk_out) to a * Convert the outbound smack value (smk_out) to a
* secattr and attach it to the socket. * secattr and attach it to the socket.
* *
* Returns 0 on success or an error code * 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 socket_smack *ssp;
struct netlbl_lsm_secattr secattr; struct netlbl_lsm_secattr secattr;
int rc; int rc = 0;
ssp = sk->sk_security; ssp = sk->sk_security;
/*
* 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); netlbl_secattr_init(&secattr);
smack_to_secattr(ssp->smk_out, &secattr); smack_to_secattr(ssp->smk_out, &secattr);
rc = netlbl_sock_setattr(sk, &secattr); rc = netlbl_sock_setattr(sk, &secattr);
netlbl_secattr_destroy(&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; return rc;
} }
...@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ...@@ -1432,7 +1457,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
ssp->smk_in = sp; ssp->smk_in = sp;
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = sp; ssp->smk_out = sp;
rc = smack_netlabel(sock->sk); rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc); __func__, -rc);
...@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family, ...@@ -1462,7 +1487,108 @@ static int smack_socket_post_create(struct socket *sock, int family,
/* /*
* Set the outbound netlbl. * 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, ...@@ -2101,8 +2227,14 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == NULL) if (newsmack == NULL)
return -EINVAL; return -EINVAL;
/*
* No process is ever allowed the web ("@") label.
*/
if (newsmack == smack_known_web.smk_known)
return -EPERM;
new = prepare_creds(); new = prepare_creds();
if (!new) if (new == NULL)
return -ENOMEM; return -ENOMEM;
new->security = newsmack; new->security = newsmack;
commit_creds(new); commit_creds(new);
...@@ -2143,6 +2275,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) ...@@ -2143,6 +2275,49 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE); return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE);
} }
/**
* 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 * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
* pair to smack * pair to smack
...@@ -2154,28 +2329,25 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) ...@@ -2154,28 +2329,25 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other)
static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
{ {
char smack[SMK_LABELLEN]; char smack[SMK_LABELLEN];
char *sp;
int pcat; 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 * If there are flags but no level netlabel isn't
* behaving the way we expect it to. * behaving the way we expect it to.
* *
* Get the categories, if any
* Without guidance regarding the smack value * Without guidance regarding the smack value
* for the packet fall back on the network * for the packet fall back on the network
* ambient value. * ambient value.
*/ */
strncpy(sip, smack_net_ambient, SMK_MAXLEN);
return;
}
/*
* Get the categories, if any
*/
memset(smack, '\0', SMK_LABELLEN); memset(smack, '\0', SMK_LABELLEN);
if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
for (pcat = -1;;) { for (pcat = -1;;) {
pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat, pcat = netlbl_secattr_catmap_walk(
pcat + 1); sap->attr.mls.cat, pcat + 1);
if (pcat < 0) if (pcat < 0)
break; break;
smack_catset_bit(pcat, smack); smack_catset_bit(pcat, smack);
...@@ -2189,10 +2361,35 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip) ...@@ -2189,10 +2361,35 @@ static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
return; return;
} }
/* /*
* Look it up in the supplied table if it is not a direct mapping. * Look it up in the supplied table if it is not
* a direct mapping.
*/ */
smack_from_cipso(sap->attr.mls.lvl, smack, sip); smack_from_cipso(sap->attr.mls.lvl, smack, sip);
return; return;
}
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;
}
/*
* Without guidance regarding the smack value
* for the packet fall back on the network
* ambient value.
*/
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) ...@@ -2207,6 +2404,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct netlbl_lsm_secattr secattr; struct netlbl_lsm_secattr secattr;
struct socket_smack *ssp = sk->sk_security; struct socket_smack *ssp = sk->sk_security;
char smack[SMK_LABELLEN]; char smack[SMK_LABELLEN];
char *csp;
int rc; int rc;
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) 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) ...@@ -2215,21 +2413,24 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
/* /*
* Translate what netlabel gave us. * Translate what netlabel gave us.
*/ */
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr); netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
if (rc == 0) if (rc == 0) {
smack_from_secattr(&secattr, smack); smack_from_secattr(&secattr, smack);
else csp = smack;
strncpy(smack, smack_net_ambient, SMK_MAXLEN); } else
csp = smack_net_ambient;
netlbl_secattr_destroy(&secattr); netlbl_secattr_destroy(&secattr);
/* /*
* Receiving a packet requires that the other end * Receiving a packet requires that the other end
* be able to write here. Read access is not required. * be able to write here. Read access is not required.
* This is the simplist possible security model * This is the simplist possible security model
* for networking. * for networking.
*/ */
rc = smk_access(smack, ssp->smk_in, MAY_WRITE); rc = smk_access(csp, ssp->smk_in, MAY_WRITE);
if (rc != 0) if (rc != 0)
netlbl_skbuff_err(skb, rc, 0); netlbl_skbuff_err(skb, rc, 0);
return rc; return rc;
...@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, ...@@ -2298,7 +2499,6 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
/* /*
* Translate what netlabel gave us. * Translate what netlabel gave us.
*/ */
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&secattr); netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0) if (rc == 0)
...@@ -2341,7 +2541,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent) ...@@ -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_in = ssp->smk_out = current_security();
ssp->smk_packet[0] = '\0'; ssp->smk_packet[0] = '\0';
rc = smack_netlabel(sk); rc = smack_netlabel(sk, SMACK_CIPSO_SOCKET);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n", printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
__func__, -rc); __func__, -rc);
...@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -2367,7 +2567,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
if (skb == NULL) if (skb == NULL)
return -EACCES; return -EACCES;
memset(smack, '\0', SMK_LABELLEN);
netlbl_secattr_init(&skb_secattr); netlbl_secattr_init(&skb_secattr);
rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr); rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);
if (rc == 0) if (rc == 0)
...@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = { ...@@ -2732,6 +2931,8 @@ struct security_operations smack_ops = {
.unix_may_send = smack_unix_may_send, .unix_may_send = smack_unix_may_send,
.socket_post_create = smack_socket_post_create, .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_sock_rcv_skb = smack_socket_sock_rcv_skb,
.socket_getpeersec_stream = smack_socket_getpeersec_stream, .socket_getpeersec_stream = smack_socket_getpeersec_stream,
.socket_getpeersec_dgram = smack_socket_getpeersec_dgram, .socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
...@@ -2783,7 +2984,6 @@ static __init int smack_init(void) ...@@ -2783,7 +2984,6 @@ static __init int smack_init(void)
/* /*
* Initialize locks * Initialize locks
*/ */
spin_lock_init(&smack_known_unset.smk_cipsolock);
spin_lock_init(&smack_known_huh.smk_cipsolock); spin_lock_init(&smack_known_huh.smk_cipsolock);
spin_lock_init(&smack_known_hat.smk_cipsolock); spin_lock_init(&smack_known_hat.smk_cipsolock);
spin_lock_init(&smack_known_star.smk_cipsolock); spin_lock_init(&smack_known_star.smk_cipsolock);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <net/net_namespace.h>
#include <net/netlabel.h> #include <net/netlabel.h>
#include <net/cipso_ipv4.h> #include <net/cipso_ipv4.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -38,7 +39,7 @@ enum smk_inos { ...@@ -38,7 +39,7 @@ enum smk_inos {
SMK_DOI = 5, /* CIPSO DOI */ SMK_DOI = 5, /* CIPSO DOI */
SMK_DIRECT = 6, /* CIPSO level indicating direct label */ SMK_DIRECT = 6, /* CIPSO level indicating direct label */
SMK_AMBIENT = 7, /* internet ambient 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 */ SMK_ONLYCAP = 9, /* the only "capable" label */
}; };
...@@ -48,6 +49,7 @@ enum smk_inos { ...@@ -48,6 +49,7 @@ enum smk_inos {
static DEFINE_MUTEX(smack_list_lock); static DEFINE_MUTEX(smack_list_lock);
static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_netlbladdr_lock);
/* /*
* This is the "ambient" label for network traffic. * This is the "ambient" label for network traffic.
...@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock); ...@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock);
*/ */
char *smack_net_ambient = smack_known_floor.smk_known; 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 * This is the level in a CIPSO header that indicates a
* smack label is contained directly in the category set. * smack label is contained directly in the category set.
...@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; ...@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
*/ */
char *smack_onlycap; 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; static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
struct smk_list_entry *smack_list; struct smk_list_entry *smack_list;
...@@ -104,6 +107,24 @@ 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_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) #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 * Seq_file read operations for /smack/load
...@@ -344,13 +365,11 @@ static void smk_cipso_doi(void) ...@@ -344,13 +365,11 @@ static void smk_cipso_doi(void)
{ {
int rc; int rc;
struct cipso_v4_doi *doip; struct cipso_v4_doi *doip;
struct netlbl_audit audit_info; struct netlbl_audit nai;
audit_info.loginuid = audit_get_loginuid(current); smk_netlabel_audit_set(&nai);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = smack_to_secid(current_security());
rc = netlbl_cfg_map_del(NULL, &audit_info); rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
if (rc != 0) if (rc != 0)
printk(KERN_WARNING "%s:%d remove rc = %d\n", printk(KERN_WARNING "%s:%d remove rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
...@@ -365,11 +384,19 @@ static void smk_cipso_doi(void) ...@@ -365,11 +384,19 @@ static void smk_cipso_doi(void)
for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++) for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
doip->tags[rc] = CIPSO_V4_TAG_INVALID; 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) { 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); __func__, __LINE__, rc);
kfree(doip); 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) ...@@ -379,20 +406,19 @@ static void smk_cipso_doi(void)
static void smk_unlbl_ambient(char *oldambient) static void smk_unlbl_ambient(char *oldambient)
{ {
int rc; int rc;
struct netlbl_audit audit_info; struct netlbl_audit nai;
audit_info.loginuid = audit_get_loginuid(current); smk_netlabel_audit_set(&nai);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = smack_to_secid(current_security());
if (oldambient != NULL) { 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) if (rc != 0)
printk(KERN_WARNING "%s:%d remove rc = %d\n", printk(KERN_WARNING "%s:%d remove rc = %d\n",
__func__, __LINE__, rc); __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) if (rc != 0)
printk(KERN_WARNING "%s:%d add rc = %d\n", printk(KERN_WARNING "%s:%d add rc = %d\n",
__func__, __LINE__, rc); __func__, __LINE__, rc);
...@@ -603,6 +629,201 @@ static const struct file_operations smk_cipso_ops = { ...@@ -603,6 +629,201 @@ static const struct file_operations smk_cipso_ops = {
.release = seq_release, .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 * smk_read_doi - read() for /smack/doi
* @filp: file pointer, not actually used * @filp: file pointer, not actually used
...@@ -891,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = { ...@@ -891,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = {
.write = smk_write_onlycap, .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 * smk_fill_super - fill the /smackfs superblock
* @sb: the empty superblock * @sb: the empty superblock
...@@ -1021,8 +1138,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) ...@@ -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}, {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
[SMK_AMBIENT] = [SMK_AMBIENT] =
{"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
[SMK_NLTYPE] = [SMK_NETLBLADDR] =
{"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR}, {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] = [SMK_ONLYCAP] =
{"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR}, {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
/* last one */ {""} /* last one */ {""}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment