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