Skip to content

Commit

Permalink
netlabel: use domain based selectors when address based selectors are…
Browse files Browse the repository at this point in the history
… not available

NetLabel has the ability to selectively assign network security labels
to outbound traffic based on either the LSM's "domain" (different for
each LSM), the network destination, or a combination of both.  Depending
on the type of traffic, local or forwarded, and the type of traffic
selector, domain or address based, different hooks are used to label the
traffic; the goal being minimal overhead.

Unfortunately, there is a bug such that a system using NetLabel domain
based traffic selectors does not correctly label outbound local traffic
that is not assigned to a socket.  The issue is that in these cases
the associated NetLabel hook only looks at the address based selectors
and not the domain based selectors.  This patch corrects this by
checking both the domain and address based selectors so that the correct
labeling is applied, regardless of the configuration type.

In order to acomplish this fix, this patch also simplifies some of the
NetLabel domainhash structures to use a more common outbound traffic
mapping type: struct netlbl_dommap_def.  This simplifies some of the code
in this patch and paves the way for further simplifications in the
future.

Signed-off-by: Paul Moore <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
pcmoore authored and davem330 committed Aug 2, 2013
1 parent 5f671d6 commit 6a8b7f0
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 158 deletions.
4 changes: 2 additions & 2 deletions net/netlabel/netlabel_cipso_v4.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,8 @@ static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
{
struct netlbl_domhsh_walk_arg *cb_arg = arg;

if (entry->type == NETLBL_NLTYPE_CIPSOV4 &&
entry->type_def.cipsov4->doi == cb_arg->doi)
if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 &&
entry->def.cipso->doi == cb_arg->doi)
return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);

return 0;
Expand Down
104 changes: 49 additions & 55 deletions net/netlabel/netlabel_domainhash.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ static void netlbl_domhsh_free_entry(struct rcu_head *entry)
#endif /* IPv6 */

