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