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