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