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