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