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