linux/net/netlabel/netlabel_cipso_v4.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * NetLabel CIPSO/IPv4 Support
   4 *
   5 * This file defines the CIPSO/IPv4 functions for the NetLabel system.  The
   6 * NetLabel system manages static and dynamic label mappings for network
   7 * protocols such as CIPSO and RIPSO.
   8 *
   9 * Author: Paul Moore <paul@paul-moore.com>
  10 */
  11
  12/*
  13 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
  14 */
  15
  16#include <linux/types.h>
  17#include <linux/socket.h>
  18#include <linux/string.h>
  19#include <linux/skbuff.h>
  20#include <linux/audit.h>
  21#include <linux/slab.h>
  22#include <net/sock.h>
  23#include <net/netlink.h>
  24#include <net/genetlink.h>
  25#include <net/netlabel.h>
  26#include <net/cipso_ipv4.h>
  27#include <linux/atomic.h>
  28
  29#include "netlabel_user.h"
  30#include "netlabel_cipso_v4.h"
  31#include "netlabel_mgmt.h"
  32#include "netlabel_domainhash.h"
  33
  34/* Argument struct for cipso_v4_doi_walk() */
  35struct netlbl_cipsov4_doiwalk_arg {
  36        struct netlink_callback *nl_cb;
  37        struct sk_buff *skb;
  38        u32 seq;
  39};
  40
  41/* Argument struct for netlbl_domhsh_walk() */
  42struct netlbl_domhsh_walk_arg {
  43        struct netlbl_audit *audit_info;
  44        u32 doi;
  45};
  46
  47/* NetLabel Generic NETLINK CIPSOv4 family */
  48static struct genl_family netlbl_cipsov4_gnl_family;
  49/* NetLabel Netlink attribute policy */
  50static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = {
  51        [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 },
  52        [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 },
  53        [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 },
  54        [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED },
  55        [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 },
  56        [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 },
  57        [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED },
  58        [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED },
  59        [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 },
  60        [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 },
  61        [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED },
  62        [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED },
  63};
  64
  65/*
  66 * Helper Functions
  67 */
  68
  69/**
  70 * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
  71 * @info: the Generic NETLINK info block
  72 * @doi_def: the CIPSO V4 DOI definition
  73 *
  74 * Description:
  75 * Parse the common sections of a ADD message and fill in the related values
  76 * in @doi_def.  Returns zero on success, negative values on failure.
  77 *
  78 */
  79static int netlbl_cipsov4_add_common(struct genl_info *info,
  80                                     struct cipso_v4_doi *doi_def)
  81{
  82        struct nlattr *nla;
  83        int nla_rem;
  84        u32 iter = 0;
  85
  86        doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
  87
  88        if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_TAGLST],
  89                                           NLBL_CIPSOV4_A_MAX,
  90                                           netlbl_cipsov4_genl_policy,
  91                                           NULL) != 0)
  92                return -EINVAL;
  93
  94        nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
  95                if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
  96                        if (iter >= CIPSO_V4_TAG_MAXCNT)
  97                                return -EINVAL;
  98                        doi_def->tags[iter++] = nla_get_u8(nla);
  99                }
 100        while (iter < CIPSO_V4_TAG_MAXCNT)
 101                doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
 102
 103        return 0;
 104}
 105
 106/*
 107 * NetLabel Command Handlers
 108 */
 109
 110/**
 111 * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
 112 * @info: the Generic NETLINK info block
 113 * @audit_info: NetLabel audit information
 114 *
 115 * Description:
 116 * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
 117 * message and add it to the CIPSO V4 engine.  Return zero on success and
 118 * non-zero on error.
 119 *
 120 */
 121static int netlbl_cipsov4_add_std(struct genl_info *info,
 122                                  struct netlbl_audit *audit_info)
 123{
 124        int ret_val = -EINVAL;
 125        struct cipso_v4_doi *doi_def = NULL;
 126        struct nlattr *nla_a;
 127        struct nlattr *nla_b;
 128        int nla_a_rem;
 129        int nla_b_rem;
 130        u32 iter;
 131
 132        if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
 133            !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
 134                return -EINVAL;
 135
 136        if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 137                                           NLBL_CIPSOV4_A_MAX,
 138                                           netlbl_cipsov4_genl_policy,
 139                                           NULL) != 0)
 140                return -EINVAL;
 141
 142        doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
 143        if (doi_def == NULL)
 144                return -ENOMEM;
 145        doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
 146        if (doi_def->map.std == NULL) {
 147                kfree(doi_def);
 148                return -ENOMEM;
 149        }
 150        doi_def->type = CIPSO_V4_MAP_TRANS;
 151
 152        ret_val = netlbl_cipsov4_add_common(info, doi_def);
 153        if (ret_val != 0)
 154                goto add_std_failure;
 155        ret_val = -EINVAL;
 156
 157        nla_for_each_nested(nla_a,
 158                            info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 159                            nla_a_rem)
 160                if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
 161                        if (nla_validate_nested_deprecated(nla_a,
 162                                                           NLBL_CIPSOV4_A_MAX,
 163                                                           netlbl_cipsov4_genl_policy,
 164                                                           NULL) != 0)
 165                                goto add_std_failure;
 166                        nla_for_each_nested(nla_b, nla_a, nla_b_rem)
 167                                switch (nla_type(nla_b)) {
 168                                case NLBL_CIPSOV4_A_MLSLVLLOC:
 169                                        if (nla_get_u32(nla_b) >
 170                                            CIPSO_V4_MAX_LOC_LVLS)
 171                                                goto add_std_failure;
 172                                        if (nla_get_u32(nla_b) >=
 173                                            doi_def->map.std->lvl.local_size)
 174                                             doi_def->map.std->lvl.local_size =
 175                                                     nla_get_u32(nla_b) + 1;
 176                                        break;
 177                                case NLBL_CIPSOV4_A_MLSLVLREM:
 178                                        if (nla_get_u32(nla_b) >
 179                                            CIPSO_V4_MAX_REM_LVLS)
 180                                                goto add_std_failure;
 181                                        if (nla_get_u32(nla_b) >=
 182                                            doi_def->map.std->lvl.cipso_size)
 183                                             doi_def->map.std->lvl.cipso_size =
 184                                                     nla_get_u32(nla_b) + 1;
 185                                        break;
 186                                }
 187                }
 188        doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
 189                                              sizeof(u32),
 190                                              GFP_KERNEL | __GFP_NOWARN);
 191        if (doi_def->map.std->lvl.local == NULL) {
 192                ret_val = -ENOMEM;
 193                goto add_std_failure;
 194        }
 195        doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
 196                                              sizeof(u32),
 197                                              GFP_KERNEL | __GFP_NOWARN);
 198        if (doi_def->map.std->lvl.cipso == NULL) {
 199                ret_val = -ENOMEM;
 200                goto add_std_failure;
 201        }
 202        for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
 203                doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
 204        for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
 205                doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
 206        nla_for_each_nested(nla_a,
 207                            info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 208                            nla_a_rem)
 209                if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
 210                        struct nlattr *lvl_loc;
 211                        struct nlattr *lvl_rem;
 212
 213                        lvl_loc = nla_find_nested(nla_a,
 214                                                  NLBL_CIPSOV4_A_MLSLVLLOC);
 215                        lvl_rem = nla_find_nested(nla_a,
 216                                                  NLBL_CIPSOV4_A_MLSLVLREM);
 217                        if (lvl_loc == NULL || lvl_rem == NULL)
 218                                goto add_std_failure;
 219                        doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
 220                                nla_get_u32(lvl_rem);
 221                        doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
 222                                nla_get_u32(lvl_loc);
 223                }
 224
 225        if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
 226                if (nla_validate_nested_deprecated(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 227                                                   NLBL_CIPSOV4_A_MAX,
 228                                                   netlbl_cipsov4_genl_policy,
 229                                                   NULL) != 0)
 230                        goto add_std_failure;
 231
 232                nla_for_each_nested(nla_a,
 233                                    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 234                                    nla_a_rem)
 235                        if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
 236                                if (nla_validate_nested_deprecated(nla_a,
 237                                                                   NLBL_CIPSOV4_A_MAX,
 238                                                                   netlbl_cipsov4_genl_policy,
 239                                                                   NULL) != 0)
 240                                        goto add_std_failure;
 241                                nla_for_each_nested(nla_b, nla_a, nla_b_rem)
 242                                        switch (nla_type(nla_b)) {
 243                                        case NLBL_CIPSOV4_A_MLSCATLOC:
 244                                                if (nla_get_u32(nla_b) >
 245                                                    CIPSO_V4_MAX_LOC_CATS)
 246                                                        goto add_std_failure;
 247                                                if (nla_get_u32(nla_b) >=
 248                                              doi_def->map.std->cat.local_size)
 249                                             doi_def->map.std->cat.local_size =
 250                                                     nla_get_u32(nla_b) + 1;
 251                                                break;
 252                                        case NLBL_CIPSOV4_A_MLSCATREM:
 253                                                if (nla_get_u32(nla_b) >
 254                                                    CIPSO_V4_MAX_REM_CATS)
 255                                                        goto add_std_failure;
 256                                                if (nla_get_u32(nla_b) >=
 257                                              doi_def->map.std->cat.cipso_size)
 258                                             doi_def->map.std->cat.cipso_size =
 259                                                     nla_get_u32(nla_b) + 1;
 260                                                break;
 261                                        }
 262                        }
 263                doi_def->map.std->cat.local = kcalloc(
 264                                              doi_def->map.std->cat.local_size,
 265                                              sizeof(u32),
 266                                              GFP_KERNEL | __GFP_NOWARN);
 267                if (doi_def->map.std->cat.local == NULL) {
 268                        ret_val = -ENOMEM;
 269                        goto add_std_failure;
 270                }
 271                doi_def->map.std->cat.cipso = kcalloc(
 272                                              doi_def->map.std->cat.cipso_size,
 273                                              sizeof(u32),
 274                                              GFP_KERNEL | __GFP_NOWARN);
 275                if (doi_def->map.std->cat.cipso == NULL) {
 276                        ret_val = -ENOMEM;
 277                        goto add_std_failure;
 278                }
 279                for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
 280                        doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
 281                for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
 282                        doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
 283                nla_for_each_nested(nla_a,
 284                                    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 285                                    nla_a_rem)
 286                        if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
 287                                struct nlattr *cat_loc;
 288                                struct nlattr *cat_rem;
 289
 290                                cat_loc = nla_find_nested(nla_a,
 291                                                     NLBL_CIPSOV4_A_MLSCATLOC);
 292                                cat_rem = nla_find_nested(nla_a,
 293                                                     NLBL_CIPSOV4_A_MLSCATREM);
 294                                if (cat_loc == NULL || cat_rem == NULL)
 295                                        goto add_std_failure;
 296                                doi_def->map.std->cat.local[
 297                                                        nla_get_u32(cat_loc)] =
 298                                        nla_get_u32(cat_rem);
 299                                doi_def->map.std->cat.cipso[
 300                                                        nla_get_u32(cat_rem)] =
 301                                        nla_get_u32(cat_loc);
 302                        }
 303        }
 304
 305        ret_val = cipso_v4_doi_add(doi_def, audit_info);
 306        if (ret_val != 0)
 307                goto add_std_failure;
 308        return 0;
 309
 310add_std_failure:
 311        cipso_v4_doi_free(doi_def);
 312        return ret_val;
 313}
 314
 315/**
 316 * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
 317 * @info: the Generic NETLINK info block
 318 * @audit_info: NetLabel audit information
 319 *
 320 * Description:
 321 * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
 322 * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
 323 * error.
 324 *
 325 */
 326static int netlbl_cipsov4_add_pass(struct genl_info *info,
 327                                   struct netlbl_audit *audit_info)
 328{
 329        int ret_val;
 330        struct cipso_v4_doi *doi_def = NULL;
 331
 332        if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
 333                return -EINVAL;
 334
 335        doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
 336        if (doi_def == NULL)
 337                return -ENOMEM;
 338        doi_def->type = CIPSO_V4_MAP_PASS;
 339
 340        ret_val = netlbl_cipsov4_add_common(info, doi_def);
 341        if (ret_val != 0)
 342                goto add_pass_failure;
 343
 344        ret_val = cipso_v4_doi_add(doi_def, audit_info);
 345        if (ret_val != 0)
 346                goto add_pass_failure;
 347        return 0;
 348
 349add_pass_failure:
 350        cipso_v4_doi_free(doi_def);
 351        return ret_val;
 352}
 353
 354/**
 355 * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
 356 * @info: the Generic NETLINK info block
 357 * @audit_info: NetLabel audit information
 358 *
 359 * Description:
 360 * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
 361 * message and add it to the CIPSO V4 engine.  Return zero on success and
 362 * non-zero on error.
 363 *
 364 */
 365static int netlbl_cipsov4_add_local(struct genl_info *info,
 366                                    struct netlbl_audit *audit_info)
 367{
 368        int ret_val;
 369        struct cipso_v4_doi *doi_def = NULL;
 370
 371        if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
 372                return -EINVAL;
 373
 374        doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
 375        if (doi_def == NULL)
 376                return -ENOMEM;
 377        doi_def->type = CIPSO_V4_MAP_LOCAL;
 378
 379        ret_val = netlbl_cipsov4_add_common(info, doi_def);
 380        if (ret_val != 0)
 381                goto add_local_failure;
 382
 383        ret_val = cipso_v4_doi_add(doi_def, audit_info);
 384        if (ret_val != 0)
 385                goto add_local_failure;
 386        return 0;
 387
 388add_local_failure:
 389        cipso_v4_doi_free(doi_def);
 390        return ret_val;
 391}
 392
 393/**
 394 * netlbl_cipsov4_add - Handle an ADD message
 395 * @skb: the NETLINK buffer
 396 * @info: the Generic NETLINK info block
 397 *
 398 * Description:
 399 * Create a new DOI definition based on the given ADD message and add it to the
 400 * CIPSO V4 engine.  Returns zero on success, negative values on failure.
 401 *
 402 */
 403static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
 404
 405{
 406        int ret_val = -EINVAL;
 407        struct netlbl_audit audit_info;
 408
 409        if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
 410            !info->attrs[NLBL_CIPSOV4_A_MTYPE])
 411                return -EINVAL;
 412
 413        netlbl_netlink_auditinfo(&audit_info);
 414        switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
 415        case CIPSO_V4_MAP_TRANS:
 416                ret_val = netlbl_cipsov4_add_std(info, &audit_info);
 417                break;
 418        case CIPSO_V4_MAP_PASS:
 419                ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
 420                break;
 421        case CIPSO_V4_MAP_LOCAL:
 422                ret_val = netlbl_cipsov4_add_local(info, &audit_info);
 423                break;
 424        }
 425        if (ret_val == 0)
 426                atomic_inc(&netlabel_mgmt_protocount);
 427
 428        return ret_val;
 429}
 430
 431/**
 432 * netlbl_cipsov4_list - Handle a LIST message
 433 * @skb: the NETLINK buffer
 434 * @info: the Generic NETLINK info block
 435 *
 436 * Description:
 437 * Process a user generated LIST message and respond accordingly.  While the
 438 * response message generated by the kernel is straightforward, determining
 439 * before hand the size of the buffer to allocate is not (we have to generate
 440 * the message to know the size).  In order to keep this function sane what we
 441 * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in
 442 * that size, if we fail then we restart with a larger buffer and try again.
 443 * We continue in this manner until we hit a limit of failed attempts then we
 444 * give up and just send an error message.  Returns zero on success and
 445 * negative values on error.
 446 *
 447 */
 448static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info)
 449{
 450        int ret_val;
 451        struct sk_buff *ans_skb = NULL;
 452        u32 nlsze_mult = 1;
 453        void *data;
 454        u32 doi;
 455        struct nlattr *nla_a;
 456        struct nlattr *nla_b;
 457        struct cipso_v4_doi *doi_def;
 458        u32 iter;
 459
 460        if (!info->attrs[NLBL_CIPSOV4_A_DOI]) {
 461                ret_val = -EINVAL;
 462                goto list_failure;
 463        }
 464
 465list_start:
 466        ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL);
 467        if (ans_skb == NULL) {
 468                ret_val = -ENOMEM;
 469                goto list_failure;
 470        }
 471        data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family,
 472                                 0, NLBL_CIPSOV4_C_LIST);
 473        if (data == NULL) {
 474                ret_val = -ENOMEM;
 475                goto list_failure;
 476        }
 477
 478        doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
 479
 480        rcu_read_lock();
 481        doi_def = cipso_v4_doi_getdef(doi);
 482        if (doi_def == NULL) {
 483                ret_val = -EINVAL;
 484                goto list_failure_lock;
 485        }
 486
 487        ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
 488        if (ret_val != 0)
 489                goto list_failure_lock;
 490
 491        nla_a = nla_nest_start_noflag(ans_skb, NLBL_CIPSOV4_A_TAGLST);
 492        if (nla_a == NULL) {
 493                ret_val = -ENOMEM;
 494                goto list_failure_lock;
 495        }
 496        for (iter = 0;
 497             iter < CIPSO_V4_TAG_MAXCNT &&
 498               doi_def->tags[iter] != CIPSO_V4_TAG_INVALID;
 499             iter++) {
 500                ret_val = nla_put_u8(ans_skb,
 501                                     NLBL_CIPSOV4_A_TAG,
 502                                     doi_def->tags[iter]);
 503                if (ret_val != 0)
 504                        goto list_failure_lock;
 505        }
 506        nla_nest_end(ans_skb, nla_a);
 507
 508        switch (doi_def->type) {
 509        case CIPSO_V4_MAP_TRANS:
 510                nla_a = nla_nest_start_noflag(ans_skb,
 511                                              NLBL_CIPSOV4_A_MLSLVLLST);
 512                if (nla_a == NULL) {
 513                        ret_val = -ENOMEM;
 514                        goto list_failure_lock;
 515                }
 516                for (iter = 0;
 517                     iter < doi_def->map.std->lvl.local_size;
 518                     iter++) {
 519                        if (doi_def->map.std->lvl.local[iter] ==
 520                            CIPSO_V4_INV_LVL)
 521                                continue;
 522
 523                        nla_b = nla_nest_start_noflag(ans_skb,
 524                                                      NLBL_CIPSOV4_A_MLSLVL);
 525                        if (nla_b == NULL) {
 526                                ret_val = -ENOMEM;
 527                                goto list_retry;
 528                        }
 529                        ret_val = nla_put_u32(ans_skb,
 530                                              NLBL_CIPSOV4_A_MLSLVLLOC,
 531                                              iter);
 532                        if (ret_val != 0)
 533                                goto list_retry;
 534                        ret_val = nla_put_u32(ans_skb,
 535                                            NLBL_CIPSOV4_A_MLSLVLREM,
 536                                            doi_def->map.std->lvl.local[iter]);
 537                        if (ret_val != 0)
 538                                goto list_retry;
 539                        nla_nest_end(ans_skb, nla_b);
 540                }
 541                nla_nest_end(ans_skb, nla_a);
 542
 543                nla_a = nla_nest_start_noflag(ans_skb,
 544                                              NLBL_CIPSOV4_A_MLSCATLST);
 545                if (nla_a == NULL) {
 546                        ret_val = -ENOMEM;
 547                        goto list_retry;
 548                }
 549                for (iter = 0;
 550                     iter < doi_def->map.std->cat.local_size;
 551                     iter++) {
 552                        if (doi_def->map.std->cat.local[iter] ==
 553                            CIPSO_V4_INV_CAT)
 554                                continue;
 555
 556                        nla_b = nla_nest_start_noflag(ans_skb,
 557                                                      NLBL_CIPSOV4_A_MLSCAT);
 558                        if (nla_b == NULL) {
 559                                ret_val = -ENOMEM;
 560                                goto list_retry;
 561                        }
 562                        ret_val = nla_put_u32(ans_skb,
 563                                              NLBL_CIPSOV4_A_MLSCATLOC,
 564                                              iter);
 565                        if (ret_val != 0)
 566                                goto list_retry;
 567                        ret_val = nla_put_u32(ans_skb,
 568                                            NLBL_CIPSOV4_A_MLSCATREM,
 569                                            doi_def->map.std->cat.local[iter]);
 570                        if (ret_val != 0)
 571                                goto list_retry;
 572                        nla_nest_end(ans_skb, nla_b);
 573                }
 574                nla_nest_end(ans_skb, nla_a);
 575
 576                break;
 577        }
 578        cipso_v4_doi_putdef(doi_def);
 579        rcu_read_unlock();
 580
 581        genlmsg_end(ans_skb, data);
 582        return genlmsg_reply(ans_skb, info);
 583
 584list_retry:
 585        /* XXX - this limit is a guesstimate */
 586        if (nlsze_mult < 4) {
 587                cipso_v4_doi_putdef(doi_def);
 588                rcu_read_unlock();
 589                kfree_skb(ans_skb);
 590                nlsze_mult *= 2;
 591                goto list_start;
 592        }
 593list_failure_lock:
 594        cipso_v4_doi_putdef(doi_def);
 595        rcu_read_unlock();
 596list_failure:
 597        kfree_skb(ans_skb);
 598        return ret_val;
 599}
 600
 601/**
 602 * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL
 603 * @doi_def: the CIPSOv4 DOI definition
 604 * @arg: the netlbl_cipsov4_doiwalk_arg structure
 605 *
 606 * Description:
 607 * This function is designed to be used as a callback to the
 608 * cipso_v4_doi_walk() function for use in generating a response for a LISTALL
 609 * message.  Returns the size of the message on success, negative values on
 610 * failure.
 611 *
 612 */
 613static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
 614{
 615        int ret_val = -ENOMEM;
 616        struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
 617        void *data;
 618
 619        data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
 620                           cb_arg->seq, &netlbl_cipsov4_gnl_family,
 621                           NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
 622        if (data == NULL)
 623                goto listall_cb_failure;
 624
 625        ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi);
 626        if (ret_val != 0)
 627                goto listall_cb_failure;
 628        ret_val = nla_put_u32(cb_arg->skb,
 629                              NLBL_CIPSOV4_A_MTYPE,
 630                              doi_def->type);
 631        if (ret_val != 0)
 632                goto listall_cb_failure;
 633
 634        genlmsg_end(cb_arg->skb, data);
 635        return 0;
 636
 637listall_cb_failure:
 638        genlmsg_cancel(cb_arg->skb, data);
 639        return ret_val;
 640}
 641
 642/**
 643 * netlbl_cipsov4_listall - Handle a LISTALL message
 644 * @skb: the NETLINK buffer
 645 * @cb: the NETLINK callback
 646 *
 647 * Description:
 648 * Process a user generated LISTALL message and respond accordingly.  Returns
 649 * zero on success and negative values on error.
 650 *
 651 */
 652static int netlbl_cipsov4_listall(struct sk_buff *skb,
 653                                  struct netlink_callback *cb)
 654{
 655        struct netlbl_cipsov4_doiwalk_arg cb_arg;
 656        u32 doi_skip = cb->args[0];
 657
 658        cb_arg.nl_cb = cb;
 659        cb_arg.skb = skb;
 660        cb_arg.seq = cb->nlh->nlmsg_seq;
 661
 662        cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
 663
 664        cb->args[0] = doi_skip;
 665        return skb->len;
 666}
 667
 668/**
 669 * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
 670 * @entry: LSM domain mapping entry
 671 * @arg: the netlbl_domhsh_walk_arg structure
 672 *
 673 * Description:
 674 * This function is intended for use by netlbl_cipsov4_remove() as the callback
 675 * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
 676 * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
 677 * success, negative values on failure.
 678 *
 679 */
 680static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
 681{
 682        struct netlbl_domhsh_walk_arg *cb_arg = arg;
 683
 684        if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 &&
 685            entry->def.cipso->doi == cb_arg->doi)
 686                return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
 687
 688        return 0;
 689}
 690
 691/**
 692 * netlbl_cipsov4_remove - Handle a REMOVE message
 693 * @skb: the NETLINK buffer
 694 * @info: the Generic NETLINK info block
 695 *
 696 * Description:
 697 * Process a user generated REMOVE message and respond accordingly.  Returns
 698 * zero on success, negative values on failure.
 699 *
 700 */
 701static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
 702{
 703        int ret_val = -EINVAL;
 704        struct netlbl_domhsh_walk_arg cb_arg;
 705        struct netlbl_audit audit_info;
 706        u32 skip_bkt = 0;
 707        u32 skip_chain = 0;
 708
 709        if (!info->attrs[NLBL_CIPSOV4_A_DOI])
 710                return -EINVAL;
 711
 712        netlbl_netlink_auditinfo(&audit_info);
 713        cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
 714        cb_arg.audit_info = &audit_info;
 715        ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
 716                                     netlbl_cipsov4_remove_cb, &cb_arg);
 717        if (ret_val == 0 || ret_val == -ENOENT) {
 718                ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info);
 719                if (ret_val == 0)
 720                        atomic_dec(&netlabel_mgmt_protocount);
 721        }
 722
 723        return ret_val;
 724}
 725
 726/*
 727 * NetLabel Generic NETLINK Command Definitions
 728 */
 729
 730static const struct genl_small_ops netlbl_cipsov4_ops[] = {
 731        {
 732        .cmd = NLBL_CIPSOV4_C_ADD,
 733        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 734        .flags = GENL_ADMIN_PERM,
 735        .doit = netlbl_cipsov4_add,
 736        .dumpit = NULL,
 737        },
 738        {
 739        .cmd = NLBL_CIPSOV4_C_REMOVE,
 740        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 741        .flags = GENL_ADMIN_PERM,
 742        .doit = netlbl_cipsov4_remove,
 743        .dumpit = NULL,
 744        },
 745        {
 746        .cmd = NLBL_CIPSOV4_C_LIST,
 747        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 748        .flags = 0,
 749        .doit = netlbl_cipsov4_list,
 750        .dumpit = NULL,
 751        },
 752        {
 753        .cmd = NLBL_CIPSOV4_C_LISTALL,
 754        .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 755        .flags = 0,
 756        .doit = NULL,
 757        .dumpit = netlbl_cipsov4_listall,
 758        },
 759};
 760
 761static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = {
 762        .hdrsize = 0,
 763        .name = NETLBL_NLTYPE_CIPSOV4_NAME,
 764        .version = NETLBL_PROTO_VERSION,
 765        .maxattr = NLBL_CIPSOV4_A_MAX,
 766        .policy = netlbl_cipsov4_genl_policy,
 767        .module = THIS_MODULE,
 768        .small_ops = netlbl_cipsov4_ops,
 769        .n_small_ops = ARRAY_SIZE(netlbl_cipsov4_ops),
 770};
 771
 772/*
 773 * NetLabel Generic NETLINK Protocol Functions
 774 */
 775
 776/**
 777 * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
 778 *
 779 * Description:
 780 * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
 781 * mechanism.  Returns zero on success, negative values on failure.
 782 *
 783 */
 784int __init netlbl_cipsov4_genl_init(void)
 785{
 786        return genl_register_family(&netlbl_cipsov4_gnl_family);
 787}
 788