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