xref: /linux/net/netlabel/netlabel_domainhash.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /*
3   * NetLabel Domain Hash Table
4   *
5   * This file manages the domain hash table that NetLabel uses to determine
6   * which network labeling protocol to use for a given domain.  The NetLabel
7   * system manages static and dynamic label mappings for network protocols such
8   * as CIPSO and RIPSO.
9   *
10   * Author: Paul Moore <paul@paul-moore.com>
11   */
12  
13  /*
14   * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
15   */
16  
17  #include <linux/types.h>
18  #include <linux/rculist.h>
19  #include <linux/skbuff.h>
20  #include <linux/spinlock.h>
21  #include <linux/string.h>
22  #include <linux/audit.h>
23  #include <linux/slab.h>
24  #include <net/netlabel.h>
25  #include <net/cipso_ipv4.h>
26  #include <net/calipso.h>
27  #include <asm/bug.h>
28  
29  #include "netlabel_mgmt.h"
30  #include "netlabel_addrlist.h"
31  #include "netlabel_calipso.h"
32  #include "netlabel_domainhash.h"
33  #include "netlabel_user.h"
34  
35  struct netlbl_domhsh_tbl {
36  	struct list_head *tbl;
37  	u32 size;
38  };
39  
40  /* Domain hash table */
41  /* updates should be so rare that having one spinlock for the entire hash table
42   * should be okay */
43  static DEFINE_SPINLOCK(netlbl_domhsh_lock);
44  #define netlbl_domhsh_rcu_deref(p) \
45  	rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
46  static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
47  static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
48  static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6;
49  
50  /*
51   * Domain Hash Table Helper Functions
52   */
53  
54  /**
55   * netlbl_domhsh_free_entry - Frees a domain hash table entry
56   * @entry: the entry's RCU field
57   *
58   * Description:
59   * This function is designed to be used as a callback to the call_rcu()
60   * function so that the memory allocated to a hash table entry can be released
61   * safely.
62   *
63   */
netlbl_domhsh_free_entry(struct rcu_head * entry)64  static void netlbl_domhsh_free_entry(struct rcu_head *entry)
65  {
66  	struct netlbl_dom_map *ptr;
67  	struct netlbl_af4list *iter4;
68  	struct netlbl_af4list *tmp4;
69  #if IS_ENABLED(CONFIG_IPV6)
70  	struct netlbl_af6list *iter6;
71  	struct netlbl_af6list *tmp6;
72  #endif /* IPv6 */
73  
74  	ptr = container_of(entry, struct netlbl_dom_map, rcu);
75  	if (ptr->def.type == NETLBL_NLTYPE_ADDRSELECT) {
76  		netlbl_af4list_foreach_safe(iter4, tmp4,
77  					    &ptr->def.addrsel->list4) {
78  			netlbl_af4list_remove_entry(iter4);
79  			kfree(netlbl_domhsh_addr4_entry(iter4));
80  		}
81  #if IS_ENABLED(CONFIG_IPV6)
82  		netlbl_af6list_foreach_safe(iter6, tmp6,
83  					    &ptr->def.addrsel->list6) {
84  			netlbl_af6list_remove_entry(iter6);
85  			kfree(netlbl_domhsh_addr6_entry(iter6));
86  		}
87  #endif /* IPv6 */
88  		kfree(ptr->def.addrsel);
89  	}
90  	kfree(ptr->domain);
91  	kfree(ptr);
92  }
93  
94  /**
95   * netlbl_domhsh_hash - Hashing function for the domain hash table
96   * @key: the domain name to hash
97   *
98   * Description:
99   * This is the hashing function for the domain hash table, it returns the
100   * correct bucket number for the domain.  The caller is responsible for
101   * ensuring that the hash table is protected with either a RCU read lock or the
102   * hash table lock.
103   *
104   */
netlbl_domhsh_hash(const char * key)105  static u32 netlbl_domhsh_hash(const char *key)
106  {
107  	u32 iter;
108  	u32 val;
109  	u32 len;
110  
111  	/* This is taken (with slight modification) from
112  	 * security/selinux/ss/symtab.c:symhash() */
113  
114  	for (iter = 0, val = 0, len = strlen(key); iter < len; iter++)
115  		val = (val << 4 | (val >> (8 * sizeof(u32) - 4))) ^ key[iter];
116  	return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
117  }
118  
netlbl_family_match(u16 f1,u16 f2)119  static bool netlbl_family_match(u16 f1, u16 f2)
120  {
121  	return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC);
122  }
123  
124  /**
125   * netlbl_domhsh_search - Search for a domain entry
126   * @domain: the domain
127   * @family: the address family
128   *
129   * Description:
130   * Searches the domain hash table and returns a pointer to the hash table
131   * entry if found, otherwise NULL is returned.  @family may be %AF_UNSPEC
132   * which matches any address family entries.  The caller is responsible for
133   * ensuring that the hash table is protected with either a RCU read lock or the
134   * hash table lock.
135   *
136   */
netlbl_domhsh_search(const char * domain,u16 family)137  static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain,
138  						   u16 family)
139  {
140  	u32 bkt;
141  	struct list_head *bkt_list;
142  	struct netlbl_dom_map *iter;
143  
144  	if (domain != NULL) {
145  		bkt = netlbl_domhsh_hash(domain);
146  		bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
147  		list_for_each_entry_rcu(iter, bkt_list, list,
148  					lockdep_is_held(&netlbl_domhsh_lock))
149  			if (iter->valid &&
150  			    netlbl_family_match(iter->family, family) &&
151  			    strcmp(iter->domain, domain) == 0)
152  				return iter;
153  	}
154  
155  	return NULL;
156  }
157  
158  /**
159   * netlbl_domhsh_search_def - Search for a domain entry
160   * @domain: the domain
161   * @family: the address family
162   *
163   * Description:
164   * Searches the domain hash table and returns a pointer to the hash table
165   * entry if an exact match is found, if an exact match is not present in the
166   * hash table then the default entry is returned if valid otherwise NULL is
167   * returned.  @family may be %AF_UNSPEC which matches any address family
168   * entries.  The caller is responsible ensuring that the hash table is
169   * protected with either a RCU read lock or the hash table lock.
170   *
171   */
netlbl_domhsh_search_def(const char * domain,u16 family)172  static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain,
173  						       u16 family)
174  {
175  	struct netlbl_dom_map *entry;
176  
177  	entry = netlbl_domhsh_search(domain, family);
178  	if (entry != NULL)
179  		return entry;
180  	if (family == AF_INET || family == AF_UNSPEC) {
181  		entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4);
182  		if (entry != NULL && entry->valid)
183  			return entry;
184  	}
185  	if (family == AF_INET6 || family == AF_UNSPEC) {
186  		entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6);
187  		if (entry != NULL && entry->valid)
188  			return entry;
189  	}
190  
191  	return NULL;
192  }
193  
194  /**
195   * netlbl_domhsh_audit_add - Generate an audit entry for an add event
196   * @entry: the entry being added
197   * @addr4: the IPv4 address information
198   * @addr6: the IPv6 address information
199   * @result: the result code
200   * @audit_info: NetLabel audit information
201   *
202   * Description:
203   * Generate an audit record for adding a new NetLabel/LSM mapping entry with
204   * the given information.  Caller is responsible for holding the necessary
205   * locks.
206   *
207   */
netlbl_domhsh_audit_add(struct netlbl_dom_map * entry,struct netlbl_af4list * addr4,struct netlbl_af6list * addr6,int result,struct netlbl_audit * audit_info)208  static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
209  				    struct netlbl_af4list *addr4,
210  				    struct netlbl_af6list *addr6,
211  				    int result,
212  				    struct netlbl_audit *audit_info)
213  {
214  	struct audit_buffer *audit_buf;
215  	struct cipso_v4_doi *cipsov4 = NULL;
216  	struct calipso_doi *calipso = NULL;
217  	u32 type;
218  
219  	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
220  	if (audit_buf != NULL) {
221  		audit_log_format(audit_buf, " nlbl_domain=%s",
222  				 entry->domain ? entry->domain : "(default)");
223  		if (addr4 != NULL) {
224  			struct netlbl_domaddr4_map *map4;
225  			map4 = netlbl_domhsh_addr4_entry(addr4);
226  			type = map4->def.type;
227  			cipsov4 = map4->def.cipso;
228  			netlbl_af4list_audit_addr(audit_buf, 0, NULL,
229  						  addr4->addr, addr4->mask);
230  #if IS_ENABLED(CONFIG_IPV6)
231  		} else if (addr6 != NULL) {
232  			struct netlbl_domaddr6_map *map6;
233  			map6 = netlbl_domhsh_addr6_entry(addr6);
234  			type = map6->def.type;
235  			calipso = map6->def.calipso;
236  			netlbl_af6list_audit_addr(audit_buf, 0, NULL,
237  						  &addr6->addr, &addr6->mask);
238  #endif /* IPv6 */
239  		} else {
240  			type = entry->def.type;
241  			cipsov4 = entry->def.cipso;
242  			calipso = entry->def.calipso;
243  		}
244  		switch (type) {
245  		case NETLBL_NLTYPE_UNLABELED:
246  			audit_log_format(audit_buf, " nlbl_protocol=unlbl");
247  			break;
248  		case NETLBL_NLTYPE_CIPSOV4:
249  			BUG_ON(cipsov4 == NULL);
250  			audit_log_format(audit_buf,
251  					 " nlbl_protocol=cipsov4 cipso_doi=%u",
252  					 cipsov4->doi);
253  			break;
254  		case NETLBL_NLTYPE_CALIPSO:
255  			BUG_ON(calipso == NULL);
256  			audit_log_format(audit_buf,
257  					 " nlbl_protocol=calipso calipso_doi=%u",
258  					 calipso->doi);
259  			break;
260  		}
261  		audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
262  		audit_log_end(audit_buf);
263  	}
264  }
265  
266  /**
267   * netlbl_domhsh_validate - Validate a new domain mapping entry
268   * @entry: the entry to validate
269   *
270   * This function validates the new domain mapping entry to ensure that it is
271   * a valid entry.  Returns zero on success, negative values on failure.
272   *
273   */
netlbl_domhsh_validate(const struct netlbl_dom_map * entry)274  static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
275  {
276  	struct netlbl_af4list *iter4;
277  	struct netlbl_domaddr4_map *map4;
278  #if IS_ENABLED(CONFIG_IPV6)
279  	struct netlbl_af6list *iter6;
280  	struct netlbl_domaddr6_map *map6;
281  #endif /* IPv6 */
282  
283  	if (entry == NULL)
284  		return -EINVAL;
285  
286  	if (entry->family != AF_INET && entry->family != AF_INET6 &&
287  	    (entry->family != AF_UNSPEC ||
288  	     entry->def.type != NETLBL_NLTYPE_UNLABELED))
289  		return -EINVAL;
290  
291  	switch (entry->def.type) {
292  	case NETLBL_NLTYPE_UNLABELED:
293  		if (entry->def.cipso != NULL || entry->def.calipso != NULL ||
294  		    entry->def.addrsel != NULL)
295  			return -EINVAL;
296  		break;
297  	case NETLBL_NLTYPE_CIPSOV4:
298  		if (entry->family != AF_INET ||
299  		    entry->def.cipso == NULL)
300  			return -EINVAL;
301  		break;
302  	case NETLBL_NLTYPE_CALIPSO:
303  		if (entry->family != AF_INET6 ||
304  		    entry->def.calipso == NULL)
305  			return -EINVAL;
306  		break;
307  	case NETLBL_NLTYPE_ADDRSELECT:
308  		netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) {
309  			map4 = netlbl_domhsh_addr4_entry(iter4);
310  			switch (map4->def.type) {
311  			case NETLBL_NLTYPE_UNLABELED:
312  				if (map4->def.cipso != NULL)
313  					return -EINVAL;
314  				break;
315  			case NETLBL_NLTYPE_CIPSOV4:
316  				if (map4->def.cipso == NULL)
317  					return -EINVAL;
318  				break;
319  			default:
320  				return -EINVAL;
321  			}
322  		}
323  #if IS_ENABLED(CONFIG_IPV6)
324  		netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) {
325  			map6 = netlbl_domhsh_addr6_entry(iter6);
326  			switch (map6->def.type) {
327  			case NETLBL_NLTYPE_UNLABELED:
328  				if (map6->def.calipso != NULL)
329  					return -EINVAL;
330  				break;
331  			case NETLBL_NLTYPE_CALIPSO:
332  				if (map6->def.calipso == NULL)
333  					return -EINVAL;
334  				break;
335  			default:
336  				return -EINVAL;
337  			}
338  		}
339  #endif /* IPv6 */
340  		break;
341  	default:
342  		return -EINVAL;
343  	}
344  
345  	return 0;
346  }
347  
348  /*
349   * Domain Hash Table Functions
350   */
351  
352  /**
353   * netlbl_domhsh_init - Init for the domain hash
354   * @size: the number of bits to use for the hash buckets
355   *
356   * Description:
357   * Initializes the domain hash table, should be called only by
358   * netlbl_user_init() during initialization.  Returns zero on success, non-zero
359   * values on error.
360   *
361   */
netlbl_domhsh_init(u32 size)362  int __init netlbl_domhsh_init(u32 size)
363  {
364  	u32 iter;
365  	struct netlbl_domhsh_tbl *hsh_tbl;
366  
367  	if (size == 0)
368  		return -EINVAL;
369  
370  	hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
371  	if (hsh_tbl == NULL)
372  		return -ENOMEM;
373  	hsh_tbl->size = 1 << size;
374  	hsh_tbl->tbl = kcalloc(hsh_tbl->size,
375  			       sizeof(struct list_head),
376  			       GFP_KERNEL);
377  	if (hsh_tbl->tbl == NULL) {
378  		kfree(hsh_tbl);
379  		return -ENOMEM;
380  	}
381  	for (iter = 0; iter < hsh_tbl->size; iter++)
382  		INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
383  
384  	spin_lock(&netlbl_domhsh_lock);
385  	rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
386  	spin_unlock(&netlbl_domhsh_lock);
387  
388  	return 0;
389  }
390  
391  /**
392   * netlbl_domhsh_add - Adds a entry to the domain hash table
393   * @entry: the entry to add
394   * @audit_info: NetLabel audit information
395   *
396   * Description:
397   * Adds a new entry to the domain hash table and handles any updates to the
398   * lower level protocol handler (i.e. CIPSO).  @entry->family may be set to
399   * %AF_UNSPEC which will add an entry that matches all address families.  This
400   * is only useful for the unlabelled type and will only succeed if there is no
401   * existing entry for any address family with the same domain.  Returns zero
402   * on success, negative on failure.
403   *
404   */
netlbl_domhsh_add(struct netlbl_dom_map * entry,struct netlbl_audit * audit_info)405  int netlbl_domhsh_add(struct netlbl_dom_map *entry,
406  		      struct netlbl_audit *audit_info)
407  {
408  	int ret_val = 0;
409  	struct netlbl_dom_map *entry_old, *entry_b;
410  	struct netlbl_af4list *iter4;
411  	struct netlbl_af4list *tmp4;
412  #if IS_ENABLED(CONFIG_IPV6)
413  	struct netlbl_af6list *iter6;
414  	struct netlbl_af6list *tmp6;
415  #endif /* IPv6 */
416  
417  	ret_val = netlbl_domhsh_validate(entry);
418  	if (ret_val != 0)
419  		return ret_val;
420  
421  	/* XXX - we can remove this RCU read lock as the spinlock protects the
422  	 *       entire function, but before we do we need to fixup the
423  	 *       netlbl_af[4,6]list RCU functions to do "the right thing" with
424  	 *       respect to rcu_dereference() when only a spinlock is held. */
425  	rcu_read_lock();
426  	spin_lock(&netlbl_domhsh_lock);
427  	if (entry->domain != NULL)
428  		entry_old = netlbl_domhsh_search(entry->domain, entry->family);
429  	else
430  		entry_old = netlbl_domhsh_search_def(entry->domain,
431  						     entry->family);
432  	if (entry_old == NULL) {
433  		entry->valid = 1;
434  
435  		if (entry->domain != NULL) {
436  			u32 bkt = netlbl_domhsh_hash(entry->domain);
437  			list_add_tail_rcu(&entry->list,
438  				    &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
439  		} else {
440  			INIT_LIST_HEAD(&entry->list);
441  			switch (entry->family) {
442  			case AF_INET:
443  				rcu_assign_pointer(netlbl_domhsh_def_ipv4,
444  						   entry);
445  				break;
446  			case AF_INET6:
447  				rcu_assign_pointer(netlbl_domhsh_def_ipv6,
448  						   entry);
449  				break;
450  			case AF_UNSPEC:
451  				if (entry->def.type !=
452  				    NETLBL_NLTYPE_UNLABELED) {
453  					ret_val = -EINVAL;
454  					goto add_return;
455  				}
456  				entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC);
457  				if (entry_b == NULL) {
458  					ret_val = -ENOMEM;
459  					goto add_return;
460  				}
461  				entry_b->family = AF_INET6;
462  				entry_b->def.type = NETLBL_NLTYPE_UNLABELED;
463  				entry_b->valid = 1;
464  				entry->family = AF_INET;
465  				rcu_assign_pointer(netlbl_domhsh_def_ipv4,
466  						   entry);
467  				rcu_assign_pointer(netlbl_domhsh_def_ipv6,
468  						   entry_b);
469  				break;
470  			default:
471  				/* Already checked in
472  				 * netlbl_domhsh_validate(). */
473  				ret_val = -EINVAL;
474  				goto add_return;
475  			}
476  		}
477  
478  		if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
479  			netlbl_af4list_foreach_rcu(iter4,
480  						   &entry->def.addrsel->list4)
481  				netlbl_domhsh_audit_add(entry, iter4, NULL,
482  							ret_val, audit_info);
483  #if IS_ENABLED(CONFIG_IPV6)
484  			netlbl_af6list_foreach_rcu(iter6,
485  						   &entry->def.addrsel->list6)
486  				netlbl_domhsh_audit_add(entry, NULL, iter6,
487  							ret_val, audit_info);
488  #endif /* IPv6 */
489  		} else
490  			netlbl_domhsh_audit_add(entry, NULL, NULL,
491  						ret_val, audit_info);
492  	} else if (entry_old->def.type == NETLBL_NLTYPE_ADDRSELECT &&
493  		   entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
494  		struct list_head *old_list4;
495  		struct list_head *old_list6;
496  
497  		old_list4 = &entry_old->def.addrsel->list4;
498  		old_list6 = &entry_old->def.addrsel->list6;
499  
500  		/* we only allow the addition of address selectors if all of
501  		 * the selectors do not exist in the existing domain map */
502  		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4)
503  			if (netlbl_af4list_search_exact(iter4->addr,
504  							iter4->mask,
505  							old_list4)) {
506  				ret_val = -EEXIST;
507  				goto add_return;
508  			}
509  #if IS_ENABLED(CONFIG_IPV6)
510  		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6)
511  			if (netlbl_af6list_search_exact(&iter6->addr,
512  							&iter6->mask,
513  							old_list6)) {
514  				ret_val = -EEXIST;
515  				goto add_return;
516  			}
517  #endif /* IPv6 */
518  
519  		netlbl_af4list_foreach_safe(iter4, tmp4,
520  					    &entry->def.addrsel->list4) {
521  			netlbl_af4list_remove_entry(iter4);
522  			iter4->valid = 1;
523  			ret_val = netlbl_af4list_add(iter4, old_list4);
524  			netlbl_domhsh_audit_add(entry_old, iter4, NULL,
525  						ret_val, audit_info);
526  			if (ret_val != 0)
527  				goto add_return;
528  		}
529  #if IS_ENABLED(CONFIG_IPV6)
530  		netlbl_af6list_foreach_safe(iter6, tmp6,
531  					    &entry->def.addrsel->list6) {
532  			netlbl_af6list_remove_entry(iter6);
533  			iter6->valid = 1;
534  			ret_val = netlbl_af6list_add(iter6, old_list6);
535  			netlbl_domhsh_audit_add(entry_old, NULL, iter6,
536  						ret_val, audit_info);
537  			if (ret_val != 0)
538  				goto add_return;
539  		}
540  #endif /* IPv6 */
541  		/* cleanup the new entry since we've moved everything over */
542  		netlbl_domhsh_free_entry(&entry->rcu);
543  	} else
544  		ret_val = -EINVAL;
545  
546  add_return:
547  	spin_unlock(&netlbl_domhsh_lock);
548  	rcu_read_unlock();
549  	return ret_val;
550  }
551  
552  /**
553   * netlbl_domhsh_add_default - Adds the default entry to the domain hash table
554   * @entry: the entry to add
555   * @audit_info: NetLabel audit information
556   *
557   * Description:
558   * Adds a new default entry to the domain hash table and handles any updates
559   * to the lower level protocol handler (i.e. CIPSO).  Returns zero on success,
560   * negative on failure.
561   *
562   */
netlbl_domhsh_add_default(struct netlbl_dom_map * entry,struct netlbl_audit * audit_info)563  int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
564  			      struct netlbl_audit *audit_info)
565  {
566  	return netlbl_domhsh_add(entry, audit_info);
567  }
568  
569  /**
570   * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
571   * @entry: the entry to remove
572   * @audit_info: NetLabel audit information
573   *
574   * Description:
575   * Removes an entry from the domain hash table and handles any updates to the
576   * lower level protocol handler (i.e. CIPSO).  Caller is responsible for
577   * ensuring that the RCU read lock is held.  Returns zero on success, negative
578   * on failure.
579   *
580   */
netlbl_domhsh_remove_entry(struct netlbl_dom_map * entry,struct netlbl_audit * audit_info)581  int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
582  			       struct netlbl_audit *audit_info)
583  {
584  	int ret_val = 0;
585  	struct audit_buffer *audit_buf;
586  	struct netlbl_af4list *iter4;
587  	struct netlbl_domaddr4_map *map4;
588  #if IS_ENABLED(CONFIG_IPV6)
589  	struct netlbl_af6list *iter6;
590  	struct netlbl_domaddr6_map *map6;
591  #endif /* IPv6 */
592  
593  	if (entry == NULL)
594  		return -ENOENT;
595  
596  	spin_lock(&netlbl_domhsh_lock);
597  	if (entry->valid) {
598  		entry->valid = 0;
599  		if (entry == rcu_dereference(netlbl_domhsh_def_ipv4))
600  			RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL);
601  		else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6))
602  			RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL);
603  		else
604  			list_del_rcu(&entry->list);
605  	} else
606  		ret_val = -ENOENT;
607  	spin_unlock(&netlbl_domhsh_lock);
608  
609  	if (ret_val)
610  		return ret_val;
611  
612  	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
613  	if (audit_buf != NULL) {
614  		audit_log_format(audit_buf,
615  				 " nlbl_domain=%s res=1",
616  				 entry->domain ? entry->domain : "(default)");
617  		audit_log_end(audit_buf);
618  	}
619  
620  	switch (entry->def.type) {
621  	case NETLBL_NLTYPE_ADDRSELECT:
622  		netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4) {
623  			map4 = netlbl_domhsh_addr4_entry(iter4);
624  			cipso_v4_doi_putdef(map4->def.cipso);
625  		}
626  #if IS_ENABLED(CONFIG_IPV6)
627  		netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) {
628  			map6 = netlbl_domhsh_addr6_entry(iter6);
629  			calipso_doi_putdef(map6->def.calipso);
630  		}
631  #endif /* IPv6 */
632  		break;
633  	case NETLBL_NLTYPE_CIPSOV4:
634  		cipso_v4_doi_putdef(entry->def.cipso);
635  		break;
636  #if IS_ENABLED(CONFIG_IPV6)
637  	case NETLBL_NLTYPE_CALIPSO:
638  		calipso_doi_putdef(entry->def.calipso);
639  		break;
640  #endif /* IPv6 */
641  	}
642  	call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
643  
644  	return ret_val;
645  }
646  
647  /**
648   * netlbl_domhsh_remove_af4 - Removes an address selector entry
649   * @domain: the domain
650   * @addr: IPv4 address
651   * @mask: IPv4 address mask
652   * @audit_info: NetLabel audit information
653   *
654   * Description:
655   * Removes an individual address selector from a domain mapping and potentially
656   * the entire mapping if it is empty.  Returns zero on success, negative values
657   * on failure.
658   *
659   */
netlbl_domhsh_remove_af4(const char * domain,const struct in_addr * addr,const struct in_addr * mask,struct netlbl_audit * audit_info)660  int netlbl_domhsh_remove_af4(const char *domain,
661  			     const struct in_addr *addr,
662  			     const struct in_addr *mask,
663  			     struct netlbl_audit *audit_info)
664  {
665  	struct netlbl_dom_map *entry_map;
666  	struct netlbl_af4list *entry_addr;
667  	struct netlbl_af4list *iter4;
668  #if IS_ENABLED(CONFIG_IPV6)
669  	struct netlbl_af6list *iter6;
670  #endif /* IPv6 */
671  	struct netlbl_domaddr4_map *entry;
672  
673  	rcu_read_lock();
674  
675  	if (domain)
676  		entry_map = netlbl_domhsh_search(domain, AF_INET);
677  	else
678  		entry_map = netlbl_domhsh_search_def(domain, AF_INET);
679  	if (entry_map == NULL ||
680  	    entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
681  		goto remove_af4_failure;
682  
683  	spin_lock(&netlbl_domhsh_lock);
684  	entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
685  					   &entry_map->def.addrsel->list4);
686  	spin_unlock(&netlbl_domhsh_lock);
687  
688  	if (entry_addr == NULL)
689  		goto remove_af4_failure;
690  	netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
691  		goto remove_af4_single_addr;
692  #if IS_ENABLED(CONFIG_IPV6)
693  	netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
694  		goto remove_af4_single_addr;
695  #endif /* IPv6 */
696  	/* the domain mapping is empty so remove it from the mapping table */
697  	netlbl_domhsh_remove_entry(entry_map, audit_info);
698  
699  remove_af4_single_addr:
700  	rcu_read_unlock();
701  	/* yick, we can't use call_rcu here because we don't have a rcu head
702  	 * pointer but hopefully this should be a rare case so the pause
703  	 * shouldn't be a problem */
704  	synchronize_rcu();
705  	entry = netlbl_domhsh_addr4_entry(entry_addr);
706  	cipso_v4_doi_putdef(entry->def.cipso);
707  	kfree(entry);
708  	return 0;
709  
710  remove_af4_failure:
711  	rcu_read_unlock();
712  	return -ENOENT;
713  }
714  
715  #if IS_ENABLED(CONFIG_IPV6)
716  /**
717   * netlbl_domhsh_remove_af6 - Removes an address selector entry
718   * @domain: the domain
719   * @addr: IPv6 address
720   * @mask: IPv6 address mask
721   * @audit_info: NetLabel audit information
722   *
723   * Description:
724   * Removes an individual address selector from a domain mapping and potentially
725   * the entire mapping if it is empty.  Returns zero on success, negative values
726   * on failure.
727   *
728   */
netlbl_domhsh_remove_af6(const char * domain,const struct in6_addr * addr,const struct in6_addr * mask,struct netlbl_audit * audit_info)729  int netlbl_domhsh_remove_af6(const char *domain,
730  			     const struct in6_addr *addr,
731  			     const struct in6_addr *mask,
732  			     struct netlbl_audit *audit_info)
733  {
734  	struct netlbl_dom_map *entry_map;
735  	struct netlbl_af6list *entry_addr;
736  	struct netlbl_af4list *iter4;
737  	struct netlbl_af6list *iter6;
738  	struct netlbl_domaddr6_map *entry;
739  
740  	rcu_read_lock();
741  
742  	if (domain)
743  		entry_map = netlbl_domhsh_search(domain, AF_INET6);
744  	else
745  		entry_map = netlbl_domhsh_search_def(domain, AF_INET6);
746  	if (entry_map == NULL ||
747  	    entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
748  		goto remove_af6_failure;
749  
750  	spin_lock(&netlbl_domhsh_lock);
751  	entry_addr = netlbl_af6list_remove(addr, mask,
752  					   &entry_map->def.addrsel->list6);
753  	spin_unlock(&netlbl_domhsh_lock);
754  
755  	if (entry_addr == NULL)
756  		goto remove_af6_failure;
757  	netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
758  		goto remove_af6_single_addr;
759  	netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
760  		goto remove_af6_single_addr;
761  	/* the domain mapping is empty so remove it from the mapping table */
762  	netlbl_domhsh_remove_entry(entry_map, audit_info);
763  
764  remove_af6_single_addr:
765  	rcu_read_unlock();
766  	/* yick, we can't use call_rcu here because we don't have a rcu head
767  	 * pointer but hopefully this should be a rare case so the pause
768  	 * shouldn't be a problem */
769  	synchronize_rcu();
770  	entry = netlbl_domhsh_addr6_entry(entry_addr);
771  	calipso_doi_putdef(entry->def.calipso);
772  	kfree(entry);
773  	return 0;
774  
775  remove_af6_failure:
776  	rcu_read_unlock();
777  	return -ENOENT;
778  }
779  #endif /* IPv6 */
780  
781  /**
782   * netlbl_domhsh_remove - Removes an entry from the domain hash table
783   * @domain: the domain to remove
784   * @family: address family
785   * @audit_info: NetLabel audit information
786   *
787   * Description:
788   * Removes an entry from the domain hash table and handles any updates to the
789   * lower level protocol handler (i.e. CIPSO).  @family may be %AF_UNSPEC which
790   * removes all address family entries.  Returns zero on success, negative on
791   * failure.
792   *
793   */
netlbl_domhsh_remove(const char * domain,u16 family,struct netlbl_audit * audit_info)794  int netlbl_domhsh_remove(const char *domain, u16 family,
795  			 struct netlbl_audit *audit_info)
796  {
797  	int ret_val = -EINVAL;
798  	struct netlbl_dom_map *entry;
799  
800  	rcu_read_lock();
801  
802  	if (family == AF_INET || family == AF_UNSPEC) {
803  		if (domain)
804  			entry = netlbl_domhsh_search(domain, AF_INET);
805  		else
806  			entry = netlbl_domhsh_search_def(domain, AF_INET);
807  		ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
808  		if (ret_val && ret_val != -ENOENT)
809  			goto done;
810  	}
811  	if (family == AF_INET6 || family == AF_UNSPEC) {
812  		int ret_val2;
813  
814  		if (domain)
815  			entry = netlbl_domhsh_search(domain, AF_INET6);
816  		else
817  			entry = netlbl_domhsh_search_def(domain, AF_INET6);
818  		ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
819  		if (ret_val2 != -ENOENT)
820  			ret_val = ret_val2;
821  	}
822  done:
823  	rcu_read_unlock();
824  
825  	return ret_val;
826  }
827  
828  /**
829   * netlbl_domhsh_remove_default - Removes the default entry from the table
830   * @family: address family
831   * @audit_info: NetLabel audit information
832   *
833   * Description:
834   * Removes/resets the default entry corresponding to @family from the domain
835   * hash table and handles any updates to the lower level protocol handler
836   * (i.e. CIPSO).  @family may be %AF_UNSPEC which removes all address family
837   * entries.  Returns zero on success, negative on failure.
838   *
839   */
netlbl_domhsh_remove_default(u16 family,struct netlbl_audit * audit_info)840  int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
841  {
842  	return netlbl_domhsh_remove(NULL, family, audit_info);
843  }
844  
845  /**
846   * netlbl_domhsh_getentry - Get an entry from the domain hash table
847   * @domain: the domain name to search for
848   * @family: address family
849   *
850   * Description:
851   * Look through the domain hash table searching for an entry to match @domain,
852   * with address family @family, return a pointer to a copy of the entry or
853   * NULL.  The caller is responsible for ensuring that rcu_read_[un]lock() is
854   * called.
855   *
856   */
netlbl_domhsh_getentry(const char * domain,u16 family)857  struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
858  {
859  	if (family == AF_UNSPEC)
860  		return NULL;
861  	return netlbl_domhsh_search_def(domain, family);
862  }
863  
864  /**
865   * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
866   * @domain: the domain name to search for
867   * @addr: the IP address to search for
868   *
869   * Description:
870   * Look through the domain hash table searching for an entry to match @domain
871   * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
872   * responsible for ensuring that rcu_read_[un]lock() is called.
873   *
874   */
netlbl_domhsh_getentry_af4(const char * domain,__be32 addr)875  struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
876  						     __be32 addr)
877  {
878  	struct netlbl_dom_map *dom_iter;
879  	struct netlbl_af4list *addr_iter;
880  
881  	dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
882  	if (dom_iter == NULL)
883  		return NULL;
884  
885  	if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
886  		return &dom_iter->def;
887  	addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
888  	if (addr_iter == NULL)
889  		return NULL;
890  	return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
891  }
892  
893  #if IS_ENABLED(CONFIG_IPV6)
894  /**
895   * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
896   * @domain: the domain name to search for
897   * @addr: the IP address to search for
898   *
899   * Description:
900   * Look through the domain hash table searching for an entry to match @domain
901   * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
902   * responsible for ensuring that rcu_read_[un]lock() is called.
903   *
904   */
netlbl_domhsh_getentry_af6(const char * domain,const struct in6_addr * addr)905  struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
906  						   const struct in6_addr *addr)
907  {
908  	struct netlbl_dom_map *dom_iter;
909  	struct netlbl_af6list *addr_iter;
910  
911  	dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
912  	if (dom_iter == NULL)
913  		return NULL;
914  
915  	if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
916  		return &dom_iter->def;
917  	addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
918  	if (addr_iter == NULL)
919  		return NULL;
920  	return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
921  }
922  #endif /* IPv6 */
923  
924  /**
925   * netlbl_domhsh_walk - Iterate through the domain mapping hash table
926   * @skip_bkt: the number of buckets to skip at the start
927   * @skip_chain: the number of entries to skip in the first iterated bucket
928   * @callback: callback for each entry
929   * @cb_arg: argument for the callback function
930   *
931   * Description:
932   * Iterate over the domain mapping hash table, skipping the first @skip_bkt
933   * buckets and @skip_chain entries.  For each entry in the table call
934   * @callback, if @callback returns a negative value stop 'walking' through the
935   * table and return.  Updates the values in @skip_bkt and @skip_chain on
936   * return.  Returns zero on success, negative values on failure.
937   *
938   */
netlbl_domhsh_walk(u32 * skip_bkt,u32 * skip_chain,int (* callback)(struct netlbl_dom_map * entry,void * arg),void * cb_arg)939  int netlbl_domhsh_walk(u32 *skip_bkt,
940  		     u32 *skip_chain,
941  		     int (*callback) (struct netlbl_dom_map *entry, void *arg),
942  		     void *cb_arg)
943  {
944  	int ret_val = -ENOENT;
945  	u32 iter_bkt;
946  	struct list_head *iter_list;
947  	struct netlbl_dom_map *iter_entry;
948  	u32 chain_cnt = 0;
949  
950  	rcu_read_lock();
951  	for (iter_bkt = *skip_bkt;
952  	     iter_bkt < rcu_dereference(netlbl_domhsh)->size;
953  	     iter_bkt++, chain_cnt = 0) {
954  		iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
955  		list_for_each_entry_rcu(iter_entry, iter_list, list)
956  			if (iter_entry->valid) {
957  				if (chain_cnt++ < *skip_chain)
958  					continue;
959  				ret_val = callback(iter_entry, cb_arg);
960  				if (ret_val < 0) {
961  					chain_cnt--;
962  					goto walk_return;
963  				}
964  			}
965  	}
966  
967  walk_return:
968  	rcu_read_unlock();
969  	*skip_bkt = iter_bkt;
970  	*skip_chain = chain_cnt;
971  	return ret_val;
972  }
973