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