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_noflag(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_noflag(skb, 327 NLBL_MGMT_A_ADDRSELECTOR); 328 if (nla_b == NULL) 329 return -ENOMEM; 330 331 addr_struct.s_addr = iter4->addr; 332 ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4ADDR, 333 addr_struct.s_addr); 334 if (ret_val != 0) 335 return ret_val; 336 addr_struct.s_addr = iter4->mask; 337 ret_val = nla_put_in_addr(skb, NLBL_MGMT_A_IPV4MASK, 338 addr_struct.s_addr); 339 if (ret_val != 0) 340 return ret_val; 341 map4 = netlbl_domhsh_addr4_entry(iter4); 342 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 343 map4->def.type); 344 if (ret_val != 0) 345 return ret_val; 346 switch (map4->def.type) { 347 case NETLBL_NLTYPE_CIPSOV4: 348 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, 349 map4->def.cipso->doi); 350 if (ret_val != 0) 351 return ret_val; 352 break; 353 } 354 355 nla_nest_end(skb, nla_b); 356 } 357 #if IS_ENABLED(CONFIG_IPV6) 358 netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) { 359 struct netlbl_domaddr6_map *map6; 360 361 nla_b = nla_nest_start_noflag(skb, 362 NLBL_MGMT_A_ADDRSELECTOR); 363 if (nla_b == NULL) 364 return -ENOMEM; 365 366 ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6ADDR, 367 &iter6->addr); 368 if (ret_val != 0) 369 return ret_val; 370 ret_val = nla_put_in6_addr(skb, NLBL_MGMT_A_IPV6MASK, 371 &iter6->mask); 372 if (ret_val != 0) 373 return ret_val; 374 map6 = netlbl_domhsh_addr6_entry(iter6); 375 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 376 map6->def.type); 377 if (ret_val != 0) 378 return ret_val; 379 380 switch (map6->def.type) { 381 case NETLBL_NLTYPE_CALIPSO: 382 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI, 383 map6->def.calipso->doi); 384 if (ret_val != 0) 385 return ret_val; 386 break; 387 } 388 389 nla_nest_end(skb, nla_b); 390 } 391 #endif /* IPv6 */ 392 393 nla_nest_end(skb, nla_a); 394 break; 395 case NETLBL_NLTYPE_UNLABELED: 396 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 397 entry->def.type); 398 break; 399 case NETLBL_NLTYPE_CIPSOV4: 400 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 401 entry->def.type); 402 if (ret_val != 0) 403 return ret_val; 404 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, 405 entry->def.cipso->doi); 406 break; 407 case NETLBL_NLTYPE_CALIPSO: 408 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 409 entry->def.type); 410 if (ret_val != 0) 411 return ret_val; 412 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI, 413 entry->def.calipso->doi); 414 break; 415 } 416 417 return ret_val; 418 } 419 420 /* 421 * NetLabel Command Handlers 422 */ 423 424 /** 425 * netlbl_mgmt_add - Handle an ADD message 426 * @skb: the NETLINK buffer 427 * @info: the Generic NETLINK info block 428 * 429 * Description: 430 * Process a user generated ADD message and add the domains from the message 431 * to the hash table. See netlabel.h for a description of the message format. 432 * Returns zero on success, negative values on failure. 433 * 434 */ 435 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) 436 { 437 struct netlbl_audit audit_info; 438 439 if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) || 440 (!info->attrs[NLBL_MGMT_A_PROTOCOL]) || 441 (info->attrs[NLBL_MGMT_A_IPV4ADDR] && 442 info->attrs[NLBL_MGMT_A_IPV6ADDR]) || 443 (info->attrs[NLBL_MGMT_A_IPV4MASK] && 444 info->attrs[NLBL_MGMT_A_IPV6MASK]) || 445 ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ 446 (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || 447 ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ 448 (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) 449 return -EINVAL; 450 451 netlbl_netlink_auditinfo(skb, &audit_info); 452 453 return netlbl_mgmt_add_common(info, &audit_info); 454 } 455 456 /** 457 * netlbl_mgmt_remove - Handle a REMOVE message 458 * @skb: the NETLINK buffer 459 * @info: the Generic NETLINK info block 460 * 461 * Description: 462 * Process a user generated REMOVE message and remove the specified domain 463 * mappings. Returns zero on success, negative values on failure. 464 * 465 */ 466 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) 467 { 468 char *domain; 469 struct netlbl_audit audit_info; 470 471 if (!info->attrs[NLBL_MGMT_A_DOMAIN]) 472 return -EINVAL; 473 474 netlbl_netlink_auditinfo(skb, &audit_info); 475 476 domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); 477 return netlbl_domhsh_remove(domain, AF_UNSPEC, &audit_info); 478 } 479 480 /** 481 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL 482 * @entry: the domain mapping hash table entry 483 * @arg: the netlbl_domhsh_walk_arg structure 484 * 485 * Description: 486 * This function is designed to be used as a callback to the 487 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL 488 * message. Returns the size of the message on success, negative values on 489 * failure. 490 * 491 */ 492 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) 493 { 494 int ret_val = -ENOMEM; 495 struct netlbl_domhsh_walk_arg *cb_arg = arg; 496 void *data; 497 498 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, 499 cb_arg->seq, &netlbl_mgmt_gnl_family, 500 NLM_F_MULTI, NLBL_MGMT_C_LISTALL); 501 if (data == NULL) 502 goto listall_cb_failure; 503 504 ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry); 505 if (ret_val != 0) 506 goto listall_cb_failure; 507 508 cb_arg->seq++; 509 genlmsg_end(cb_arg->skb, data); 510 return 0; 511 512 listall_cb_failure: 513 genlmsg_cancel(cb_arg->skb, data); 514 return ret_val; 515 } 516 517 /** 518 * netlbl_mgmt_listall - Handle a LISTALL message 519 * @skb: the NETLINK buffer 520 * @cb: the NETLINK callback 521 * 522 * Description: 523 * Process a user generated LISTALL message and dumps the domain hash table in 524 * a form suitable for use in a kernel generated LISTALL message. Returns zero 525 * on success, negative values on failure. 526 * 527 */ 528 static int netlbl_mgmt_listall(struct sk_buff *skb, 529 struct netlink_callback *cb) 530 { 531 struct netlbl_domhsh_walk_arg cb_arg; 532 u32 skip_bkt = cb->args[0]; 533 u32 skip_chain = cb->args[1]; 534 535 cb_arg.nl_cb = cb; 536 cb_arg.skb = skb; 537 cb_arg.seq = cb->nlh->nlmsg_seq; 538 539 netlbl_domhsh_walk(&skip_bkt, 540 &skip_chain, 541 netlbl_mgmt_listall_cb, 542 &cb_arg); 543 544 cb->args[0] = skip_bkt; 545 cb->args[1] = skip_chain; 546 return skb->len; 547 } 548 549 /** 550 * netlbl_mgmt_adddef - Handle an ADDDEF message 551 * @skb: the NETLINK buffer 552 * @info: the Generic NETLINK info block 553 * 554 * Description: 555 * Process a user generated ADDDEF message and respond accordingly. Returns 556 * zero on success, negative values on failure. 557 * 558 */ 559 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 560 { 561 struct netlbl_audit audit_info; 562 563 if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) || 564 (info->attrs[NLBL_MGMT_A_IPV4ADDR] && 565 info->attrs[NLBL_MGMT_A_IPV6ADDR]) || 566 (info->attrs[NLBL_MGMT_A_IPV4MASK] && 567 info->attrs[NLBL_MGMT_A_IPV6MASK]) || 568 ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ 569 (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || 570 ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ 571 (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) 572 return -EINVAL; 573 574 netlbl_netlink_auditinfo(skb, &audit_info); 575 576 return netlbl_mgmt_add_common(info, &audit_info); 577 } 578 579 /** 580 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 581 * @skb: the NETLINK buffer 582 * @info: the Generic NETLINK info block 583 * 584 * Description: 585 * Process a user generated REMOVEDEF message and remove the default domain 586 * mapping. Returns zero on success, negative values on failure. 587 * 588 */ 589 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 590 { 591 struct netlbl_audit audit_info; 592 593 netlbl_netlink_auditinfo(skb, &audit_info); 594 595 return netlbl_domhsh_remove_default(AF_UNSPEC, &audit_info); 596 } 597 598 /** 599 * netlbl_mgmt_listdef - Handle a LISTDEF message 600 * @skb: the NETLINK buffer 601 * @info: the Generic NETLINK info block 602 * 603 * Description: 604 * Process a user generated LISTDEF message and dumps the default domain 605 * mapping in a form suitable for use in a kernel generated LISTDEF message. 606 * Returns zero on success, negative values on failure. 607 * 608 */ 609 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 610 { 611 int ret_val = -ENOMEM; 612 struct sk_buff *ans_skb = NULL; 613 void *data; 614 struct netlbl_dom_map *entry; 615 u16 family; 616 617 if (info->attrs[NLBL_MGMT_A_FAMILY]) 618 family = nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]); 619 else 620 family = AF_INET; 621 622 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 623 if (ans_skb == NULL) 624 return -ENOMEM; 625 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 626 0, NLBL_MGMT_C_LISTDEF); 627 if (data == NULL) 628 goto listdef_failure; 629 630 rcu_read_lock(); 631 entry = netlbl_domhsh_getentry(NULL, family); 632 if (entry == NULL) { 633 ret_val = -ENOENT; 634 goto listdef_failure_lock; 635 } 636 ret_val = netlbl_mgmt_listentry(ans_skb, entry); 637 rcu_read_unlock(); 638 if (ret_val != 0) 639 goto listdef_failure; 640 641 genlmsg_end(ans_skb, data); 642 return genlmsg_reply(ans_skb, info); 643 644 listdef_failure_lock: 645 rcu_read_unlock(); 646 listdef_failure: 647 kfree_skb(ans_skb); 648 return ret_val; 649 } 650 651 /** 652 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 653 * @skb: the skb to write to 654 * @cb: the NETLINK callback 655 * @protocol: the NetLabel protocol to use in the message 656 * 657 * Description: 658 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 659 * answer a application's PROTOCOLS message. Returns the size of the message 660 * on success, negative values on failure. 661 * 662 */ 663 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 664 struct netlink_callback *cb, 665 u32 protocol) 666 { 667 int ret_val = -ENOMEM; 668 void *data; 669 670 data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 671 &netlbl_mgmt_gnl_family, NLM_F_MULTI, 672 NLBL_MGMT_C_PROTOCOLS); 673 if (data == NULL) 674 goto protocols_cb_failure; 675 676 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 677 if (ret_val != 0) 678 goto protocols_cb_failure; 679 680 genlmsg_end(skb, data); 681 return 0; 682 683 protocols_cb_failure: 684 genlmsg_cancel(skb, data); 685 return ret_val; 686 } 687 688 /** 689 * netlbl_mgmt_protocols - Handle a PROTOCOLS message 690 * @skb: the NETLINK buffer 691 * @cb: the NETLINK callback 692 * 693 * Description: 694 * Process a user generated PROTOCOLS message and respond accordingly. 695 * 696 */ 697 static int netlbl_mgmt_protocols(struct sk_buff *skb, 698 struct netlink_callback *cb) 699 { 700 u32 protos_sent = cb->args[0]; 701 702 if (protos_sent == 0) { 703 if (netlbl_mgmt_protocols_cb(skb, 704 cb, 705 NETLBL_NLTYPE_UNLABELED) < 0) 706 goto protocols_return; 707 protos_sent++; 708 } 709 if (protos_sent == 1) { 710 if (netlbl_mgmt_protocols_cb(skb, 711 cb, 712 NETLBL_NLTYPE_CIPSOV4) < 0) 713 goto protocols_return; 714 protos_sent++; 715 } 716 #if IS_ENABLED(CONFIG_IPV6) 717 if (protos_sent == 2) { 718 if (netlbl_mgmt_protocols_cb(skb, 719 cb, 720 NETLBL_NLTYPE_CALIPSO) < 0) 721 goto protocols_return; 722 protos_sent++; 723 } 724 #endif 725 726 protocols_return: 727 cb->args[0] = protos_sent; 728 return skb->len; 729 } 730 731 /** 732 * netlbl_mgmt_version - Handle a VERSION message 733 * @skb: the NETLINK buffer 734 * @info: the Generic NETLINK info block 735 * 736 * Description: 737 * Process a user generated VERSION message and respond accordingly. Returns 738 * zero on success, negative values on failure. 739 * 740 */ 741 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) 742 { 743 int ret_val = -ENOMEM; 744 struct sk_buff *ans_skb = NULL; 745 void *data; 746 747 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 748 if (ans_skb == NULL) 749 return -ENOMEM; 750 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 751 0, NLBL_MGMT_C_VERSION); 752 if (data == NULL) 753 goto version_failure; 754 755 ret_val = nla_put_u32(ans_skb, 756 NLBL_MGMT_A_VERSION, 757 NETLBL_PROTO_VERSION); 758 if (ret_val != 0) 759 goto version_failure; 760 761 genlmsg_end(ans_skb, data); 762 return genlmsg_reply(ans_skb, info); 763 764 version_failure: 765 kfree_skb(ans_skb); 766 return ret_val; 767 } 768 769 770 /* 771 * NetLabel Generic NETLINK Command Definitions 772 */ 773 774 static const struct genl_ops netlbl_mgmt_genl_ops[] = { 775 { 776 .cmd = NLBL_MGMT_C_ADD, 777 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 778 .flags = GENL_ADMIN_PERM, 779 .doit = netlbl_mgmt_add, 780 .dumpit = NULL, 781 }, 782 { 783 .cmd = NLBL_MGMT_C_REMOVE, 784 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 785 .flags = GENL_ADMIN_PERM, 786 .doit = netlbl_mgmt_remove, 787 .dumpit = NULL, 788 }, 789 { 790 .cmd = NLBL_MGMT_C_LISTALL, 791 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 792 .flags = 0, 793 .doit = NULL, 794 .dumpit = netlbl_mgmt_listall, 795 }, 796 { 797 .cmd = NLBL_MGMT_C_ADDDEF, 798 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 799 .flags = GENL_ADMIN_PERM, 800 .doit = netlbl_mgmt_adddef, 801 .dumpit = NULL, 802 }, 803 { 804 .cmd = NLBL_MGMT_C_REMOVEDEF, 805 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 806 .flags = GENL_ADMIN_PERM, 807 .doit = netlbl_mgmt_removedef, 808 .dumpit = NULL, 809 }, 810 { 811 .cmd = NLBL_MGMT_C_LISTDEF, 812 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 813 .flags = 0, 814 .doit = netlbl_mgmt_listdef, 815 .dumpit = NULL, 816 }, 817 { 818 .cmd = NLBL_MGMT_C_PROTOCOLS, 819 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 820 .flags = 0, 821 .doit = NULL, 822 .dumpit = netlbl_mgmt_protocols, 823 }, 824 { 825 .cmd = NLBL_MGMT_C_VERSION, 826 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 827 .flags = 0, 828 .doit = netlbl_mgmt_version, 829 .dumpit = NULL, 830 }, 831 }; 832 833 static struct genl_family netlbl_mgmt_gnl_family __ro_after_init = { 834 .hdrsize = 0, 835 .name = NETLBL_NLTYPE_MGMT_NAME, 836 .version = NETLBL_PROTO_VERSION, 837 .maxattr = NLBL_MGMT_A_MAX, 838 .policy = netlbl_mgmt_genl_policy, 839 .module = THIS_MODULE, 840 .ops = netlbl_mgmt_genl_ops, 841 .n_ops = ARRAY_SIZE(netlbl_mgmt_genl_ops), 842 }; 843 844 /* 845 * NetLabel Generic NETLINK Protocol Functions 846 */ 847 848 /** 849 * netlbl_mgmt_genl_init - Register the NetLabel management component 850 * 851 * Description: 852 * Register the NetLabel management component with the Generic NETLINK 853 * mechanism. Returns zero on success, negative values on failure. 854 * 855 */ 856 int __init netlbl_mgmt_genl_init(void) 857 { 858 return genl_register_family(&netlbl_mgmt_gnl_family); 859 } 860