linux/net/netlabel/netlabel_mgmt.c
<<
>>
Prefs
   1/*
   2 * NetLabel Management Support
   3 *
   4 * This file defines the management 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, 2008
  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/in.h>
  36#include <linux/in6.h>
  37#include <net/sock.h>
  38#include <net/netlink.h>
  39#include <net/genetlink.h>
  40#include <net/ip.h>
  41#include <net/ipv6.h>
  42#include <net/netlabel.h>
  43#include <net/cipso_ipv4.h>
  44#include <asm/atomic.h>
  45
  46#include "netlabel_domainhash.h"
  47#include "netlabel_user.h"
  48#include "netlabel_mgmt.h"
  49
  50/* NetLabel configured protocol counter */
  51atomic_t netlabel_mgmt_protocount = ATOMIC_INIT(0);
  52
  53/* Argument struct for netlbl_domhsh_walk() */
  54struct netlbl_domhsh_walk_arg {
  55        struct netlink_callback *nl_cb;
  56        struct sk_buff *skb;
  57        u32 seq;
  58};
  59
  60/* NetLabel Generic NETLINK CIPSOv4 family */
  61static struct genl_family netlbl_mgmt_gnl_family = {
  62        .id = GENL_ID_GENERATE,
  63        .hdrsize = 0,
  64        .name = NETLBL_NLTYPE_MGMT_NAME,
  65        .version = NETLBL_PROTO_VERSION,
  66        .maxattr = NLBL_MGMT_A_MAX,
  67};
  68
  69/* NetLabel Netlink attribute policy */
  70static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
  71        [NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING },
  72        [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 },
  73        [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 },
  74        [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 },
  75};
  76
  77/*
  78 * Helper Functions
  79 */
  80
  81/**
  82 * netlbl_mgmt_add - Handle an ADD message
  83 * @info: the Generic NETLINK info block
  84 * @audit_info: NetLabel audit information
  85 *
  86 * Description:
  87 * Helper function for the ADD and ADDDEF messages to add the domain mappings
  88 * from the message to the hash table.  See netlabel.h for a description of the
  89 * message format.  Returns zero on success, negative values on failure.
  90 *
  91 */
  92static int netlbl_mgmt_add_common(struct genl_info *info,
  93                                  struct netlbl_audit *audit_info)
  94{
  95        int ret_val = -EINVAL;
  96        struct netlbl_dom_map *entry = NULL;
  97        struct netlbl_domaddr_map *addrmap = NULL;
  98        struct cipso_v4_doi *cipsov4 = NULL;
  99        u32 tmp_val;
 100
 101        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 102        if (entry == NULL) {
 103                ret_val = -ENOMEM;
 104                goto add_failure;
 105        }
 106        entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
 107        if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
 108                size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
 109                entry->domain = kmalloc(tmp_size, GFP_KERNEL);
 110                if (entry->domain == NULL) {
 111                        ret_val = -ENOMEM;
 112                        goto add_failure;
 113                }
 114                nla_strlcpy(entry->domain,
 115                            info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
 116        }
 117
 118        /* NOTE: internally we allow/use a entry->type value of
 119         *       NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
 120         *       to pass that as a protocol value because we need to know the
 121         *       "real" protocol */
 122
 123        switch (entry->type) {
 124        case NETLBL_NLTYPE_UNLABELED:
 125                break;
 126        case NETLBL_NLTYPE_CIPSOV4:
 127                if (!info->attrs[NLBL_MGMT_A_CV4DOI])
 128                        goto add_failure;
 129
 130                tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
 131                cipsov4 = cipso_v4_doi_getdef(tmp_val);
 132                if (cipsov4 == NULL)
 133                        goto add_failure;
 134                entry->type_def.cipsov4 = cipsov4;
 135                break;
 136        default:
 137                goto add_failure;
 138        }
 139
 140        if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
 141                struct in_addr *addr;
 142                struct in_addr *mask;
 143                struct netlbl_domaddr4_map *map;
 144
 145                addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
 146                if (addrmap == NULL) {
 147                        ret_val = -ENOMEM;
 148                        goto add_failure;
 149                }
 150                INIT_LIST_HEAD(&addrmap->list4);
 151                INIT_LIST_HEAD(&addrmap->list6);
 152
 153                if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
 154                    sizeof(struct in_addr)) {
 155                        ret_val = -EINVAL;
 156                        goto add_failure;
 157                }
 158                if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
 159                    sizeof(struct in_addr)) {
 160                        ret_val = -EINVAL;
 161                        goto add_failure;
 162                }
 163                addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
 164                mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
 165
 166                map = kzalloc(sizeof(*map), GFP_KERNEL);
 167                if (map == NULL) {
 168                        ret_val = -ENOMEM;
 169                        goto add_failure;
 170                }
 171                map->list.addr = addr->s_addr & mask->s_addr;
 172                map->list.mask = mask->s_addr;
 173                map->list.valid = 1;
 174                map->type = entry->type;
 175                if (cipsov4)
 176                        map->type_def.cipsov4 = cipsov4;
 177
 178                ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
 179                if (ret_val != 0) {
 180                        kfree(map);
 181                        goto add_failure;
 182                }
 183
 184                entry->type = NETLBL_NLTYPE_ADDRSELECT;
 185                entry->type_def.addrsel = addrmap;
 186#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 187        } else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
 188                struct in6_addr *addr;
 189                struct in6_addr *mask;
 190                struct netlbl_domaddr6_map *map;
 191
 192                addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
 193                if (addrmap == NULL) {
 194                        ret_val = -ENOMEM;
 195                        goto add_failure;
 196                }
 197                INIT_LIST_HEAD(&addrmap->list4);
 198                INIT_LIST_HEAD(&addrmap->list6);
 199
 200                if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
 201                    sizeof(struct in6_addr)) {
 202                        ret_val = -EINVAL;
 203                        goto add_failure;
 204                }
 205                if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
 206                    sizeof(struct in6_addr)) {
 207                        ret_val = -EINVAL;
 208                        goto add_failure;
 209                }
 210                addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
 211                mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
 212
 213                map = kzalloc(sizeof(*map), GFP_KERNEL);
 214                if (map == NULL) {
 215                        ret_val = -ENOMEM;
 216                        goto add_failure;
 217                }
 218                ipv6_addr_copy(&map->list.addr, addr);
 219                map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
 220                map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
 221                map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
 222                map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
 223                ipv6_addr_copy(&map->list.mask, mask);
 224                map->list.valid = 1;
 225                map->type = entry->type;
 226
 227                ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
 228                if (ret_val != 0) {
 229                        kfree(map);
 230                        goto add_failure;
 231                }
 232
 233                entry->type = NETLBL_NLTYPE_ADDRSELECT;
 234                entry->type_def.addrsel = addrmap;
 235#endif /* IPv6 */
 236        }
 237
 238        ret_val = netlbl_domhsh_add(entry, audit_info);
 239        if (ret_val != 0)
 240                goto add_failure;
 241
 242        return 0;
 243
 244add_failure:
 245        if (cipsov4)
 246                cipso_v4_doi_putdef(cipsov4);
 247        if (entry)
 248                kfree(entry->domain);
 249        kfree(addrmap);
 250        kfree(entry);
 251        return ret_val;
 252}
 253
 254/**
 255 * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
 256 * @skb: the NETLINK buffer
 257 * @entry: the map entry
 258 *
 259 * Description:
 260 * This function is a helper function used by the LISTALL and LISTDEF command
 261 * handlers.  The caller is responsibile for ensuring that the RCU read lock
 262 * is held.  Returns zero on success, negative values on failure.
 263 *
 264 */
 265static int netlbl_mgmt_listentry(struct sk_buff *skb,
 266                                 struct netlbl_dom_map *entry)
 267{
 268        int ret_val = 0;
 269        struct nlattr *nla_a;
 270        struct nlattr *nla_b;
 271        struct netlbl_af4list *iter4;
 272#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 273        struct netlbl_af6list *iter6;
 274#endif
 275
 276        if (entry->domain != NULL) {
 277                ret_val = nla_put_string(skb,
 278                                         NLBL_MGMT_A_DOMAIN, entry->domain);
 279                if (ret_val != 0)
 280                        return ret_val;
 281        }
 282
 283        switch (entry->type) {
 284        case NETLBL_NLTYPE_ADDRSELECT:
 285                nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
 286                if (nla_a == NULL)
 287                        return -ENOMEM;
 288
 289                netlbl_af4list_foreach_rcu(iter4,
 290                                           &entry->type_def.addrsel->list4) {
 291                        struct netlbl_domaddr4_map *map4;
 292                        struct in_addr addr_struct;
 293
 294                        nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
 295                        if (nla_b == NULL)
 296                                return -ENOMEM;
 297
 298                        addr_struct.s_addr = iter4->addr;
 299                        ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR,
 300                                          sizeof(struct in_addr),
 301                                          &addr_struct);
 302                        if (ret_val != 0)
 303                                return ret_val;
 304                        addr_struct.s_addr = iter4->mask;
 305                        ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK,
 306                                          sizeof(struct in_addr),
 307                                          &addr_struct);
 308                        if (ret_val != 0)
 309                                return ret_val;
 310                        map4 = netlbl_domhsh_addr4_entry(iter4);
 311                        ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
 312                                              map4->type);
 313                        if (ret_val != 0)
 314                                return ret_val;
 315                        switch (map4->type) {
 316                        case NETLBL_NLTYPE_CIPSOV4:
 317                                ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
 318                                                  map4->type_def.cipsov4->doi);
 319                                if (ret_val != 0)
 320                                        return ret_val;
 321                                break;
 322                        }
 323
 324                        nla_nest_end(skb, nla_b);
 325                }
 326#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 327                netlbl_af6list_foreach_rcu(iter6,
 328                                           &entry->type_def.addrsel->list6) {
 329                        struct netlbl_domaddr6_map *map6;
 330
 331                        nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
 332                        if (nla_b == NULL)
 333                                return -ENOMEM;
 334
 335                        ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR,
 336                                          sizeof(struct in6_addr),
 337                                          &iter6->addr);
 338                        if (ret_val != 0)
 339                                return ret_val;
 340                        ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK,
 341                                          sizeof(struct in6_addr),
 342                                          &iter6->mask);
 343                        if (ret_val != 0)
 344                                return ret_val;
 345                        map6 = netlbl_domhsh_addr6_entry(iter6);
 346                        ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
 347                                              map6->type);
 348                        if (ret_val != 0)
 349                                return ret_val;
 350
 351                        nla_nest_end(skb, nla_b);
 352                }
 353#endif /* IPv6 */
 354
 355                nla_nest_end(skb, nla_a);
 356                break;
 357        case NETLBL_NLTYPE_UNLABELED:
 358                ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
 359                break;
 360        case NETLBL_NLTYPE_CIPSOV4:
 361                ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
 362                if (ret_val != 0)
 363                        return ret_val;
 364                ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
 365                                      entry->type_def.cipsov4->doi);
 366                break;
 367        }
 368
 369        return ret_val;
 370}
 371
 372/*
 373 * NetLabel Command Handlers
 374 */
 375
 376/**
 377 * netlbl_mgmt_add - Handle an ADD message
 378 * @skb: the NETLINK buffer
 379 * @info: the Generic NETLINK info block
 380 *
 381 * Description:
 382 * Process a user generated ADD message and add the domains from the message
 383 * to the hash table.  See netlabel.h for a description of the message format.
 384 * Returns zero on success, negative values on failure.
 385 *
 386 */
 387static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
 388{
 389        struct netlbl_audit audit_info;
 390
 391        if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
 392            (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
 393            (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
 394             info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
 395            (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
 396             info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
 397            ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
 398             (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
 399            ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
 400             (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
 401                return -EINVAL;
 402
 403        netlbl_netlink_auditinfo(skb, &audit_info);
 404
 405        return netlbl_mgmt_add_common(info, &audit_info);
 406}
 407
 408/**
 409 * netlbl_mgmt_remove - Handle a REMOVE message
 410 * @skb: the NETLINK buffer
 411 * @info: the Generic NETLINK info block
 412 *
 413 * Description:
 414 * Process a user generated REMOVE message and remove the specified domain
 415 * mappings.  Returns zero on success, negative values on failure.
 416 *
 417 */
 418static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info)
 419{
 420        char *domain;
 421        struct netlbl_audit audit_info;
 422
 423        if (!info->attrs[NLBL_MGMT_A_DOMAIN])
 424                return -EINVAL;
 425
 426        netlbl_netlink_auditinfo(skb, &audit_info);
 427
 428        domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]);
 429        return netlbl_domhsh_remove(domain, &audit_info);
 430}
 431
 432/**
 433 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL
 434 * @entry: the domain mapping hash table entry
 435 * @arg: the netlbl_domhsh_walk_arg structure
 436 *
 437 * Description:
 438 * This function is designed to be used as a callback to the
 439 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL
 440 * message.  Returns the size of the message on success, negative values on
 441 * failure.
 442 *
 443 */
 444static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
 445{
 446        int ret_val = -ENOMEM;
 447        struct netlbl_domhsh_walk_arg *cb_arg = arg;
 448        void *data;
 449
 450        data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
 451                           cb_arg->seq, &netlbl_mgmt_gnl_family,
 452                           NLM_F_MULTI, NLBL_MGMT_C_LISTALL);
 453        if (data == NULL)
 454                goto listall_cb_failure;
 455
 456        ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
 457        if (ret_val != 0)
 458                goto listall_cb_failure;
 459
 460        cb_arg->seq++;
 461        return genlmsg_end(cb_arg->skb, data);
 462
 463listall_cb_failure:
 464        genlmsg_cancel(cb_arg->skb, data);
 465        return ret_val;
 466}
 467
 468/**
 469 * netlbl_mgmt_listall - Handle a LISTALL message
 470 * @skb: the NETLINK buffer
 471 * @cb: the NETLINK callback
 472 *
 473 * Description:
 474 * Process a user generated LISTALL message and dumps the domain hash table in
 475 * a form suitable for use in a kernel generated LISTALL message.  Returns zero
 476 * on success, negative values on failure.
 477 *
 478 */
 479static int netlbl_mgmt_listall(struct sk_buff *skb,
 480                               struct netlink_callback *cb)
 481{
 482        struct netlbl_domhsh_walk_arg cb_arg;
 483        u32 skip_bkt = cb->args[0];
 484        u32 skip_chain = cb->args[1];
 485
 486        cb_arg.nl_cb = cb;
 487        cb_arg.skb = skb;
 488        cb_arg.seq = cb->nlh->nlmsg_seq;
 489
 490        netlbl_domhsh_walk(&skip_bkt,
 491                           &skip_chain,
 492                           netlbl_mgmt_listall_cb,
 493                           &cb_arg);
 494
 495        cb->args[0] = skip_bkt;
 496        cb->args[1] = skip_chain;
 497        return skb->len;
 498}
 499
 500/**
 501 * netlbl_mgmt_adddef - Handle an ADDDEF message
 502 * @skb: the NETLINK buffer
 503 * @info: the Generic NETLINK info block
 504 *
 505 * Description:
 506 * Process a user generated ADDDEF message and respond accordingly.  Returns
 507 * zero on success, negative values on failure.
 508 *
 509 */
 510static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
 511{
 512        struct netlbl_audit audit_info;
 513
 514        if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
 515            (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
 516             info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
 517            (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
 518             info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
 519            ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
 520             (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
 521            ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
 522             (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
 523                return -EINVAL;
 524
 525        netlbl_netlink_auditinfo(skb, &audit_info);
 526
 527        return netlbl_mgmt_add_common(info, &audit_info);
 528}
 529
 530/**
 531 * netlbl_mgmt_removedef - Handle a REMOVEDEF message
 532 * @skb: the NETLINK buffer
 533 * @info: the Generic NETLINK info block
 534 *
 535 * Description:
 536 * Process a user generated REMOVEDEF message and remove the default domain
 537 * mapping.  Returns zero on success, negative values on failure.
 538 *
 539 */
 540static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info)
 541{
 542        struct netlbl_audit audit_info;
 543
 544        netlbl_netlink_auditinfo(skb, &audit_info);
 545
 546        return netlbl_domhsh_remove_default(&audit_info);
 547}
 548
 549/**
 550 * netlbl_mgmt_listdef - Handle a LISTDEF message
 551 * @skb: the NETLINK buffer
 552 * @info: the Generic NETLINK info block
 553 *
 554 * Description:
 555 * Process a user generated LISTDEF message and dumps the default domain
 556 * mapping in a form suitable for use in a kernel generated LISTDEF message.
 557 * Returns zero on success, negative values on failure.
 558 *
 559 */
 560static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
 561{
 562        int ret_val = -ENOMEM;
 563        struct sk_buff *ans_skb = NULL;
 564        void *data;
 565        struct netlbl_dom_map *entry;
 566
 567        ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 568        if (ans_skb == NULL)
 569                return -ENOMEM;
 570        data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
 571                                 0, NLBL_MGMT_C_LISTDEF);
 572        if (data == NULL)
 573                goto listdef_failure;
 574
 575        rcu_read_lock();
 576        entry = netlbl_domhsh_getentry(NULL);
 577        if (entry == NULL) {
 578                ret_val = -ENOENT;
 579                goto listdef_failure_lock;
 580        }
 581        ret_val = netlbl_mgmt_listentry(ans_skb, entry);
 582        rcu_read_unlock();
 583        if (ret_val != 0)
 584                goto listdef_failure;
 585
 586        genlmsg_end(ans_skb, data);
 587        return genlmsg_reply(ans_skb, info);
 588
 589listdef_failure_lock:
 590        rcu_read_unlock();
 591listdef_failure:
 592        kfree_skb(ans_skb);
 593        return ret_val;
 594}
 595
 596/**
 597 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
 598 * @skb: the skb to write to
 599 * @cb: the NETLINK callback
 600 * @protocol: the NetLabel protocol to use in the message
 601 *
 602 * Description:
 603 * This function is to be used in conjunction with netlbl_mgmt_protocols() to
 604 * answer a application's PROTOCOLS message.  Returns the size of the message
 605 * on success, negative values on failure.
 606 *
 607 */
 608static int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
 609                                    struct netlink_callback *cb,
 610                                    u32 protocol)
 611{
 612        int ret_val = -ENOMEM;
 613        void *data;
 614
 615        data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
 616                           &netlbl_mgmt_gnl_family, NLM_F_MULTI,
 617                           NLBL_MGMT_C_PROTOCOLS);
 618        if (data == NULL)
 619                goto protocols_cb_failure;
 620
 621        ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol);
 622        if (ret_val != 0)
 623                goto protocols_cb_failure;
 624
 625        return genlmsg_end(skb, data);
 626
 627protocols_cb_failure:
 628        genlmsg_cancel(skb, data);
 629        return ret_val;
 630}
 631
 632/**
 633 * netlbl_mgmt_protocols - Handle a PROTOCOLS message
 634 * @skb: the NETLINK buffer
 635 * @cb: the NETLINK callback
 636 *
 637 * Description:
 638 * Process a user generated PROTOCOLS message and respond accordingly.
 639 *
 640 */
 641static int netlbl_mgmt_protocols(struct sk_buff *skb,
 642                                 struct netlink_callback *cb)
 643{
 644        u32 protos_sent = cb->args[0];
 645
 646        if (protos_sent == 0) {
 647                if (netlbl_mgmt_protocols_cb(skb,
 648                                             cb,
 649                                             NETLBL_NLTYPE_UNLABELED) < 0)
 650                        goto protocols_return;
 651                protos_sent++;
 652        }
 653        if (protos_sent == 1) {
 654                if (netlbl_mgmt_protocols_cb(skb,
 655                                             cb,
 656                                             NETLBL_NLTYPE_CIPSOV4) < 0)
 657                        goto protocols_return;
 658                protos_sent++;
 659        }
 660
 661protocols_return:
 662        cb->args[0] = protos_sent;
 663        return skb->len;
 664}
 665
 666/**
 667 * netlbl_mgmt_version - Handle a VERSION message
 668 * @skb: the NETLINK buffer
 669 * @info: the Generic NETLINK info block
 670 *
 671 * Description:
 672 * Process a user generated VERSION message and respond accordingly.  Returns
 673 * zero on success, negative values on failure.
 674 *
 675 */
 676static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
 677{
 678        int ret_val = -ENOMEM;
 679        struct sk_buff *ans_skb = NULL;
 680        void *data;
 681
 682        ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 683        if (ans_skb == NULL)
 684                return -ENOMEM;
 685        data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family,
 686                                 0, NLBL_MGMT_C_VERSION);
 687        if (data == NULL)
 688                goto version_failure;
 689
 690        ret_val = nla_put_u32(ans_skb,
 691                              NLBL_MGMT_A_VERSION,
 692                              NETLBL_PROTO_VERSION);
 693        if (ret_val != 0)
 694                goto version_failure;
 695
 696        genlmsg_end(ans_skb, data);
 697        return genlmsg_reply(ans_skb, info);
 698
 699version_failure:
 700        kfree_skb(ans_skb);
 701        return ret_val;
 702}
 703
 704
 705/*
 706 * NetLabel Generic NETLINK Command Definitions
 707 */
 708
 709static struct genl_ops netlbl_mgmt_genl_ops[] = {
 710        {
 711        .cmd = NLBL_MGMT_C_ADD,
 712        .flags = GENL_ADMIN_PERM,
 713        .policy = netlbl_mgmt_genl_policy,
 714        .doit = netlbl_mgmt_add,
 715        .dumpit = NULL,
 716        },
 717        {
 718        .cmd = NLBL_MGMT_C_REMOVE,
 719        .flags = GENL_ADMIN_PERM,
 720        .policy = netlbl_mgmt_genl_policy,
 721        .doit = netlbl_mgmt_remove,
 722        .dumpit = NULL,
 723        },
 724        {
 725        .cmd = NLBL_MGMT_C_LISTALL,
 726        .flags = 0,
 727        .policy = netlbl_mgmt_genl_policy,
 728        .doit = NULL,
 729        .dumpit = netlbl_mgmt_listall,
 730        },
 731        {
 732        .cmd = NLBL_MGMT_C_ADDDEF,
 733        .flags = GENL_ADMIN_PERM,
 734        .policy = netlbl_mgmt_genl_policy,
 735        .doit = netlbl_mgmt_adddef,
 736        .dumpit = NULL,
 737        },
 738        {
 739        .cmd = NLBL_MGMT_C_REMOVEDEF,
 740        .flags = GENL_ADMIN_PERM,
 741        .policy = netlbl_mgmt_genl_policy,
 742        .doit = netlbl_mgmt_removedef,
 743        .dumpit = NULL,
 744        },
 745        {
 746        .cmd = NLBL_MGMT_C_LISTDEF,
 747        .flags = 0,
 748        .policy = netlbl_mgmt_genl_policy,
 749        .doit = netlbl_mgmt_listdef,
 750        .dumpit = NULL,
 751        },
 752        {
 753        .cmd = NLBL_MGMT_C_PROTOCOLS,
 754        .flags = 0,
 755        .policy = netlbl_mgmt_genl_policy,
 756        .doit = NULL,
 757        .dumpit = netlbl_mgmt_protocols,
 758        },
 759        {
 760        .cmd = NLBL_MGMT_C_VERSION,
 761        .flags = 0,
 762        .policy = netlbl_mgmt_genl_policy,
 763        .doit = netlbl_mgmt_version,
 764        .dumpit = NULL,
 765        },
 766};
 767
 768/*
 769 * NetLabel Generic NETLINK Protocol Functions
 770 */
 771
 772/**
 773 * netlbl_mgmt_genl_init - Register the NetLabel management component
 774 *
 775 * Description:
 776 * Register the NetLabel management component with the Generic NETLINK
 777 * mechanism.  Returns zero on success, negative values on failure.
 778 *
 779 */
 780int __init netlbl_mgmt_genl_init(void)
 781{
 782        return genl_register_family_with_ops(&netlbl_mgmt_gnl_family,
 783                netlbl_mgmt_genl_ops, ARRAY_SIZE(netlbl_mgmt_genl_ops));
 784}
 785