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