ptr = container_of(entry, struct netlbl_dom_map, rcu);
if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
if (ptr->def.type == NETLBL_NLTYPE_ADDRSELECT) {
netlbl_af4list_foreach_safe(iter4, tmp4,
&ptr->type_def.addrsel->list4) {
&ptr->def.addrsel->list4) {
netlbl_af4list_remove_entry(iter4);
kfree(netlbl_domhsh_addr4_entry(iter4));
}
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach_safe(iter6, tmp6,
&ptr->type_def.addrsel->list6) {
&ptr->def.addrsel->list6) {
netlbl_af6list_remove_entry(iter6);
kfree(netlbl_domhsh_addr6_entry(iter6));
}
Expand Down Expand Up @@ -213,21 +213,21 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
if (addr4 != NULL) {
struct netlbl_domaddr4_map *map4;
map4 = netlbl_domhsh_addr4_entry(addr4);
type = map4->type;
cipsov4 = map4->type_def.cipsov4;
type = map4->def.type;
cipsov4 = map4->def.cipso;
netlbl_af4list_audit_addr(audit_buf, 0, NULL,
addr4->addr, addr4->mask);
#if IS_ENABLED(CONFIG_IPV6)
} else if (addr6 != NULL) {
struct netlbl_domaddr6_map *map6;
map6 = netlbl_domhsh_addr6_entry(addr6);
type = map6->type;
type = map6->def.type;
netlbl_af6list_audit_addr(audit_buf, 0, NULL,
&addr6->addr, &addr6->mask);
#endif /* IPv6 */
} else {
type = entry->type;
cipsov4 = entry->type_def.cipsov4;
type = entry->def.type;
cipsov4 = entry->def.cipso;
}
switch (type) {
case NETLBL_NLTYPE_UNLABELED:
Expand Down Expand Up @@ -265,36 +265,35 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
if (entry == NULL)
return -EINVAL;

switch (entry->type) {
switch (entry->def.type) {
case NETLBL_NLTYPE_UNLABELED:
if (entry->type_def.cipsov4 != NULL ||
entry->type_def.addrsel != NULL)
if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
return -EINVAL;
break;
case NETLBL_NLTYPE_CIPSOV4:
if (entry->type_def.cipsov4 == NULL)
if (entry->def.cipso == NULL)
return -EINVAL;
break;
case NETLBL_NLTYPE_ADDRSELECT:
netlbl_af4list_foreach(iter4, &entry->type_def.addrsel->list4) {
netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) {
map4 = netlbl_domhsh_addr4_entry(iter4);
switch (map4->type) {
switch (map4->def.type) {
case NETLBL_NLTYPE_UNLABELED:
if (map4->type_def.cipsov4 != NULL)
if (map4->def.cipso != NULL)
return -EINVAL;
break;
case NETLBL_NLTYPE_CIPSOV4:
if (map4->type_def.cipsov4 == NULL)
if (map4->def.cipso == NULL)
return -EINVAL;
break;
default:
return -EINVAL;
}
}
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach(iter6, &entry->type_def.addrsel->list6) {
netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) {
map6 = netlbl_domhsh_addr6_entry(iter6);
switch (map6->type) {
switch (map6->def.type) {
case NETLBL_NLTYPE_UNLABELED:
break;
default:
Expand Down Expand Up @@ -402,41 +401,39 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
rcu_assign_pointer(netlbl_domhsh_def, entry);
}

if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4)
&entry->def.addrsel->list4)
netlbl_domhsh_audit_add(entry, iter4, NULL,
ret_val, audit_info);
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach_rcu(iter6,
&entry->type_def.addrsel->list6)
&entry->def.addrsel->list6)
netlbl_domhsh_audit_add(entry, NULL, iter6,
ret_val, audit_info);
#endif /* IPv6 */
} else
netlbl_domhsh_audit_add(entry, NULL, NULL,
ret_val, audit_info);
} else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
entry->type == NETLBL_NLTYPE_ADDRSELECT) {
} else if (entry_old->def.type == NETLBL_NLTYPE_ADDRSELECT &&
entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
struct list_head *old_list4;
struct list_head *old_list6;

old_list4 = &entry_old->type_def.addrsel->list4;
old_list6 = &entry_old->type_def.addrsel->list6;
old_list4 = &entry_old->def.addrsel->list4;
old_list6 = &entry_old->def.addrsel->list6;

/* we only allow the addition of address selectors if all of
* the selectors do not exist in the existing domain map */
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4)
netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4)
if (netlbl_af4list_search_exact(iter4->addr,
iter4->mask,
old_list4)) {
ret_val = -EEXIST;
goto add_return;
}
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach_rcu(iter6,
&entry->type_def.addrsel->list6)
netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6)
if (netlbl_af6list_search_exact(&iter6->addr,
&iter6->mask,
old_list6)) {
Expand All @@ -446,7 +443,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
#endif /* IPv6 */

netlbl_af4list_foreach_safe(iter4, tmp4,
&entry->type_def.addrsel->list4) {
&entry->def.addrsel->list4) {
netlbl_af4list_remove_entry(iter4);
iter4->valid = 1;
ret_val = netlbl_af4list_add(iter4, old_list4);
Expand All @@ -457,7 +454,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
}
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach_safe(iter6, tmp6,
&entry->type_def.addrsel->list6) {
&entry->def.addrsel->list6) {
netlbl_af6list_remove_entry(iter6);
iter6->valid = 1;
ret_val = netlbl_af6list_add(iter6, old_list6);
Expand Down Expand Up @@ -538,18 +535,18 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
struct netlbl_af4list *iter4;
struct netlbl_domaddr4_map *map4;

switch (entry->type) {
switch (entry->def.type) {
case NETLBL_NLTYPE_ADDRSELECT:
netlbl_af4list_foreach_rcu(iter4,
&entry->type_def.addrsel->list4) {
&entry->def.addrsel->list4) {
map4 = netlbl_domhsh_addr4_entry(iter4);
cipso_v4_doi_putdef(map4->type_def.cipsov4);
cipso_v4_doi_putdef(map4->def.cipso);
}
/* no need to check the IPv6 list since we currently
* support only unlabeled protocols for IPv6 */
break;
case NETLBL_NLTYPE_CIPSOV4:
cipso_v4_doi_putdef(entry->type_def.cipsov4);
cipso_v4_doi_putdef(entry->def.cipso);
break;
}
call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
Expand Down Expand Up @@ -590,20 +587,21 @@ int netlbl_domhsh_remove_af4(const char *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)
if (entry_map == NULL ||
entry_map->def.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);
&entry_map->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)
netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
goto remove_af4_single_addr;
#if IS_ENABLED(CONFIG_IPV6)
netlbl_af6list_foreach_rcu(iter6, &entry_map->type_def.addrsel->list6)
netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
goto remove_af4_single_addr;
#endif /* IPv6 */
/* the domain mapping is empty so remove it from the mapping table */
Expand All @@ -616,7 +614,7 @@ int netlbl_domhsh_remove_af4(const char *domain,
* shouldn't be a problem */
synchronize_rcu();
entry = netlbl_domhsh_addr4_entry(entry_addr);
cipso_v4_doi_putdef(entry->type_def.cipsov4);
cipso_v4_doi_putdef(entry->def.cipso);
kfree(entry);
return 0;

Expand Down Expand Up @@ -693,24 +691,22 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
* responsible for ensuring that rcu_read_[un]lock() is called.
*
*/
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr)
struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr)
{
struct netlbl_dom_map *dom_iter;
struct netlbl_af4list *addr_iter;

dom_iter = netlbl_domhsh_search_def(domain);
if (dom_iter == NULL)
return NULL;
if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
return NULL;

addr_iter = netlbl_af4list_search(addr,
&dom_iter->type_def.addrsel->list4);
if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
return &dom_iter->def;
addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
if (addr_iter == NULL)
return NULL;

return netlbl_domhsh_addr4_entry(addr_iter);
return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
}

#if IS_ENABLED(CONFIG_IPV6)
Expand All @@ -725,7 +721,7 @@ struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
* responsible for ensuring that rcu_read_[un]lock() is called.
*
*/
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
const struct in6_addr *addr)
{
struct netlbl_dom_map *dom_iter;
Expand All @@ -734,15 +730,13 @@ struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
dom_iter = netlbl_domhsh_search_def(domain);
if (dom_iter == NULL)
return NULL;
if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
return NULL;

addr_iter = netlbl_af6list_search(addr,
&dom_iter->type_def.addrsel->list6);
if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
return &dom_iter->def;
addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
if (addr_iter == NULL)
return NULL;

return netlbl_domhsh_addr6_entry(addr_iter);
return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
}
#endif /* IPv6 */

