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