Branch data Line data Source code
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 : : */
76 : 0 : 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 : 0 : ptr = container_of(entry, struct netlbl_dom_map, rcu);
87 [ # # ]: 0 : if (ptr->def.type == NETLBL_NLTYPE_ADDRSELECT) {
88 [ # # ]: 0 : netlbl_af4list_foreach_safe(iter4, tmp4,
89 : : &ptr->def.addrsel->list4) {
90 : 0 : netlbl_af4list_remove_entry(iter4);
91 : 0 : kfree(netlbl_domhsh_addr4_entry(iter4));
92 : : }
93 : : #if IS_ENABLED(CONFIG_IPV6)
94 [ # # ]: 0 : netlbl_af6list_foreach_safe(iter6, tmp6,
95 : : &ptr->def.addrsel->list6) {
96 : 0 : netlbl_af6list_remove_entry(iter6);
97 : 0 : kfree(netlbl_domhsh_addr6_entry(iter6));
98 : : }
99 : : #endif /* IPv6 */
100 : : }
101 : 0 : kfree(ptr->domain);
102 : 0 : kfree(ptr);
103 : 0 : }
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 : : */
116 : 0 : 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 [ # # ]: 0 : for (iter = 0, val = 0, len = strlen(key); iter < len; iter++)
126 : 0 : val = (val << 4 | (val >> (8 * sizeof(u32) - 4))) ^ key[iter];
127 : 0 : 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 : : */
141 : 0 : 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 [ # # ]: 0 : if (domain != NULL) {
148 : 0 : bkt = netlbl_domhsh_hash(domain);
149 : 0 : bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
150 [ # # ]: 0 : list_for_each_entry_rcu(iter, bkt_list, list)
151 [ # # ][ # # ]: 0 : 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 : : */
171 : 0 : static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
172 : : {
173 : : struct netlbl_dom_map *entry;
174 : :
175 : 0 : entry = netlbl_domhsh_search(domain);
176 [ # # ]: 0 : if (entry == NULL) {
177 : 0 : entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def);
178 [ # # ][ # # ]: 0 : if (entry != NULL && !entry->valid)
179 : : entry = NULL;
180 : : }
181 : :
182 : 0 : 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 : : */
199 : 0 : 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 : 0 : audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
210 [ # # ]: 0 : if (audit_buf != NULL) {
211 [ # # ]: 0 : audit_log_format(audit_buf, " nlbl_domain=%s",
212 : 0 : entry->domain ? entry->domain : "(default)");
213 [ # # ]: 0 : if (addr4 != NULL) {
214 : : struct netlbl_domaddr4_map *map4;
215 : : map4 = netlbl_domhsh_addr4_entry(addr4);
216 : 0 : type = map4->def.type;
217 : 0 : cipsov4 = map4->def.cipso;
218 : 0 : netlbl_af4list_audit_addr(audit_buf, 0, NULL,
219 : : addr4->addr, addr4->mask);
220 : : #if IS_ENABLED(CONFIG_IPV6)
221 [ # # ]: 0 : } else if (addr6 != NULL) {
222 : : struct netlbl_domaddr6_map *map6;
223 : : map6 = netlbl_domhsh_addr6_entry(addr6);
224 : 0 : type = map6->def.type;
225 : 0 : netlbl_af6list_audit_addr(audit_buf, 0, NULL,
226 : 0 : &addr6->addr, &addr6->mask);
227 : : #endif /* IPv6 */
228 : : } else {
229 : 0 : type = entry->def.type;
230 : 0 : cipsov4 = entry->def.cipso;
231 : : }
232 [ # # # ]: 0 : switch (type) {
233 : : case NETLBL_NLTYPE_UNLABELED:
234 : 0 : audit_log_format(audit_buf, " nlbl_protocol=unlbl");
235 : 0 : break;
236 : : case NETLBL_NLTYPE_CIPSOV4:
237 [ # # ]: 0 : BUG_ON(cipsov4 == NULL);
238 : 0 : audit_log_format(audit_buf,
239 : : " nlbl_protocol=cipsov4 cipso_doi=%u",
240 : : cipsov4->doi);
241 : 0 : break;
242 : : }
243 : 0 : audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
244 : 0 : audit_log_end(audit_buf);
245 : : }
246 : 0 : }
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 : : */
256 : 0 : 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 [ # # ]: 0 : if (entry == NULL)
266 : : return -EINVAL;
267 : :
268 [ # # # # ]: 0 : switch (entry->def.type) {
269 : : case NETLBL_NLTYPE_UNLABELED:
270 [ # # ][ # # ]: 0 : if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
271 : : return -EINVAL;
272 : : break;
273 : : case NETLBL_NLTYPE_CIPSOV4:
274 [ # # ]: 0 : if (entry->def.cipso == NULL)
275 : : return -EINVAL;
276 : : break;
277 : : case NETLBL_NLTYPE_ADDRSELECT:
278 [ # # ]: 0 : netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) {
279 : : map4 = netlbl_domhsh_addr4_entry(iter4);
280 [ # # # ]: 0 : switch (map4->def.type) {
281 : : case NETLBL_NLTYPE_UNLABELED:
282 [ # # ]: 0 : if (map4->def.cipso != NULL)
283 : : return -EINVAL;
284 : : break;
285 : : case NETLBL_NLTYPE_CIPSOV4:
286 [ # # ]: 0 : if (map4->def.cipso == NULL)
287 : : return -EINVAL;
288 : : break;
289 : : default:
290 : : return -EINVAL;
291 : : }
292 : : }
293 : : #if IS_ENABLED(CONFIG_IPV6)
294 [ # # ]: 0 : netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) {
295 : : map6 = netlbl_domhsh_addr6_entry(iter6);
296 [ # # ]: 0 : switch (map6->def.type) {
297 : : case NETLBL_NLTYPE_UNLABELED:
298 : : break;
299 : : default:
300 : : return -EINVAL;
301 : : }
302 : : }
303 : : #endif /* IPv6 */
304 : : break;
305 : : default:
306 : : return -EINVAL;
307 : : }
308 : :
309 : 0 : return 0;
310 : : }
311 : :
312 : : /*
313 : : * Domain Hash Table Functions
314 : : */
315 : :
316 : : /**
317 : : * netlbl_domhsh_init - Init for the domain hash
318 : : * @size: the number of bits to use for the hash buckets
319 : : *
320 : : * Description:
321 : : * Initializes the domain hash table, should be called only by
322 : : * netlbl_user_init() during initialization. Returns zero on success, non-zero
323 : : * values on error.
324 : : *
325 : : */
326 : 0 : int __init netlbl_domhsh_init(u32 size)
327 : : {
328 : : u32 iter;
329 : : struct netlbl_domhsh_tbl *hsh_tbl;
330 : :
331 [ # # ]: 0 : if (size == 0)
332 : : return -EINVAL;
333 : :
334 : : hsh_tbl = kmalloc(sizeof(*hsh_tbl), GFP_KERNEL);
335 [ # # ]: 0 : if (hsh_tbl == NULL)
336 : : return -ENOMEM;
337 : 0 : hsh_tbl->size = 1 << size;
338 : 0 : hsh_tbl->tbl = kcalloc(hsh_tbl->size,
339 : : sizeof(struct list_head),
340 : : GFP_KERNEL);
341 [ # # ]: 0 : if (hsh_tbl->tbl == NULL) {
342 : 0 : kfree(hsh_tbl);
343 : 0 : return -ENOMEM;
344 : : }
345 [ # # ]: 0 : for (iter = 0; iter < hsh_tbl->size; iter++)
346 : 0 : INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
347 : :
348 : : spin_lock(&netlbl_domhsh_lock);
349 : 0 : rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
350 : : spin_unlock(&netlbl_domhsh_lock);
351 : :
352 : 0 : return 0;
353 : : }
354 : :
355 : : /**
356 : : * netlbl_domhsh_add - Adds a entry to the domain hash table
357 : : * @entry: the entry to add
358 : : * @audit_info: NetLabel audit information
359 : : *
360 : : * Description:
361 : : * Adds a new entry to the domain hash table and handles any updates to the
362 : : * lower level protocol handler (i.e. CIPSO). Returns zero on success,
363 : : * negative on failure.
364 : : *
365 : : */
366 : 0 : int netlbl_domhsh_add(struct netlbl_dom_map *entry,
367 : : struct netlbl_audit *audit_info)
368 : : {
369 : : int ret_val = 0;
370 : : struct netlbl_dom_map *entry_old;
371 : : struct netlbl_af4list *iter4;
372 : : struct netlbl_af4list *tmp4;
373 : : #if IS_ENABLED(CONFIG_IPV6)
374 : : struct netlbl_af6list *iter6;
375 : : struct netlbl_af6list *tmp6;
376 : : #endif /* IPv6 */
377 : :
378 : 0 : ret_val = netlbl_domhsh_validate(entry);
379 [ # # ]: 0 : if (ret_val != 0)
380 : : return ret_val;
381 : :
382 : : /* XXX - we can remove this RCU read lock as the spinlock protects the
383 : : * entire function, but before we do we need to fixup the
384 : : * netlbl_af[4,6]list RCU functions to do "the right thing" with
385 : : * respect to rcu_dereference() when only a spinlock is held. */
386 : : rcu_read_lock();
387 : : spin_lock(&netlbl_domhsh_lock);
388 [ # # ]: 0 : if (entry->domain != NULL)
389 : 0 : entry_old = netlbl_domhsh_search(entry->domain);
390 : : else
391 : 0 : entry_old = netlbl_domhsh_search_def(entry->domain);
392 [ # # ]: 0 : if (entry_old == NULL) {
393 : 0 : entry->valid = 1;
394 : :
395 [ # # ]: 0 : if (entry->domain != NULL) {
396 : 0 : u32 bkt = netlbl_domhsh_hash(entry->domain);
397 : 0 : list_add_tail_rcu(&entry->list,
398 : 0 : &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
399 : : } else {
400 : 0 : INIT_LIST_HEAD(&entry->list);
401 : 0 : rcu_assign_pointer(netlbl_domhsh_def, entry);
402 : : }
403 : :
404 [ # # ]: 0 : if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
405 [ # # ]: 0 : netlbl_af4list_foreach_rcu(iter4,
406 : : &entry->def.addrsel->list4)
407 : 0 : netlbl_domhsh_audit_add(entry, iter4, NULL,
408 : : ret_val, audit_info);
409 : : #if IS_ENABLED(CONFIG_IPV6)
410 [ # # ]: 0 : netlbl_af6list_foreach_rcu(iter6,
411 : : &entry->def.addrsel->list6)
412 : 0 : netlbl_domhsh_audit_add(entry, NULL, iter6,
413 : : ret_val, audit_info);
414 : : #endif /* IPv6 */
415 : : } else
416 : 0 : netlbl_domhsh_audit_add(entry, NULL, NULL,
417 : : ret_val, audit_info);
418 [ # # ][ # # ]: 0 : } else if (entry_old->def.type == NETLBL_NLTYPE_ADDRSELECT &&
419 : 0 : entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
420 : : struct list_head *old_list4;
421 : : struct list_head *old_list6;
422 : :
423 : 0 : old_list4 = &entry_old->def.addrsel->list4;
424 : 0 : old_list6 = &entry_old->def.addrsel->list6;
425 : :
426 : : /* we only allow the addition of address selectors if all of
427 : : * the selectors do not exist in the existing domain map */
428 [ # # ]: 0 : netlbl_af4list_foreach_rcu(iter4, &entry->def.addrsel->list4)
429 [ # # ]: 0 : if (netlbl_af4list_search_exact(iter4->addr,
430 : : iter4->mask,
431 : : old_list4)) {
432 : : ret_val = -EEXIST;
433 : : goto add_return;
434 : : }
435 : : #if IS_ENABLED(CONFIG_IPV6)
436 [ # # ]: 0 : netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6)
437 [ # # ]: 0 : if (netlbl_af6list_search_exact(&iter6->addr,
438 : 0 : &iter6->mask,
439 : : old_list6)) {
440 : : ret_val = -EEXIST;
441 : : goto add_return;
442 : : }
443 : : #endif /* IPv6 */
444 : :
445 [ # # ]: 0 : netlbl_af4list_foreach_safe(iter4, tmp4,
446 : : &entry->def.addrsel->list4) {
447 : 0 : netlbl_af4list_remove_entry(iter4);
448 : 0 : iter4->valid = 1;
449 : 0 : ret_val = netlbl_af4list_add(iter4, old_list4);
450 : 0 : netlbl_domhsh_audit_add(entry_old, iter4, NULL,
451 : : ret_val, audit_info);
452 [ # # ]: 0 : if (ret_val != 0)
453 : : goto add_return;
454 : : }
455 : : #if IS_ENABLED(CONFIG_IPV6)
456 [ # # ]: 0 : netlbl_af6list_foreach_safe(iter6, tmp6,
457 : : &entry->def.addrsel->list6) {
458 : 0 : netlbl_af6list_remove_entry(iter6);
459 : 0 : iter6->valid = 1;
460 : 0 : ret_val = netlbl_af6list_add(iter6, old_list6);
461 : 0 : netlbl_domhsh_audit_add(entry_old, NULL, iter6,
462 : : ret_val, audit_info);
463 [ # # ]: 0 : if (ret_val != 0)
464 : : goto add_return;
465 : : }
466 : : #endif /* IPv6 */
467 : : } else
468 : : ret_val = -EINVAL;
469 : :
470 : : add_return:
471 : : spin_unlock(&netlbl_domhsh_lock);
472 : : rcu_read_unlock();
473 : 0 : return ret_val;
474 : : }
475 : :
476 : : /**
477 : : * netlbl_domhsh_add_default - Adds the default entry to the domain hash table
478 : : * @entry: the entry to add
479 : : * @audit_info: NetLabel audit information
480 : : *
481 : : * Description:
482 : : * Adds a new default entry to the domain hash table and handles any updates
483 : : * to the lower level protocol handler (i.e. CIPSO). Returns zero on success,
484 : : * negative on failure.
485 : : *
486 : : */
487 : 0 : int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
488 : : struct netlbl_audit *audit_info)
489 : : {
490 : 0 : return netlbl_domhsh_add(entry, audit_info);
491 : : }
492 : :
493 : : /**
494 : : * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
495 : : * @entry: the entry to remove
496 : : * @audit_info: NetLabel audit information
497 : : *
498 : : * Description:
499 : : * Removes an entry from the domain hash table and handles any updates to the
500 : : * lower level protocol handler (i.e. CIPSO). Caller is responsible for
501 : : * ensuring that the RCU read lock is held. Returns zero on success, negative
502 : : * on failure.
503 : : *
504 : : */
505 : 0 : int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
506 : : struct netlbl_audit *audit_info)
507 : : {
508 : : int ret_val = 0;
509 : : struct audit_buffer *audit_buf;
510 : :
511 [ # # ]: 0 : if (entry == NULL)
512 : : return -ENOENT;
513 : :
514 : : spin_lock(&netlbl_domhsh_lock);
515 [ # # ]: 0 : if (entry->valid) {
516 : 0 : entry->valid = 0;
517 [ # # ]: 0 : if (entry != rcu_dereference(netlbl_domhsh_def))
518 : : list_del_rcu(&entry->list);
519 : : else
520 : 0 : RCU_INIT_POINTER(netlbl_domhsh_def, NULL);
521 : : } else
522 : : ret_val = -ENOENT;
523 : : spin_unlock(&netlbl_domhsh_lock);
524 : :
525 : 0 : audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
526 [ # # ]: 0 : if (audit_buf != NULL) {
527 [ # # ]: 0 : audit_log_format(audit_buf,
528 : : " nlbl_domain=%s res=%u",
529 : 0 : entry->domain ? entry->domain : "(default)",
530 : : ret_val == 0 ? 1 : 0);
531 : 0 : audit_log_end(audit_buf);
532 : : }
533 : :
534 [ # # ]: 0 : if (ret_val == 0) {
535 : : struct netlbl_af4list *iter4;
536 : : struct netlbl_domaddr4_map *map4;
537 : :
538 [ # # # ]: 0 : switch (entry->def.type) {
539 : : case NETLBL_NLTYPE_ADDRSELECT:
540 [ # # ]: 0 : netlbl_af4list_foreach_rcu(iter4,
541 : : &entry->def.addrsel->list4) {
542 : : map4 = netlbl_domhsh_addr4_entry(iter4);
543 : 0 : cipso_v4_doi_putdef(map4->def.cipso);
544 : : }
545 : : /* no need to check the IPv6 list since we currently
546 : : * support only unlabeled protocols for IPv6 */
547 : : break;
548 : : case NETLBL_NLTYPE_CIPSOV4:
549 : 0 : cipso_v4_doi_putdef(entry->def.cipso);
550 : 0 : break;
551 : : }
552 : 0 : call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
553 : : }
554 : :
555 : 0 : return ret_val;
556 : : }
557 : :
558 : : /**
559 : : * netlbl_domhsh_remove_af4 - Removes an address selector entry
560 : : * @domain: the domain
561 : : * @addr: IPv4 address
562 : : * @mask: IPv4 address mask
563 : : * @audit_info: NetLabel audit information
564 : : *
565 : : * Description:
566 : : * Removes an individual address selector from a domain mapping and potentially
567 : : * the entire mapping if it is empty. Returns zero on success, negative values
568 : : * on failure.
569 : : *
570 : : */
571 : 0 : int netlbl_domhsh_remove_af4(const char *domain,
572 : : const struct in_addr *addr,
573 : : const struct in_addr *mask,
574 : : struct netlbl_audit *audit_info)
575 : : {
576 : : struct netlbl_dom_map *entry_map;
577 : : struct netlbl_af4list *entry_addr;
578 : : struct netlbl_af4list *iter4;
579 : : #if IS_ENABLED(CONFIG_IPV6)
580 : : struct netlbl_af6list *iter6;
581 : : #endif /* IPv6 */
582 : : struct netlbl_domaddr4_map *entry;
583 : :
584 : : rcu_read_lock();
585 : :
586 [ # # ]: 0 : if (domain)
587 : 0 : entry_map = netlbl_domhsh_search(domain);
588 : : else
589 : 0 : entry_map = netlbl_domhsh_search_def(domain);
590 [ # # ][ # # ]: 0 : if (entry_map == NULL ||
591 : 0 : entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
592 : : goto remove_af4_failure;
593 : :
594 : : spin_lock(&netlbl_domhsh_lock);
595 : 0 : entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
596 : 0 : &entry_map->def.addrsel->list4);
597 : : spin_unlock(&netlbl_domhsh_lock);
598 : :
599 [ # # ]: 0 : if (entry_addr == NULL)
600 : : goto remove_af4_failure;
601 [ # # ]: 0 : netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
602 : : goto remove_af4_single_addr;
603 : : #if IS_ENABLED(CONFIG_IPV6)
604 [ # # ]: 0 : netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
605 : : goto remove_af4_single_addr;
606 : : #endif /* IPv6 */
607 : : /* the domain mapping is empty so remove it from the mapping table */
608 : 0 : netlbl_domhsh_remove_entry(entry_map, audit_info);
609 : :
610 : : remove_af4_single_addr:
611 : : rcu_read_unlock();
612 : : /* yick, we can't use call_rcu here because we don't have a rcu head
613 : : * pointer but hopefully this should be a rare case so the pause
614 : : * shouldn't be a problem */
615 : : synchronize_rcu();
616 : 0 : entry = netlbl_domhsh_addr4_entry(entry_addr);
617 : 0 : cipso_v4_doi_putdef(entry->def.cipso);
618 : 0 : kfree(entry);
619 : 0 : return 0;
620 : :
621 : : remove_af4_failure:
622 : : rcu_read_unlock();
623 : 0 : return -ENOENT;
624 : : }
625 : :
626 : : /**
627 : : * netlbl_domhsh_remove - Removes an entry from the domain hash table
628 : : * @domain: the domain to remove
629 : : * @audit_info: NetLabel audit information
630 : : *
631 : : * Description:
632 : : * Removes an entry from the domain hash table and handles any updates to the
633 : : * lower level protocol handler (i.e. CIPSO). Returns zero on success,
634 : : * negative on failure.
635 : : *
636 : : */
637 : 0 : int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
638 : : {
639 : : int ret_val;
640 : : struct netlbl_dom_map *entry;
641 : :
642 : : rcu_read_lock();
643 [ # # ]: 0 : if (domain)
644 : 0 : entry = netlbl_domhsh_search(domain);
645 : : else
646 : 0 : entry = netlbl_domhsh_search_def(domain);
647 : 0 : ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
648 : : rcu_read_unlock();
649 : :
650 : 0 : return ret_val;
651 : : }
652 : :
653 : : /**
654 : : * netlbl_domhsh_remove_default - Removes the default entry from the table
655 : : * @audit_info: NetLabel audit information
656 : : *
657 : : * Description:
658 : : * Removes/resets the default entry for the domain hash table and handles any
659 : : * updates to the lower level protocol handler (i.e. CIPSO). Returns zero on
660 : : * success, non-zero on failure.
661 : : *
662 : : */
663 : 0 : int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
664 : : {
665 : 0 : return netlbl_domhsh_remove(NULL, audit_info);
666 : : }
667 : :
668 : : /**
669 : : * netlbl_domhsh_getentry - Get an entry from the domain hash table
670 : : * @domain: the domain name to search for
671 : : *
672 : : * Description:
673 : : * Look through the domain hash table searching for an entry to match @domain,
674 : : * return a pointer to a copy of the entry or NULL. The caller is responsible
675 : : * for ensuring that rcu_read_[un]lock() is called.
676 : : *
677 : : */
678 : 0 : struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
679 : : {
680 : 0 : return netlbl_domhsh_search_def(domain);
681 : : }
682 : :
683 : : /**
684 : : * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
685 : : * @domain: the domain name to search for
686 : : * @addr: the IP address to search for
687 : : *
688 : : * Description:
689 : : * Look through the domain hash table searching for an entry to match @domain
690 : : * and @addr, return a pointer to a copy of the entry or NULL. The caller is
691 : : * responsible for ensuring that rcu_read_[un]lock() is called.
692 : : *
693 : : */
694 : 0 : struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
695 : : __be32 addr)
696 : : {
697 : : struct netlbl_dom_map *dom_iter;
698 : : struct netlbl_af4list *addr_iter;
699 : :
700 : 0 : dom_iter = netlbl_domhsh_search_def(domain);
701 [ # # ]: 0 : if (dom_iter == NULL)
702 : : return NULL;
703 : :
704 [ # # ]: 0 : if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
705 : 0 : return &dom_iter->def;
706 : 0 : addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
707 [ # # ]: 0 : if (addr_iter == NULL)
708 : : return NULL;
709 : 0 : return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
710 : : }
711 : :
712 : : #if IS_ENABLED(CONFIG_IPV6)
713 : : /**
714 : : * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
715 : : * @domain: the domain name to search for
716 : : * @addr: the IP address to search for
717 : : *
718 : : * Description:
719 : : * Look through the domain hash table searching for an entry to match @domain
720 : : * and @addr, return a pointer to a copy of the entry or NULL. The caller is
721 : : * responsible for ensuring that rcu_read_[un]lock() is called.
722 : : *
723 : : */
724 : 0 : struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
725 : : const struct in6_addr *addr)
726 : : {
727 : : struct netlbl_dom_map *dom_iter;
728 : : struct netlbl_af6list *addr_iter;
729 : :
730 : 0 : dom_iter = netlbl_domhsh_search_def(domain);
731 [ # # ]: 0 : if (dom_iter == NULL)
732 : : return NULL;
733 : :
734 [ # # ]: 0 : if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
735 : 0 : return &dom_iter->def;
736 : 0 : addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
737 [ # # ]: 0 : if (addr_iter == NULL)
738 : : return NULL;
739 : 0 : return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
740 : : }
741 : : #endif /* IPv6 */
742 : :
743 : : /**
744 : : * netlbl_domhsh_walk - Iterate through the domain mapping hash table
745 : : * @skip_bkt: the number of buckets to skip at the start
746 : : * @skip_chain: the number of entries to skip in the first iterated bucket
747 : : * @callback: callback for each entry
748 : : * @cb_arg: argument for the callback function
749 : : *
750 : : * Description:
751 : : * Interate over the domain mapping hash table, skipping the first @skip_bkt
752 : : * buckets and @skip_chain entries. For each entry in the table call
753 : : * @callback, if @callback returns a negative value stop 'walking' through the
754 : : * table and return. Updates the values in @skip_bkt and @skip_chain on
755 : : * return. Returns zero on success, negative values on failure.
756 : : *
757 : : */
758 : 0 : int netlbl_domhsh_walk(u32 *skip_bkt,
759 : : u32 *skip_chain,
760 : : int (*callback) (struct netlbl_dom_map *entry, void *arg),
761 : : void *cb_arg)
762 : : {
763 : : int ret_val = -ENOENT;
764 : : u32 iter_bkt;
765 : : struct list_head *iter_list;
766 : : struct netlbl_dom_map *iter_entry;
767 : : u32 chain_cnt = 0;
768 : :
769 : : rcu_read_lock();
770 [ # # ]: 0 : for (iter_bkt = *skip_bkt;
771 : 0 : iter_bkt < rcu_dereference(netlbl_domhsh)->size;
772 : 0 : iter_bkt++, chain_cnt = 0) {
773 : 0 : iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
774 [ # # ]: 0 : list_for_each_entry_rcu(iter_entry, iter_list, list)
775 [ # # ]: 0 : if (iter_entry->valid) {
776 [ # # ]: 0 : if (chain_cnt++ < *skip_chain)
777 : 0 : continue;
778 : 0 : ret_val = callback(iter_entry, cb_arg);
779 [ # # ]: 0 : if (ret_val < 0) {
780 : : chain_cnt--;
781 : : goto walk_return;
782 : : }
783 : : }
784 : : }
785 : :
786 : : walk_return:
787 : : rcu_read_unlock();
788 : 0 : *skip_bkt = iter_bkt;
789 : 0 : *skip_chain = chain_cnt;
790 : 0 : return ret_val;
791 : : }
|