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, 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
  49struct 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 */
  57static DEFINE_SPINLOCK(netlbl_domhsh_lock);
  58#define netlbl_domhsh_rcu_deref(p) \
  59        rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
  60static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh;
  61static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4;
  62static 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 */
  78static 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 */
 118static 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
 132static 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 */
 150static 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 */
 184static 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 */
 220static 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 */
 286static 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 */
 374int __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 */
 417int 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
 556add_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 */
 573int 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 */
 591int 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 */
 673int 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
 712remove_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
 723remove_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 */
 742int 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
 777remove_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
 788remove_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 */
 807int 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        }
 835done:
 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 */
 853int 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 */
 870struct 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 */
 888struct 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 */
 918struct 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 */
 952int 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
 980walk_return:
 981        rcu_read_unlock();
 982        *skip_bkt = iter_bkt;
 983        *skip_chain = chain_cnt;
 984        return ret_val;
 985}
 986