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