Expand Down
46 changes: 22 additions & 24 deletions net/netlabel/netlabel_domainhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,35 @@
#define NETLBL_DOMHSH_BITSIZE 7

/* Domain mapping definition structures */
struct netlbl_domaddr_map {
struct list_head list4;
struct list_head list6;
};
struct netlbl_dommap_def {
u32 type;
union {
struct netlbl_domaddr_map *addrsel;
struct cipso_v4_doi *cipso;
};
};
#define netlbl_domhsh_addr4_entry(iter) \
container_of(iter, struct netlbl_domaddr4_map, list)
struct netlbl_domaddr4_map {
u32 type;
union {
struct cipso_v4_doi *cipsov4;
} type_def;
struct netlbl_dommap_def def;

struct netlbl_af4list list;
};
#define netlbl_domhsh_addr6_entry(iter) \
container_of(iter, struct netlbl_domaddr6_map, list)
struct netlbl_domaddr6_map {
u32 type;

/* NOTE: no 'type_def' union needed at present since we don't currently
* support any IPv6 labeling protocols */
struct netlbl_dommap_def def;

struct netlbl_af6list list;
};
struct netlbl_domaddr_map {
struct list_head list4;
struct list_head list6;
};

struct netlbl_dom_map {
char *domain;
u32 type;
union {
struct cipso_v4_doi *cipsov4;
struct netlbl_domaddr_map *addrsel;
} type_def;
struct netlbl_dommap_def def;

u32 valid;
struct list_head list;
Expand All @@ -97,16 +95,16 @@ int netlbl_domhsh_remove_af4(const char *domain,
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);
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr);
struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
__be32 addr);
#if IS_ENABLED(CONFIG_IPV6)
struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
const struct in6_addr *addr);
#endif /* IPv6 */

int netlbl_domhsh_walk(u32 *skip_bkt,
u32 *skip_chain,
int (*callback) (struct netlbl_dom_map *entry, void *arg),
void *cb_arg);

#if IS_ENABLED(CONFIG_IPV6)
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
const struct in6_addr *addr);
#endif /* IPv6 */

#endif
Loading

0 comments on commit 6a8b7f0

Please sign in to comment.