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.moore@hp.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 <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 <asm/atomic.h>
  42
  43#include "netlabel_user.h"
  44#include "netlabel_cipso_v4.h"
  45#include "netlabel_mgmt.h"
  46
  47/* Argument struct for cipso_v4_doi_walk() */
  48struct netlbl_cipsov4_doiwalk_arg {
  49        struct netlink_callback *nl_cb;
  50        struct sk_buff *skb;
  51        u32 seq;
  52};
  53
  54/* NetLabel Generic NETLINK CIPSOv4 family */
  55static struct genl_family netlbl_cipsov4_gnl_family = {
  56        .id = GENL_ID_GENERATE,
  57        .hdrsize = 0,
  58        .name = NETLBL_NLTYPE_CIPSOV4_NAME,
  59        .version = NETLBL_PROTO_VERSION,
  60        .maxattr = NLBL_CIPSOV4_A_MAX,
  61};
  62
  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_doi_free - Frees a CIPSO V4 DOI definition
  85 * @entry: the entry's RCU field
  86 *
  87 * Description:
  88 * This function is designed to be used as a callback to the call_rcu()
  89 * function so that the memory allocated to the DOI definition can be released
  90 * safely.
  91 *
  92 */
  93void netlbl_cipsov4_doi_free(struct rcu_head *entry)
  94{
  95        struct cipso_v4_doi *ptr;
  96
  97        ptr = container_of(entry, struct cipso_v4_doi, rcu);
  98        switch (ptr->type) {
  99        case CIPSO_V4_MAP_STD:
 100                kfree(ptr->map.std->lvl.cipso);
 101                kfree(ptr->map.std->lvl.local);
 102                kfree(ptr->map.std->cat.cipso);
 103                kfree(ptr->map.std->cat.local);
 104                break;
 105        }
 106        kfree(ptr);
 107}
 108
 109/**
 110 * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
 111 * @info: the Generic NETLINK info block
 112 * @doi_def: the CIPSO V4 DOI definition
 113 *
 114 * Description:
 115 * Parse the common sections of a ADD message and fill in the related values
 116 * in @doi_def.  Returns zero on success, negative values on failure.
 117 *
 118 */
 119static int netlbl_cipsov4_add_common(struct genl_info *info,
 120                                     struct cipso_v4_doi *doi_def)
 121{
 122        struct nlattr *nla;
 123        int nla_rem;
 124        u32 iter = 0;
 125
 126        doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
 127
 128        if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST],
 129                                NLBL_CIPSOV4_A_MAX,
 130                                netlbl_cipsov4_genl_policy) != 0)
 131                return -EINVAL;
 132
 133        nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem)
 134                if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) {
 135                        if (iter >= CIPSO_V4_TAG_MAXCNT)
 136                                return -EINVAL;
 137                        doi_def->tags[iter++] = nla_get_u8(nla);
 138                }
 139        while (iter < CIPSO_V4_TAG_MAXCNT)
 140                doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID;
 141
 142        return 0;
 143}
 144
 145/*
 146 * NetLabel Command Handlers
 147 */
 148
 149/**
 150 * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
 151 * @info: the Generic NETLINK info block
 152 *
 153 * Description:
 154 * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
 155 * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
 156 * error.
 157 *
 158 */
 159static int netlbl_cipsov4_add_std(struct genl_info *info)
 160{
 161        int ret_val = -EINVAL;
 162        struct cipso_v4_doi *doi_def = NULL;
 163        struct nlattr *nla_a;
 164        struct nlattr *nla_b;
 165        int nla_a_rem;
 166        int nla_b_rem;
 167        u32 iter;
 168
 169        if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] ||
 170            !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST])
 171                return -EINVAL;
 172
 173        if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 174                                NLBL_CIPSOV4_A_MAX,
 175                                netlbl_cipsov4_genl_policy) != 0)
 176                return -EINVAL;
 177
 178        doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
 179        if (doi_def == NULL)
 180                return -ENOMEM;
 181        doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL);
 182        if (doi_def->map.std == NULL) {
 183                ret_val = -ENOMEM;
 184                goto add_std_failure;
 185        }
 186        doi_def->type = CIPSO_V4_MAP_STD;
 187
 188        ret_val = netlbl_cipsov4_add_common(info, doi_def);
 189        if (ret_val != 0)
 190                goto add_std_failure;
 191        ret_val = -EINVAL;
 192
 193        nla_for_each_nested(nla_a,
 194                            info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 195                            nla_a_rem)
 196                if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
 197                        if (nla_validate_nested(nla_a,
 198                                            NLBL_CIPSOV4_A_MAX,
 199                                            netlbl_cipsov4_genl_policy) != 0)
 200                                        goto add_std_failure;
 201                        nla_for_each_nested(nla_b, nla_a, nla_b_rem)
 202                                switch (nla_type(nla_b)) {
 203                                case NLBL_CIPSOV4_A_MLSLVLLOC:
 204                                        if (nla_get_u32(nla_b) >
 205                                            CIPSO_V4_MAX_LOC_LVLS)
 206                                                goto add_std_failure;
 207                                        if (nla_get_u32(nla_b) >=
 208                                            doi_def->map.std->lvl.local_size)
 209                                             doi_def->map.std->lvl.local_size =
 210                                                     nla_get_u32(nla_b) + 1;
 211                                        break;
 212                                case NLBL_CIPSOV4_A_MLSLVLREM:
 213                                        if (nla_get_u32(nla_b) >
 214                                            CIPSO_V4_MAX_REM_LVLS)
 215                                                goto add_std_failure;
 216                                        if (nla_get_u32(nla_b) >=
 217                                            doi_def->map.std->lvl.cipso_size)
 218                                             doi_def->map.std->lvl.cipso_size =
 219                                                     nla_get_u32(nla_b) + 1;
 220                                        break;
 221                                }
 222                }
 223        doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size,
 224                                              sizeof(u32),
 225                                              GFP_KERNEL);
 226        if (doi_def->map.std->lvl.local == NULL) {
 227                ret_val = -ENOMEM;
 228                goto add_std_failure;
 229        }
 230        doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size,
 231                                              sizeof(u32),
 232                                              GFP_KERNEL);
 233        if (doi_def->map.std->lvl.cipso == NULL) {
 234                ret_val = -ENOMEM;
 235                goto add_std_failure;
 236        }
 237        for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++)
 238                doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL;
 239        for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++)
 240                doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL;
 241        nla_for_each_nested(nla_a,
 242                            info->attrs[NLBL_CIPSOV4_A_MLSLVLLST],
 243                            nla_a_rem)
 244                if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) {
 245                        struct nlattr *lvl_loc;
 246                        struct nlattr *lvl_rem;
 247
 248                        lvl_loc = nla_find_nested(nla_a,
 249                                                  NLBL_CIPSOV4_A_MLSLVLLOC);
 250                        lvl_rem = nla_find_nested(nla_a,
 251                                                  NLBL_CIPSOV4_A_MLSLVLREM);
 252                        if (lvl_loc == NULL || lvl_rem == NULL)
 253                                goto add_std_failure;
 254                        doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] =
 255                                nla_get_u32(lvl_rem);
 256                        doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] =
 257                                nla_get_u32(lvl_loc);
 258                }
 259
 260        if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) {
 261                if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 262                                        NLBL_CIPSOV4_A_MAX,
 263                                        netlbl_cipsov4_genl_policy) != 0)
 264                        goto add_std_failure;
 265
 266                nla_for_each_nested(nla_a,
 267                                    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 268                                    nla_a_rem)
 269                        if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
 270                                if (nla_validate_nested(nla_a,
 271                                              NLBL_CIPSOV4_A_MAX,
 272                                              netlbl_cipsov4_genl_policy) != 0)
 273                                        goto add_std_failure;
 274                                nla_for_each_nested(nla_b, nla_a, nla_b_rem)
 275                                        switch (nla_type(nla_b)) {
 276                                        case NLBL_CIPSOV4_A_MLSCATLOC:
 277                                                if (nla_get_u32(nla_b) >
 278                                                    CIPSO_V4_MAX_LOC_CATS)
 279                                                        goto add_std_failure;
 280                                                if (nla_get_u32(nla_b) >=
 281                                              doi_def->map.std->cat.local_size)
 282                                             doi_def->map.std->cat.local_size =
 283                                                     nla_get_u32(nla_b) + 1;
 284                                                break;
 285                                        case NLBL_CIPSOV4_A_MLSCATREM:
 286                                                if (nla_get_u32(nla_b) >
 287                                                    CIPSO_V4_MAX_REM_CATS)
 288                                                        goto add_std_failure;
 289                                                if (nla_get_u32(nla_b) >=
 290                                              doi_def->map.std->cat.cipso_size)
 291                                             doi_def->map.std->cat.cipso_size =
 292                                                     nla_get_u32(nla_b) + 1;
 293                                                break;
 294                                        }
 295                        }
 296                doi_def->map.std->cat.local = kcalloc(
 297                                              doi_def->map.std->cat.local_size,
 298                                              sizeof(u32),
 299                                              GFP_KERNEL);
 300                if (doi_def->map.std->cat.local == NULL) {
 301                        ret_val = -ENOMEM;
 302                        goto add_std_failure;
 303                }
 304                doi_def->map.std->cat.cipso = kcalloc(
 305                                              doi_def->map.std->cat.cipso_size,
 306                                              sizeof(u32),
 307                                              GFP_KERNEL);
 308                if (doi_def->map.std->cat.cipso == NULL) {
 309                        ret_val = -ENOMEM;
 310                        goto add_std_failure;
 311                }
 312                for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++)
 313                        doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT;
 314                for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++)
 315                        doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT;
 316                nla_for_each_nested(nla_a,
 317                                    info->attrs[NLBL_CIPSOV4_A_MLSCATLST],
 318                                    nla_a_rem)
 319                        if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) {
 320                                struct nlattr *cat_loc;
 321                                struct nlattr *cat_rem;
 322
 323                                cat_loc = nla_find_nested(nla_a,
 324                                                     NLBL_CIPSOV4_A_MLSCATLOC);
 325                                cat_rem = nla_find_nested(nla_a,
 326                                                     NLBL_CIPSOV4_A_MLSCATREM);
 327                                if (cat_loc == NULL || cat_rem == NULL)
 328                                        goto add_std_failure;
 329                                doi_def->map.std->cat.local[
 330                                                        nla_get_u32(cat_loc)] =
 331                                        nla_get_u32(cat_rem);
 332                                doi_def->map.std->cat.cipso[
 333                                                        nla_get_u32(cat_rem)] =
 334                                        nla_get_u32(cat_loc);
 335                        }
 336        }
 337
 338        ret_val = cipso_v4_doi_add(doi_def);
 339        if (ret_val != 0)
 340                goto add_std_failure;
 341        return 0;
 342
 343add_std_failure:
 344        if (doi_def)
 345                netlbl_cipsov4_doi_free(&doi_def->rcu);
 346        return ret_val;
 347}
 348
 349/**
 350 * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
 351 * @info: the Generic NETLINK info block
 352 *
 353 * Description:
 354 * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
 355 * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
 356 * error.
 357 *
 358 */
 359static int netlbl_cipsov4_add_pass(struct genl_info *info)
 360{
 361        int ret_val;
 362        struct cipso_v4_doi *doi_def = NULL;
 363
 364        if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
 365                return -EINVAL;
 366
 367        doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
 368        if (doi_def == NULL)
 369                return -ENOMEM;
 370        doi_def->type = CIPSO_V4_MAP_PASS;
 371
 372        ret_val = netlbl_cipsov4_add_common(info, doi_def);
 373        if (ret_val != 0)
 374                goto add_pass_failure;
 375
 376        ret_val = cipso_v4_doi_add(doi_def);
 377        if (ret_val != 0)
 378                goto add_pass_failure;
 379        return 0;
 380
 381add_pass_failure:
 382        netlbl_cipsov4_doi_free(&doi_def->rcu);
 383        return ret_val;
 384}
 385
 386/**
 387 * netlbl_cipsov4_add - Handle an ADD message
 388 * @skb: the NETLINK buffer
 389 * @info: the Generic NETLINK info block
 390 *
 391 * Description:
 392 * Create a new DOI definition based on the given ADD message and add it to the
 393 * CIPSO V4 engine.  Returns zero on success, negative values on failure.
 394 *
 395 */
 396static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
 397
 398{
 399        int ret_val = -EINVAL;
 400        u32 type;
 401        u32 doi;
 402        const char *type_str = "(unknown)";
 403        struct audit_buffer *audit_buf;
 404        struct netlbl_audit audit_info;
 405
 406        if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
 407            !info->attrs[NLBL_CIPSOV4_A_MTYPE])
 408                return -EINVAL;
 409
 410        doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
 411        netlbl_netlink_auditinfo(skb, &audit_info);
 412
 413        type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
 414        switch (type) {
 415        case CIPSO_V4_MAP_STD:
 416                type_str = "std";
 417                ret_val = netlbl_cipsov4_add_std(info);
 418                break;
 419        case CIPSO_V4_MAP_PASS:
 420                type_str = "pass";
 421                ret_val = netlbl_cipsov4_add_pass(info);
 422                break;
 423        }
 424        if (ret_val == 0)
 425                atomic_inc(&netlabel_mgmt_protocount);
 426
 427        audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
 428                                              &audit_info);
 429        if (audit_buf != NULL) {
 430                audit_log_format(audit_buf,
 431                                 " cipso_doi=%u cipso_type=%s res=%u",
 432                                 doi,
 433                                 type_str,
 434                                 ret_val == 0 ? 1 : 0);
 435                audit_log_end(audit_buf);
 436        }
 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;
 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_STD:
 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).pid,
 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        return genlmsg_end(cb_arg->skb, data);
 638
 639listall_cb_failure:
 640        genlmsg_cancel(cb_arg->skb, data);
 641        return ret_val;
 642}
 643
 644/**
 645 * netlbl_cipsov4_listall - Handle a LISTALL message
 646 * @skb: the NETLINK buffer
 647 * @cb: the NETLINK callback
 648 *
 649 * Description:
 650 * Process a user generated LISTALL message and respond accordingly.  Returns
 651 * zero on success and negative values on error.
 652 *
 653 */
 654static int netlbl_cipsov4_listall(struct sk_buff *skb,
 655                                  struct netlink_callback *cb)
 656{
 657        struct netlbl_cipsov4_doiwalk_arg cb_arg;
 658        int doi_skip = cb->args[0];
 659
 660        cb_arg.nl_cb = cb;
 661        cb_arg.skb = skb;
 662        cb_arg.seq = cb->nlh->nlmsg_seq;
 663
 664        cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg);
 665
 666        cb->args[0] = doi_skip;
 667        return skb->len;
 668}
 669
 670/**
 671 * netlbl_cipsov4_remove - Handle a REMOVE message
 672 * @skb: the NETLINK buffer
 673 * @info: the Generic NETLINK info block
 674 *
 675 * Description:
 676 * Process a user generated REMOVE message and respond accordingly.  Returns
 677 * zero on success, negative values on failure.
 678 *
 679 */
 680static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
 681{
 682        int ret_val = -EINVAL;
 683        u32 doi = 0;
 684        struct audit_buffer *audit_buf;
 685        struct netlbl_audit audit_info;
 686
 687        if (!info->attrs[NLBL_CIPSOV4_A_DOI])
 688                return -EINVAL;
 689
 690        doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
 691        netlbl_netlink_auditinfo(skb, &audit_info);
 692
 693        ret_val = cipso_v4_doi_remove(doi,
 694                                      &audit_info,
 695                                      netlbl_cipsov4_doi_free);
 696        if (ret_val == 0)
 697                atomic_dec(&netlabel_mgmt_protocount);
 698
 699        audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
 700                                              &audit_info);
 701        if (audit_buf != NULL) {
 702                audit_log_format(audit_buf,
 703                                 " cipso_doi=%u res=%u",
 704                                 doi,
 705                                 ret_val == 0 ? 1 : 0);
 706                audit_log_end(audit_buf);
 707        }
 708
 709        return ret_val;
 710}
 711
 712/*
 713 * NetLabel Generic NETLINK Command Definitions
 714 */
 715
 716static struct genl_ops netlbl_cipsov4_ops[] = {
 717        {
 718        .cmd = NLBL_CIPSOV4_C_ADD,
 719        .flags = GENL_ADMIN_PERM,
 720        .policy = netlbl_cipsov4_genl_policy,
 721        .doit = netlbl_cipsov4_add,
 722        .dumpit = NULL,
 723        },
 724        {
 725        .cmd = NLBL_CIPSOV4_C_REMOVE,
 726        .flags = GENL_ADMIN_PERM,
 727        .policy = netlbl_cipsov4_genl_policy,
 728        .doit = netlbl_cipsov4_remove,
 729        .dumpit = NULL,
 730        },
 731        {
 732        .cmd = NLBL_CIPSOV4_C_LIST,
 733        .flags = 0,
 734        .policy = netlbl_cipsov4_genl_policy,
 735        .doit = netlbl_cipsov4_list,
 736        .dumpit = NULL,
 737        },
 738        {
 739        .cmd = NLBL_CIPSOV4_C_LISTALL,
 740        .flags = 0,
 741        .policy = netlbl_cipsov4_genl_policy,
 742        .doit = NULL,
 743        .dumpit = netlbl_cipsov4_listall,
 744        },
 745};
 746
 747/*
 748 * NetLabel Generic NETLINK Protocol Functions
 749 */
 750
 751/**
 752 * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component
 753 *
 754 * Description:
 755 * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK
 756 * mechanism.  Returns zero on success, negative values on failure.
 757 *
 758 */
 759int __init netlbl_cipsov4_genl_init(void)
 760{
 761        int ret_val, i;
 762
 763        ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
 764        if (ret_val != 0)
 765                return ret_val;
 766
 767        for (i = 0; i < ARRAY_SIZE(netlbl_cipsov4_ops); i++) {
 768                ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
 769                                &netlbl_cipsov4_ops[i]);
 770                if (ret_val != 0)
 771                        return ret_val;
 772        }
 773
 774        return 0;
 775}
 776