|
@@ -56,7 +56,8 @@ static DEFINE_SPINLOCK(netlbl_domhsh_lock);
|
|
#define netlbl_domhsh_rcu_deref(p) \
|
|
#define netlbl_domhsh_rcu_deref(p) \
|
|
rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
|
|
rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
|
|
static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
|
|
static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
|
|
-static struct netlbl_dom_map __rcu *netlbl_domhsh_def;
|
|
|
|
|
|
+static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
|
|
|
|
+static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Domain Hash Table Helper Functions
|
|
* Domain Hash Table Helper Functions
|
|
@@ -126,18 +127,26 @@ static u32 netlbl_domhsh_hash(const char *key)
|
|
return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
|
|
return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool netlbl_family_match(u16 f1, u16 f2)
|
|
|
|
+{
|
|
|
|
+ return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* netlbl_domhsh_search - Search for a domain entry
|
|
* netlbl_domhsh_search - Search for a domain entry
|
|
* @domain: the domain
|
|
* @domain: the domain
|
|
|
|
+ * @family: the address family
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* Searches the domain hash table and returns a pointer to the hash table
|
|
* Searches the domain hash table and returns a pointer to the hash table
|
|
- * entry if found, otherwise NULL is returned. The caller is responsible for
|
|
|
|
|
|
+ * entry if found, otherwise NULL is returned. @family may be %AF_UNSPEC
|
|
|
|
+ * which matches any address family entries. The caller is responsible for
|
|
* ensuring that the hash table is protected with either a RCU read lock or the
|
|
* ensuring that the hash table is protected with either a RCU read lock or the
|
|
* hash table lock.
|
|
* hash table lock.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
|
|
|
|
|
|
+static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain,
|
|
|
|
+ u16 family)
|
|
{
|
|
{
|
|
u32 bkt;
|
|
u32 bkt;
|
|
struct list_head *bkt_list;
|
|
struct list_head *bkt_list;
|
|
@@ -147,7 +156,9 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
|
|
bkt = netlbl_domhsh_hash(domain);
|
|
bkt = netlbl_domhsh_hash(domain);
|
|
bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
|
|
bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
|
|
list_for_each_entry_rcu(iter, bkt_list, list)
|
|
list_for_each_entry_rcu(iter, bkt_list, list)
|
|
- if (iter->valid && strcmp(iter->domain, domain) == 0)
|
|
|
|
|
|
+ if (iter->valid &&
|
|
|
|
+ netlbl_family_match(iter->family, family) &&
|
|
|
|
+ strcmp(iter->domain, domain) == 0)
|
|
return iter;
|
|
return iter;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -157,28 +168,37 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
|
|
/**
|
|
/**
|
|
* netlbl_domhsh_search_def - Search for a domain entry
|
|
* netlbl_domhsh_search_def - Search for a domain entry
|
|
* @domain: the domain
|
|
* @domain: the domain
|
|
- * @def: return default if no match is found
|
|
|
|
|
|
+ * @family: the address family
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* Searches the domain hash table and returns a pointer to the hash table
|
|
* Searches the domain hash table and returns a pointer to the hash table
|
|
* entry if an exact match is found, if an exact match is not present in the
|
|
* entry if an exact match is found, if an exact match is not present in the
|
|
* hash table then the default entry is returned if valid otherwise NULL is
|
|
* hash table then the default entry is returned if valid otherwise NULL is
|
|
- * returned. The caller is responsible ensuring that the hash table is
|
|
|
|
|
|
+ * returned. @family may be %AF_UNSPEC which matches any address family
|
|
|
|
+ * entries. The caller is responsible ensuring that the hash table is
|
|
* protected with either a RCU read lock or the hash table lock.
|
|
* protected with either a RCU read lock or the hash table lock.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
|
|
|
|
|
|
+static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain,
|
|
|
|
+ u16 family)
|
|
{
|
|
{
|
|
struct netlbl_dom_map *entry;
|
|
struct netlbl_dom_map *entry;
|
|
|
|
|
|
- entry = netlbl_domhsh_search(domain);
|
|
|
|
- if (entry == NULL) {
|
|
|
|
- entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def);
|
|
|
|
- if (entry != NULL && !entry->valid)
|
|
|
|
- entry = NULL;
|
|
|
|
|
|
+ entry = netlbl_domhsh_search(domain, family);
|
|
|
|
+ if (entry != NULL)
|
|
|
|
+ return entry;
|
|
|
|
+ if (family == AF_INET || family == AF_UNSPEC) {
|
|
|
|
+ entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4);
|
|
|
|
+ if (entry != NULL && entry->valid)
|
|
|
|
+ return entry;
|
|
|
|
+ }
|
|
|
|
+ if (family == AF_INET6 || family == AF_UNSPEC) {
|
|
|
|
+ entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6);
|
|
|
|
+ if (entry != NULL && entry->valid)
|
|
|
|
+ return entry;
|
|
}
|
|
}
|
|
|
|
|
|
- return entry;
|
|
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -264,13 +284,19 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
|
|
if (entry == NULL)
|
|
if (entry == NULL)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
+ if (entry->family != AF_INET && entry->family != AF_INET6 &&
|
|
|
|
+ (entry->family != AF_UNSPEC ||
|
|
|
|
+ entry->def.type != NETLBL_NLTYPE_UNLABELED))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
switch (entry->def.type) {
|
|
switch (entry->def.type) {
|
|
case NETLBL_NLTYPE_UNLABELED:
|
|
case NETLBL_NLTYPE_UNLABELED:
|
|
if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
|
|
if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
break;
|
|
break;
|
|
case NETLBL_NLTYPE_CIPSOV4:
|
|
case NETLBL_NLTYPE_CIPSOV4:
|
|
- if (entry->def.cipso == NULL)
|
|
|
|
|
|
+ if (entry->family != AF_INET ||
|
|
|
|
+ entry->def.cipso == NULL)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
break;
|
|
break;
|
|
case NETLBL_NLTYPE_ADDRSELECT:
|
|
case NETLBL_NLTYPE_ADDRSELECT:
|
|
@@ -358,15 +384,18 @@ int __init netlbl_domhsh_init(u32 size)
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* Adds a new entry to the domain hash table and handles any updates to the
|
|
* Adds a new entry to the domain hash table and handles any updates to the
|
|
- * lower level protocol handler (i.e. CIPSO). Returns zero on success,
|
|
|
|
- * negative on failure.
|
|
|
|
|
|
+ * lower level protocol handler (i.e. CIPSO). @entry->family may be set to
|
|
|
|
+ * %AF_UNSPEC which will add an entry that matches all address families. This
|
|
|
|
+ * is only useful for the unlabelled type and will only succeed if there is no
|
|
|
|
+ * existing entry for any address family with the same domain. Returns zero
|
|
|
|
+ * on success, negative on failure.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
|
|
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
|
|
struct netlbl_audit *audit_info)
|
|
struct netlbl_audit *audit_info)
|
|
{
|
|
{
|
|
int ret_val = 0;
|
|
int ret_val = 0;
|
|
- struct netlbl_dom_map *entry_old;
|
|
|
|
|
|
+ struct netlbl_dom_map *entry_old, *entry_b;
|
|
struct netlbl_af4list *iter4;
|
|
struct netlbl_af4list *iter4;
|
|
struct netlbl_af4list *tmp4;
|
|
struct netlbl_af4list *tmp4;
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
@@ -385,9 +414,10 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
spin_lock(&netlbl_domhsh_lock);
|
|
spin_lock(&netlbl_domhsh_lock);
|
|
if (entry->domain != NULL)
|
|
if (entry->domain != NULL)
|
|
- entry_old = netlbl_domhsh_search(entry->domain);
|
|
|
|
|
|
+ entry_old = netlbl_domhsh_search(entry->domain, entry->family);
|
|
else
|
|
else
|
|
- entry_old = netlbl_domhsh_search_def(entry->domain);
|
|
|
|
|
|
+ entry_old = netlbl_domhsh_search_def(entry->domain,
|
|
|
|
+ entry->family);
|
|
if (entry_old == NULL) {
|
|
if (entry_old == NULL) {
|
|
entry->valid = 1;
|
|
entry->valid = 1;
|
|
|
|
|
|
@@ -397,7 +427,41 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
|
|
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
|
|
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
|
|
} else {
|
|
} else {
|
|
INIT_LIST_HEAD(&entry->list);
|
|
INIT_LIST_HEAD(&entry->list);
|
|
- rcu_assign_pointer(netlbl_domhsh_def, entry);
|
|
|
|
|
|
+ switch (entry->family) {
|
|
|
|
+ case AF_INET:
|
|
|
|
+ rcu_assign_pointer(netlbl_domhsh_def_ipv4,
|
|
|
|
+ entry);
|
|
|
|
+ break;
|
|
|
|
+ case AF_INET6:
|
|
|
|
+ rcu_assign_pointer(netlbl_domhsh_def_ipv6,
|
|
|
|
+ entry);
|
|
|
|
+ break;
|
|
|
|
+ case AF_UNSPEC:
|
|
|
|
+ if (entry->def.type !=
|
|
|
|
+ NETLBL_NLTYPE_UNLABELED) {
|
|
|
|
+ ret_val = -EINVAL;
|
|
|
|
+ goto add_return;
|
|
|
|
+ }
|
|
|
|
+ entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC);
|
|
|
|
+ if (entry_b == NULL) {
|
|
|
|
+ ret_val = -ENOMEM;
|
|
|
|
+ goto add_return;
|
|
|
|
+ }
|
|
|
|
+ entry_b->family = AF_INET6;
|
|
|
|
+ entry_b->def.type = NETLBL_NLTYPE_UNLABELED;
|
|
|
|
+ entry_b->valid = 1;
|
|
|
|
+ entry->family = AF_INET;
|
|
|
|
+ rcu_assign_pointer(netlbl_domhsh_def_ipv4,
|
|
|
|
+ entry);
|
|
|
|
+ rcu_assign_pointer(netlbl_domhsh_def_ipv6,
|
|
|
|
+ entry_b);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ /* Already checked in
|
|
|
|
+ * netlbl_domhsh_validate(). */
|
|
|
|
+ ret_val = -EINVAL;
|
|
|
|
+ goto add_return;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
|
|
if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
|
|
@@ -513,10 +577,12 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
|
|
spin_lock(&netlbl_domhsh_lock);
|
|
spin_lock(&netlbl_domhsh_lock);
|
|
if (entry->valid) {
|
|
if (entry->valid) {
|
|
entry->valid = 0;
|
|
entry->valid = 0;
|
|
- if (entry != rcu_dereference(netlbl_domhsh_def))
|
|
|
|
- list_del_rcu(&entry->list);
|
|
|
|
|
|
+ if (entry == rcu_dereference(netlbl_domhsh_def_ipv4))
|
|
|
|
+ RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL);
|
|
|
|
+ else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6))
|
|
|
|
+ RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL);
|
|
else
|
|
else
|
|
- RCU_INIT_POINTER(netlbl_domhsh_def, NULL);
|
|
|
|
|
|
+ list_del_rcu(&entry->list);
|
|
} else
|
|
} else
|
|
ret_val = -ENOENT;
|
|
ret_val = -ENOENT;
|
|
spin_unlock(&netlbl_domhsh_lock);
|
|
spin_unlock(&netlbl_domhsh_lock);
|
|
@@ -583,9 +649,9 @@ int netlbl_domhsh_remove_af4(const char *domain,
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
|
|
|
|
if (domain)
|
|
if (domain)
|
|
- entry_map = netlbl_domhsh_search(domain);
|
|
|
|
|
|
+ entry_map = netlbl_domhsh_search(domain, AF_INET);
|
|
else
|
|
else
|
|
- entry_map = netlbl_domhsh_search_def(domain);
|
|
|
|
|
|
+ entry_map = netlbl_domhsh_search_def(domain, AF_INET);
|
|
if (entry_map == NULL ||
|
|
if (entry_map == NULL ||
|
|
entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
|
|
entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
|
|
goto remove_af4_failure;
|
|
goto remove_af4_failure;
|
|
@@ -625,25 +691,45 @@ remove_af4_failure:
|
|
/**
|
|
/**
|
|
* 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
|
|
|
|
+ * @family: address family
|
|
* @audit_info: NetLabel audit information
|
|
* @audit_info: NetLabel audit information
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* Removes an entry from the domain hash table and handles any updates to the
|
|
* Removes an entry from the domain hash table and handles any updates to the
|
|
- * lower level protocol handler (i.e. CIPSO). Returns zero on success,
|
|
|
|
- * negative on failure.
|
|
|
|
|
|
+ * lower level protocol handler (i.e. CIPSO). @family may be %AF_UNSPEC which
|
|
|
|
+ * removes all address family entries. Returns zero on success, negative on
|
|
|
|
+ * failure.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
|
|
|
|
|
|
+int netlbl_domhsh_remove(const char *domain, u16 family,
|
|
|
|
+ struct netlbl_audit *audit_info)
|
|
{
|
|
{
|
|
- int ret_val;
|
|
|
|
|
|
+ int ret_val = -EINVAL;
|
|
struct netlbl_dom_map *entry;
|
|
struct netlbl_dom_map *entry;
|
|
|
|
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
- if (domain)
|
|
|
|
- entry = netlbl_domhsh_search(domain);
|
|
|
|
- else
|
|
|
|
- entry = netlbl_domhsh_search_def(domain);
|
|
|
|
- ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
|
|
|
|
|
|
+
|
|
|
|
+ if (family == AF_INET || family == AF_UNSPEC) {
|
|
|
|
+ if (domain)
|
|
|
|
+ entry = netlbl_domhsh_search(domain, AF_INET);
|
|
|
|
+ else
|
|
|
|
+ entry = netlbl_domhsh_search_def(domain, AF_INET);
|
|
|
|
+ ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
|
|
|
|
+ if (ret_val && ret_val != -ENOENT)
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+ if (family == AF_INET6 || family == AF_UNSPEC) {
|
|
|
|
+ int ret_val2;
|
|
|
|
+
|
|
|
|
+ if (domain)
|
|
|
|
+ entry = netlbl_domhsh_search(domain, AF_INET6);
|
|
|
|
+ else
|
|
|
|
+ entry = netlbl_domhsh_search_def(domain, AF_INET6);
|
|
|
|
+ ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
|
|
|
|
+ if (ret_val2 != -ENOENT)
|
|
|
|
+ ret_val = ret_val2;
|
|
|
|
+ }
|
|
|
|
+done:
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
|
|
|
|
return ret_val;
|
|
return ret_val;
|
|
@@ -651,32 +737,38 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
|
|
|
|
|
|
/**
|
|
/**
|
|
* netlbl_domhsh_remove_default - Removes the default entry from the table
|
|
* netlbl_domhsh_remove_default - Removes the default entry from the table
|
|
|
|
+ * @family: address family
|
|
* @audit_info: NetLabel audit information
|
|
* @audit_info: NetLabel audit information
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
- * Removes/resets the default entry for the domain hash table and handles any
|
|
|
|
- * updates to the lower level protocol handler (i.e. CIPSO). Returns zero on
|
|
|
|
- * success, non-zero on failure.
|
|
|
|
|
|
+ * Removes/resets the default entry corresponding to @family from the domain
|
|
|
|
+ * hash table and handles any updates to the lower level protocol handler
|
|
|
|
+ * (i.e. CIPSO). @family may be %AF_UNSPEC which removes all address family
|
|
|
|
+ * entries. Returns zero on success, negative on failure.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
|
|
|
|
|
|
+int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
|
|
{
|
|
{
|
|
- return netlbl_domhsh_remove(NULL, audit_info);
|
|
|
|
|
|
+ return netlbl_domhsh_remove(NULL, family, audit_info);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* netlbl_domhsh_getentry - Get an entry from the domain hash table
|
|
* netlbl_domhsh_getentry - Get an entry from the domain hash table
|
|
* @domain: the domain name to search for
|
|
* @domain: the domain name to search for
|
|
|
|
+ * @family: address family
|
|
*
|
|
*
|
|
* Description:
|
|
* Description:
|
|
* Look through the domain hash table searching for an entry to match @domain,
|
|
* Look through the domain hash table searching for an entry to match @domain,
|
|
- * return a pointer to a copy of the entry or NULL. The caller is responsible
|
|
|
|
- * for ensuring that rcu_read_[un]lock() is called.
|
|
|
|
|
|
+ * with address family @family, return a pointer to a copy of the entry or
|
|
|
|
+ * NULL. The caller is responsible for ensuring that rcu_read_[un]lock() is
|
|
|
|
+ * called.
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
|
|
|
|
|
|
+struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
|
|
{
|
|
{
|
|
- return netlbl_domhsh_search_def(domain);
|
|
|
|
|
|
+ if (family == AF_UNSPEC)
|
|
|
|
+ return NULL;
|
|
|
|
+ return netlbl_domhsh_search_def(domain, family);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -696,7 +788,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
|
|
struct netlbl_dom_map *dom_iter;
|
|
struct netlbl_dom_map *dom_iter;
|
|
struct netlbl_af4list *addr_iter;
|
|
struct netlbl_af4list *addr_iter;
|
|
|
|
|
|
- dom_iter = netlbl_domhsh_search_def(domain);
|
|
|
|
|
|
+ dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
|
|
if (dom_iter == NULL)
|
|
if (dom_iter == NULL)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
@@ -726,7 +818,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
|
|
struct netlbl_dom_map *dom_iter;
|
|
struct netlbl_dom_map *dom_iter;
|
|
struct netlbl_af6list *addr_iter;
|
|
struct netlbl_af6list *addr_iter;
|
|
|
|
|
|
- dom_iter = netlbl_domhsh_search_def(domain);
|
|
|
|
|
|
+ dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
|
|
if (dom_iter == NULL)
|
|
if (dom_iter == NULL)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|