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(skb, NLBL_MGMT_A_IPV4ADDR, 297 sizeof(struct in_addr), 298 &addr_struct); 299 if (ret_val != 0) 300 return ret_val; 301 addr_struct.s_addr = iter4->mask; 302 ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK, 303 sizeof(struct in_addr), 304 &addr_struct); 305 if (ret_val != 0) 306 return ret_val; 307 map4 = netlbl_domhsh_addr4_entry(iter4); 308 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 309 map4->def.type); 310 if (ret_val != 0) 311 return ret_val; 312 switch (map4->def.type) { 313 case NETLBL_NLTYPE_CIPSOV4: 314 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, 315 map4->def.cipso->doi); 316 if (ret_val != 0) 317 return ret_val; 318 break; 319 } 320 321 nla_nest_end(skb, nla_b); 322 } 323 #if IS_ENABLED(CONFIG_IPV6) 324 netlbl_af6list_foreach_rcu(iter6, &entry->def.addrsel->list6) { 325 struct netlbl_domaddr6_map *map6; 326 327 nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR); 328 if (nla_b == NULL) 329 return -ENOMEM; 330 331 ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR, 332 sizeof(struct in6_addr), 333 &iter6->addr); 334 if (ret_val != 0) 335 return ret_val; 336 ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK, 337 sizeof(struct in6_addr), 338 &iter6->mask); 339 if (ret_val != 0) 340 return ret_val; 341 map6 = netlbl_domhsh_addr6_entry(iter6); 342 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, 343 map6->def.type); 344 if (ret_val != 0) 345 return ret_val; 346 347 nla_nest_end(skb, nla_b); 348 } 349 #endif /* IPv6 */ 350 351 nla_nest_end(skb, nla_a); 352 break; 353 case NETLBL_NLTYPE_UNLABELED: 354 ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type); 355 break; 356 case NETLBL_NLTYPE_CIPSOV4: 357 ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type); 358 if (ret_val != 0) 359 return ret_val; 360 ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, 361 entry->def.cipso->doi); 362 break; 363 } 364 365 return ret_val; 366 } 367 368 /* 369 * NetLabel Command Handlers 370 */ 371 372 /** 373 * netlbl_mgmt_add - Handle an ADD message 374 * @skb: the NETLINK buffer 375 * @info: the Generic NETLINK info block 376 * 377 * Description: 378 * Process a user generated ADD message and add the domains from the message 379 * to the hash table. See netlabel.h for a description of the message format. 380 * Returns zero on success, negative values on failure. 381 * 382 */ 383 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) 384 { 385 struct netlbl_audit audit_info; 386 387 if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) || 388 (!info->attrs[NLBL_MGMT_A_PROTOCOL]) || 389 (info->attrs[NLBL_MGMT_A_IPV4ADDR] && 390 info->attrs[NLBL_MGMT_A_IPV6ADDR]) || 391 (info->attrs[NLBL_MGMT_A_IPV4MASK] && 392 info->attrs[NLBL_MGMT_A_IPV6MASK]) || 393 ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ 394 (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || 395 ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ 396 (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) 397 return -EINVAL; 398 399 netlbl_netlink_auditinfo(skb, &audit_info); 400 401 return netlbl_mgmt_add_common(info, &audit_info); 402 } 403 404 /** 405 * netlbl_mgmt_remove - Handle a REMOVE message 406 * @skb: the NETLINK buffer 407 * @info: the Generic NETLINK info block 408 * 409 * Description: 410 * Process a user generated REMOVE message and remove the specified domain 411 * mappings. Returns zero on success, negative values on failure. 412 * 413 */ 414 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) 415 { 416 char *domain; 417 struct netlbl_audit audit_info; 418 419 if (!info->attrs[NLBL_MGMT_A_DOMAIN]) 420 return -EINVAL; 421 422 netlbl_netlink_auditinfo(skb, &audit_info); 423 424 domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); 425 return netlbl_domhsh_remove(domain, &audit_info); 426 } 427 428 /** 429 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL 430 * @entry: the domain mapping hash table entry 431 * @arg: the netlbl_domhsh_walk_arg structure 432 * 433 * Description: 434 * This function is designed to be used as a callback to the 435 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL 436 * message. Returns the size of the message on success, negative values on 437 * failure. 438 * 439 */ 440 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) 441 { 442 int ret_val = -ENOMEM; 443 struct netlbl_domhsh_walk_arg *cb_arg = arg; 444 void *data; 445 446 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, 447 cb_arg->seq, &netlbl_mgmt_gnl_family, 448 NLM_F_MULTI, NLBL_MGMT_C_LISTALL); 449 if (data == NULL) 450 goto listall_cb_failure; 451 452 ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry); 453 if (ret_val != 0) 454 goto listall_cb_failure; 455 456 cb_arg->seq++; 457 genlmsg_end(cb_arg->skb, data); 458 return 0; 459 460 listall_cb_failure: 461 genlmsg_cancel(cb_arg->skb, data); 462 return ret_val; 463 } 464 465 /** 466 * netlbl_mgmt_listall - Handle a LISTALL message 467 * @skb: the NETLINK buffer 468 * @cb: the NETLINK callback 469 * 470 * Description: 471 * Process a user generated LISTALL message and dumps the domain hash table in 472 * a form suitable for use in a kernel generated LISTALL message. Returns zero 473 * on success, negative values on failure. 474 * 475 */ 476 static int netlbl_mgmt_listall(struct sk_buff *skb, 477 struct netlink_callback *cb) 478 { 479 struct netlbl_domhsh_walk_arg cb_arg; 480 u32 skip_bkt = cb->args[0]; 481 u32 skip_chain = cb->args[1]; 482 483 cb_arg.nl_cb = cb; 484 cb_arg.skb = skb; 485 cb_arg.seq = cb->nlh->nlmsg_seq; 486 487 netlbl_domhsh_walk(&skip_bkt, 488 &skip_chain, 489 netlbl_mgmt_listall_cb, 490 &cb_arg); 491 492 cb->args[0] = skip_bkt; 493 cb->args[1] = skip_chain; 494 return skb->len; 495 } 496 497 /** 498 * netlbl_mgmt_adddef - Handle an ADDDEF message 499 * @skb: the NETLINK buffer 500 * @info: the Generic NETLINK info block 501 * 502 * Description: 503 * Process a user generated ADDDEF message and respond accordingly. Returns 504 * zero on success, negative values on failure. 505 * 506 */ 507 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 508 { 509 struct netlbl_audit audit_info; 510 511 if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) || 512 (info->attrs[NLBL_MGMT_A_IPV4ADDR] && 513 info->attrs[NLBL_MGMT_A_IPV6ADDR]) || 514 (info->attrs[NLBL_MGMT_A_IPV4MASK] && 515 info->attrs[NLBL_MGMT_A_IPV6MASK]) || 516 ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ 517 (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || 518 ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ 519 (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) 520 return -EINVAL; 521 522 netlbl_netlink_auditinfo(skb, &audit_info); 523 524 return netlbl_mgmt_add_common(info, &audit_info); 525 } 526 527 /** 528 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 529 * @skb: the NETLINK buffer 530 * @info: the Generic NETLINK info block 531 * 532 * Description: 533 * Process a user generated REMOVEDEF message and remove the default domain 534 * mapping. Returns zero on success, negative values on failure. 535 * 536 */ 537 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 538 { 539 struct netlbl_audit audit_info; 540 541 netlbl_netlink_auditinfo(skb, &audit_info); 542 543 return netlbl_domhsh_remove_default(&audit_info); 544 } 545 546 /** 547 * netlbl_mgmt_listdef - Handle a LISTDEF message 548 * @skb: the NETLINK buffer 549 * @info: the Generic NETLINK info block 550 * 551 * Description: 552 * Process a user generated LISTDEF message and dumps the default domain 553 * mapping in a form suitable for use in a kernel generated LISTDEF message. 554 * Returns zero on success, negative values on failure. 555 * 556 */ 557 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 558 { 559 int ret_val = -ENOMEM; 560 struct sk_buff *ans_skb = NULL; 561 void *data; 562 struct netlbl_dom_map *entry; 563 564 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 565 if (ans_skb == NULL) 566 return -ENOMEM; 567 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 568 0, NLBL_MGMT_C_LISTDEF); 569 if (data == NULL) 570 goto listdef_failure; 571 572 rcu_read_lock(); 573 entry = netlbl_domhsh_getentry(NULL); 574 if (entry == NULL) { 575 ret_val = -ENOENT; 576 goto listdef_failure_lock; 577 } 578 ret_val = netlbl_mgmt_listentry(ans_skb, entry); 579 rcu_read_unlock(); 580 if (ret_val != 0) 581 goto listdef_failure; 582 583 genlmsg_end(ans_skb, data); 584 return genlmsg_reply(ans_skb, info); 585 586 listdef_failure_lock: 587 rcu_read_unlock(); 588 listdef_failure: 589 kfree_skb(ans_skb); 590 return ret_val; 591 } 592 593 /** 594 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 595 * @skb: the skb to write to 596 * @cb: the NETLINK callback 597 * @protocol: the NetLabel protocol to use in the message 598 * 599 * Description: 600 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 601 * answer a application's PROTOCOLS message. Returns the size of the message 602 * on success, negative values on failure. 603 * 604 */ 605 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 606 struct netlink_callback *cb, 607 u32 protocol) 608 { 609 int ret_val = -ENOMEM; 610 void *data; 611 612 data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 613 &netlbl_mgmt_gnl_family, NLM_F_MULTI, 614 NLBL_MGMT_C_PROTOCOLS); 615 if (data == NULL) 616 goto protocols_cb_failure; 617 618 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 619 if (ret_val != 0) 620 goto protocols_cb_failure; 621 622 genlmsg_end(skb, data); 623 return 0; 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