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