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