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