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