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