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