~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netlabel/netlabel_domainhash.c

Version: ~ [ linux-5.6 ] ~ [ linux-5.5.13 ] ~ [ linux-5.4.28 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.113 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.174 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.217 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.217 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.82 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 #if IS_ENABLED(CONFIG_IPV6)
729 /**
730  * netlbl_domhsh_remove_af6 - Removes an address selector entry
731  * @domain: the domain
732  * @addr: IPv6 address
733  * @mask: IPv6 address mask
734  * @audit_info: NetLabel audit information
735  *
736  * Description:
737  * Removes an individual address selector from a domain mapping and potentially
738  * the entire mapping if it is empty.  Returns zero on success, negative values
739  * on failure.
740  *
741  */
742 int netlbl_domhsh_remove_af6(const char *domain,
743                              const struct in6_addr *addr,
744                              const struct in6_addr *mask,
745                              struct netlbl_audit *audit_info)
746 {
747         struct netlbl_dom_map *entry_map;
748         struct netlbl_af6list *entry_addr;
749         struct netlbl_af4list *iter4;
750         struct netlbl_af6list *iter6;
751         struct netlbl_domaddr6_map *entry;
752 
753         rcu_read_lock();
754 
755         if (domain)
756                 entry_map = netlbl_domhsh_search(domain, AF_INET6);
757         else
758                 entry_map = netlbl_domhsh_search_def(domain, AF_INET6);
759         if (entry_map == NULL ||
760             entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
761                 goto remove_af6_failure;
762 
763         spin_lock(&netlbl_domhsh_lock);
764         entry_addr = netlbl_af6list_remove(addr, mask,
765                                            &entry_map->def.addrsel->list6);
766         spin_unlock(&netlbl_domhsh_lock);
767 
768         if (entry_addr == NULL)
769                 goto remove_af6_failure;
770         netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
771                 goto remove_af6_single_addr;
772         netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
773                 goto remove_af6_single_addr;
774         /* the domain mapping is empty so remove it from the mapping table */
775         netlbl_domhsh_remove_entry(entry_map, audit_info);
776 
777 remove_af6_single_addr:
778         rcu_read_unlock();
779         /* yick, we can't use call_rcu here because we don't have a rcu head
780          * pointer but hopefully this should be a rare case so the pause
781          * shouldn't be a problem */
782         synchronize_rcu();
783         entry = netlbl_domhsh_addr6_entry(entry_addr);
784         calipso_doi_putdef(entry->def.calipso);
785         kfree(entry);
786         return 0;
787 
788 remove_af6_failure:
789         rcu_read_unlock();
790         return -ENOENT;
791 }
792 #endif /* IPv6 */
793 
794 /**
795  * netlbl_domhsh_remove - Removes an entry from the domain hash table
796  * @domain: the domain to remove
797  * @family: address family
798  * @audit_info: NetLabel audit information
799  *
800  * Description:
801  * Removes an entry from the domain hash table and handles any updates to the
802  * lower level protocol handler (i.e. CIPSO).  @family may be %AF_UNSPEC which
803  * removes all address family entries.  Returns zero on success, negative on
804  * failure.
805  *
806  */
807 int netlbl_domhsh_remove(const char *domain, u16 family,
808                          struct netlbl_audit *audit_info)
809 {
810         int ret_val = -EINVAL;
811         struct netlbl_dom_map *entry;
812 
813         rcu_read_lock();
814 
815         if (family == AF_INET || family == AF_UNSPEC) {
816                 if (domain)
817                         entry = netlbl_domhsh_search(domain, AF_INET);
818                 else
819                         entry = netlbl_domhsh_search_def(domain, AF_INET);
820                 ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
821                 if (ret_val && ret_val != -ENOENT)
822                         goto done;
823         }
824         if (family == AF_INET6 || family == AF_UNSPEC) {
825                 int ret_val2;
826 
827                 if (domain)
828                         entry = netlbl_domhsh_search(domain, AF_INET6);
829                 else
830                         entry = netlbl_domhsh_search_def(domain, AF_INET6);
831                 ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info);
832                 if (ret_val2 != -ENOENT)
833                         ret_val = ret_val2;
834         }
835 done:
836         rcu_read_unlock();
837 
838         return ret_val;
839 }
840 
841 /**
842  * netlbl_domhsh_remove_default - Removes the default entry from the table
843  * @family: address family
844  * @audit_info: NetLabel audit information
845  *
846  * Description:
847  * Removes/resets the default entry corresponding to @family from the domain
848  * hash table and handles any updates to the lower level protocol handler
849  * (i.e. CIPSO).  @family may be %AF_UNSPEC which removes all address family
850  * entries.  Returns zero on success, negative on failure.
851  *
852  */
853 int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info)
854 {
855         return netlbl_domhsh_remove(NULL, family, audit_info);
856 }
857 
858 /**
859  * netlbl_domhsh_getentry - Get an entry from the domain hash table
860  * @domain: the domain name to search for
861  * @family: address family
862  *
863  * Description:
864  * Look through the domain hash table searching for an entry to match @domain,
865  * with address family @family, return a pointer to a copy of the entry or
866  * NULL.  The caller is responsible for ensuring that rcu_read_[un]lock() is
867  * called.
868  *
869  */
870 struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family)
871 {
872         if (family == AF_UNSPEC)
873                 return NULL;
874         return netlbl_domhsh_search_def(domain, family);
875 }
876 
877 /**
878  * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
879  * @domain: the domain name to search for
880  * @addr: the IP address to search for
881  *
882  * Description:
883  * Look through the domain hash table searching for an entry to match @domain
884  * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
885  * responsible for ensuring that rcu_read_[un]lock() is called.
886  *
887  */
888 struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
889                                                      __be32 addr)
890 {
891         struct netlbl_dom_map *dom_iter;
892         struct netlbl_af4list *addr_iter;
893 
894         dom_iter = netlbl_domhsh_search_def(domain, AF_INET);
895         if (dom_iter == NULL)
896                 return NULL;
897 
898         if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
899                 return &dom_iter->def;
900         addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
901         if (addr_iter == NULL)
902                 return NULL;
903         return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
904 }
905 
906 #if IS_ENABLED(CONFIG_IPV6)
907 /**
908  * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
909  * @domain: the domain name to search for
910  * @addr: the IP address to search for
911  *
912  * Description:
913  * Look through the domain hash table searching for an entry to match @domain
914  * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
915  * responsible for ensuring that rcu_read_[un]lock() is called.
916  *
917  */
918 struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
919                                                    const struct in6_addr *addr)
920 {
921         struct netlbl_dom_map *dom_iter;
922         struct netlbl_af6list *addr_iter;
923 
924         dom_iter = netlbl_domhsh_search_def(domain, AF_INET6);
925         if (dom_iter == NULL)
926                 return NULL;
927 
928         if (dom_iter->def.type != NETLBL_NLTYPE_ADDRSELECT)
929                 return &dom_iter->def;
930         addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
931         if (addr_iter == NULL)
932                 return NULL;
933         return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
934 }
935 #endif /* IPv6 */
936 
937 /**
938  * netlbl_domhsh_walk - Iterate through the domain mapping hash table
939  * @skip_bkt: the number of buckets to skip at the start
940  * @skip_chain: the number of entries to skip in the first iterated bucket
941  * @callback: callback for each entry
942  * @cb_arg: argument for the callback function
943  *
944  * Description:
945  * Interate over the domain mapping hash table, skipping the first @skip_bkt
946  * buckets and @skip_chain entries.  For each entry in the table call
947  * @callback, if @callback returns a negative value stop 'walking' through the
948  * table and return.  Updates the values in @skip_bkt and @skip_chain on
949  * return.  Returns zero on success, negative values on failure.
950  *
951  */
952 int netlbl_domhsh_walk(u32 *skip_bkt,
953                      u32 *skip_chain,
954                      int (*callback) (struct netlbl_dom_map *entry, void *arg),
955                      void *cb_arg)
956 {
957         int ret_val = -ENOENT;
958         u32 iter_bkt;
959         struct list_head *iter_list;
960         struct netlbl_dom_map *iter_entry;
961         u32 chain_cnt = 0;
962 
963         rcu_read_lock();
964         for (iter_bkt = *skip_bkt;
965              iter_bkt < rcu_dereference(netlbl_domhsh)->size;
966              iter_bkt++, chain_cnt = 0) {
967                 iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
968                 list_for_each_entry_rcu(iter_entry, iter_list, list)
969                         if (iter_entry->valid) {
970                                 if (chain_cnt++ < *skip_chain)
971                                         continue;
972                                 ret_val = callback(iter_entry, cb_arg);
973                                 if (ret_val < 0) {
974                                         chain_cnt--;
975                                         goto walk_return;
976                                 }
977                         }
978         }
979 
980 walk_return:
981         rcu_read_unlock();
982         *skip_bkt = iter_bkt;
983         *skip_chain = chain_cnt;
984         return ret_val;
985 }
986 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp