netlabel_mgmt.c (22174B)
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}