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