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.moore@hp.com> 9 * 10 */ 11 12 /* 13 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 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 <net/sock.h> 36 #include <net/netlink.h> 37 #include <net/genetlink.h> 38 #include <net/netlabel.h> 39 #include <net/cipso_ipv4.h> 40 41 #include "netlabel_domainhash.h" 42 #include "netlabel_user.h" 43 #include "netlabel_mgmt.h" 44 45 /* Argument struct for netlbl_domhsh_walk() */ 46 struct netlbl_domhsh_walk_arg { 47 struct netlink_callback *nl_cb; 48 struct sk_buff *skb; 49 u32 seq; 50 }; 51 52 /* NetLabel Generic NETLINK CIPSOv4 family */ 53 static struct genl_family netlbl_mgmt_gnl_family = { 54 .id = GENL_ID_GENERATE, 55 .hdrsize = 0, 56 .name = NETLBL_NLTYPE_MGMT_NAME, 57 .version = NETLBL_PROTO_VERSION, 58 .maxattr = NLBL_MGMT_A_MAX, 59 }; 60 61 /* NetLabel Netlink attribute policy */ 62 static struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { 63 [NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING }, 64 [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 }, 65 [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 }, 66 [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 }, 67 }; 68 69 /* 70 * NetLabel Command Handlers 71 */ 72 73 /** 74 * netlbl_mgmt_add - Handle an ADD message 75 * @skb: the NETLINK buffer 76 * @info: the Generic NETLINK info block 77 * 78 * Description: 79 * Process a user generated ADD message and add the domains from the message 80 * to the hash table. See netlabel.h for a description of the message format. 81 * Returns zero on success, negative values on failure. 82 * 83 */ 84 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) 85 { 86 int ret_val = -EINVAL; 87 struct netlbl_dom_map *entry = NULL; 88 size_t tmp_size; 89 u32 tmp_val; 90 struct netlbl_audit audit_info; 91 92 if (!info->attrs[NLBL_MGMT_A_DOMAIN] || 93 !info->attrs[NLBL_MGMT_A_PROTOCOL]) 94 goto add_failure; 95 96 netlbl_netlink_auditinfo(skb, &audit_info); 97 98 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 99 if (entry == NULL) { 100 ret_val = -ENOMEM; 101 goto add_failure; 102 } 103 tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); 104 entry->domain = kmalloc(tmp_size, GFP_KERNEL); 105 if (entry->domain == NULL) { 106 ret_val = -ENOMEM; 107 goto add_failure; 108 } 109 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 110 nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); 111 112 switch (entry->type) { 113 case NETLBL_NLTYPE_UNLABELED: 114 ret_val = netlbl_domhsh_add(entry, &audit_info); 115 break; 116 case NETLBL_NLTYPE_CIPSOV4: 117 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 118 goto add_failure; 119 120 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 121 /* We should be holding a rcu_read_lock() here while we hold 122 * the result but since the entry will always be deleted when 123 * the CIPSO DOI is deleted we aren't going to keep the 124 * lock. */ 125 rcu_read_lock(); 126 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 127 if (entry->type_def.cipsov4 == NULL) { 128 rcu_read_unlock(); 129 goto add_failure; 130 } 131 ret_val = netlbl_domhsh_add(entry, &audit_info); 132 rcu_read_unlock(); 133 break; 134 default: 135 goto add_failure; 136 } 137 if (ret_val != 0) 138 goto add_failure; 139 140 return 0; 141 142 add_failure: 143 if (entry) 144 kfree(entry->domain); 145 kfree(entry); 146 return ret_val; 147 } 148 149 /** 150 * netlbl_mgmt_remove - Handle a REMOVE message 151 * @skb: the NETLINK buffer 152 * @info: the Generic NETLINK info block 153 * 154 * Description: 155 * Process a user generated REMOVE message and remove the specified domain 156 * mappings. Returns zero on success, negative values on failure. 157 * 158 */ 159 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) 160 { 161 char *domain; 162 struct netlbl_audit audit_info; 163 164 if (!info->attrs[NLBL_MGMT_A_DOMAIN]) 165 return -EINVAL; 166 167 netlbl_netlink_auditinfo(skb, &audit_info); 168 169 domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); 170 return netlbl_domhsh_remove(domain, &audit_info); 171 } 172 173 /** 174 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL 175 * @entry: the domain mapping hash table entry 176 * @arg: the netlbl_domhsh_walk_arg structure 177 * 178 * Description: 179 * This function is designed to be used as a callback to the 180 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL 181 * message. Returns the size of the message on success, negative values on 182 * failure. 183 * 184 */ 185 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) 186 { 187 int ret_val = -ENOMEM; 188 struct netlbl_domhsh_walk_arg *cb_arg = arg; 189 void *data; 190 191 data = netlbl_netlink_hdr_put(cb_arg->skb, 192 NETLINK_CB(cb_arg->nl_cb->skb).pid, 193 cb_arg->seq, 194 netlbl_mgmt_gnl_family.id, 195 NLM_F_MULTI, 196 NLBL_MGMT_C_LISTALL); 197 if (data == NULL) 198 goto listall_cb_failure; 199 200 ret_val = nla_put_string(cb_arg->skb, 201 NLBL_MGMT_A_DOMAIN, 202 entry->domain); 203 if (ret_val != 0) 204 goto listall_cb_failure; 205 ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); 206 if (ret_val != 0) 207 goto listall_cb_failure; 208 switch (entry->type) { 209 case NETLBL_NLTYPE_CIPSOV4: 210 ret_val = nla_put_u32(cb_arg->skb, 211 NLBL_MGMT_A_CV4DOI, 212 entry->type_def.cipsov4->doi); 213 if (ret_val != 0) 214 goto listall_cb_failure; 215 break; 216 } 217 218 cb_arg->seq++; 219 return genlmsg_end(cb_arg->skb, data); 220 221 listall_cb_failure: 222 genlmsg_cancel(cb_arg->skb, data); 223 return ret_val; 224 } 225 226 /** 227 * netlbl_mgmt_listall - Handle a LISTALL message 228 * @skb: the NETLINK buffer 229 * @cb: the NETLINK callback 230 * 231 * Description: 232 * Process a user generated LISTALL message and dumps the domain hash table in 233 * a form suitable for use in a kernel generated LISTALL message. Returns zero 234 * on success, negative values on failure. 235 * 236 */ 237 static int netlbl_mgmt_listall(struct sk_buff *skb, 238 struct netlink_callback *cb) 239 { 240 struct netlbl_domhsh_walk_arg cb_arg; 241 u32 skip_bkt = cb->args[0]; 242 u32 skip_chain = cb->args[1]; 243 244 cb_arg.nl_cb = cb; 245 cb_arg.skb = skb; 246 cb_arg.seq = cb->nlh->nlmsg_seq; 247 248 netlbl_domhsh_walk(&skip_bkt, 249 &skip_chain, 250 netlbl_mgmt_listall_cb, 251 &cb_arg); 252 253 cb->args[0] = skip_bkt; 254 cb->args[1] = skip_chain; 255 return skb->len; 256 } 257 258 /** 259 * netlbl_mgmt_adddef - Handle an ADDDEF message 260 * @skb: the NETLINK buffer 261 * @info: the Generic NETLINK info block 262 * 263 * Description: 264 * Process a user generated ADDDEF message and respond accordingly. Returns 265 * zero on success, negative values on failure. 266 * 267 */ 268 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 269 { 270 int ret_val = -EINVAL; 271 struct netlbl_dom_map *entry = NULL; 272 u32 tmp_val; 273 struct netlbl_audit audit_info; 274 275 if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) 276 goto adddef_failure; 277 278 netlbl_netlink_auditinfo(skb, &audit_info); 279 280 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 281 if (entry == NULL) { 282 ret_val = -ENOMEM; 283 goto adddef_failure; 284 } 285 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 286 287 switch (entry->type) { 288 case NETLBL_NLTYPE_UNLABELED: 289 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 290 break; 291 case NETLBL_NLTYPE_CIPSOV4: 292 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 293 goto adddef_failure; 294 295 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 296 /* We should be holding a rcu_read_lock() here while we hold 297 * the result but since the entry will always be deleted when 298 * the CIPSO DOI is deleted we aren't going to keep the 299 * lock. */ 300 rcu_read_lock(); 301 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 302 if (entry->type_def.cipsov4 == NULL) { 303 rcu_read_unlock(); 304 goto adddef_failure; 305 } 306 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 307 rcu_read_unlock(); 308 break; 309 default: 310 goto adddef_failure; 311 } 312 if (ret_val != 0) 313 goto adddef_failure; 314 315 return 0; 316 317 adddef_failure: 318 kfree(entry); 319 return ret_val; 320 } 321 322 /** 323 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 324 * @skb: the NETLINK buffer 325 * @info: the Generic NETLINK info block 326 * 327 * Description: 328 * Process a user generated REMOVEDEF message and remove the default domain 329 * mapping. Returns zero on success, negative values on failure. 330 * 331 */ 332 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 333 { 334 struct netlbl_audit audit_info; 335 336 netlbl_netlink_auditinfo(skb, &audit_info); 337 338 return netlbl_domhsh_remove_default(&audit_info); 339 } 340 341 /** 342 * netlbl_mgmt_listdef - Handle a LISTDEF message 343 * @skb: the NETLINK buffer 344 * @info: the Generic NETLINK info block 345 * 346 * Description: 347 * Process a user generated LISTDEF message and dumps the default domain 348 * mapping in a form suitable for use in a kernel generated LISTDEF message. 349 * Returns zero on success, negative values on failure. 350 * 351 */ 352 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 353 { 354 int ret_val = -ENOMEM; 355 struct sk_buff *ans_skb = NULL; 356 void *data; 357 struct netlbl_dom_map *entry; 358 359 ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 360 if (ans_skb == NULL) 361 return -ENOMEM; 362 data = netlbl_netlink_hdr_put(ans_skb, 363 info->snd_pid, 364 info->snd_seq, 365 netlbl_mgmt_gnl_family.id, 366 0, 367 NLBL_MGMT_C_LISTDEF); 368 if (data == NULL) 369 goto listdef_failure; 370 371 rcu_read_lock(); 372 entry = netlbl_domhsh_getentry(NULL); 373 if (entry == NULL) { 374 ret_val = -ENOENT; 375 goto listdef_failure_lock; 376 } 377 ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); 378 if (ret_val != 0) 379 goto listdef_failure_lock; 380 switch (entry->type) { 381 case NETLBL_NLTYPE_CIPSOV4: 382 ret_val = nla_put_u32(ans_skb, 383 NLBL_MGMT_A_CV4DOI, 384 entry->type_def.cipsov4->doi); 385 if (ret_val != 0) 386 goto listdef_failure_lock; 387 break; 388 } 389 rcu_read_unlock(); 390 391 genlmsg_end(ans_skb, data); 392 393 ret_val = genlmsg_unicast(ans_skb, info->snd_pid); 394 if (ret_val != 0) 395 goto listdef_failure; 396 return 0; 397 398 listdef_failure_lock: 399 rcu_read_unlock(); 400 listdef_failure: 401 kfree_skb(ans_skb); 402 return ret_val; 403 } 404 405 /** 406 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 407 * @skb: the skb to write to 408 * @seq: the NETLINK sequence number 409 * @cb: the NETLINK callback 410 * @protocol: the NetLabel protocol to use in the message 411 * 412 * Description: 413 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 414 * answer a application's PROTOCOLS message. Returns the size of the message 415 * on success, negative values on failure. 416 * 417 */ 418 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 419 struct netlink_callback *cb, 420 u32 protocol) 421 { 422 int ret_val = -ENOMEM; 423 void *data; 424 425 data = netlbl_netlink_hdr_put(skb, 426 NETLINK_CB(cb->skb).pid, 427 cb->nlh->nlmsg_seq, 428 netlbl_mgmt_gnl_family.id, 429 NLM_F_MULTI, 430 NLBL_MGMT_C_PROTOCOLS); 431 if (data == NULL) 432 goto protocols_cb_failure; 433 434 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 435 if (ret_val != 0) 436 goto protocols_cb_failure; 437 438 return genlmsg_end(skb, data); 439 440 protocols_cb_failure: 441 genlmsg_cancel(skb, data); 442 return ret_val; 443 } 444 445 /** 446 * netlbl_mgmt_protocols - Handle a PROTOCOLS message 447 * @skb: the NETLINK buffer 448 * @cb: the NETLINK callback 449 * 450 * Description: 451 * Process a user generated PROTOCOLS message and respond accordingly. 452 * 453 */ 454 static int netlbl_mgmt_protocols(struct sk_buff *skb, 455 struct netlink_callback *cb) 456 { 457 u32 protos_sent = cb->args[0]; 458 459 if (protos_sent == 0) { 460 if (netlbl_mgmt_protocols_cb(skb, 461 cb, 462 NETLBL_NLTYPE_UNLABELED) < 0) 463 goto protocols_return; 464 protos_sent++; 465 } 466 if (protos_sent == 1) { 467 if (netlbl_mgmt_protocols_cb(skb, 468 cb, 469 NETLBL_NLTYPE_CIPSOV4) < 0) 470 goto protocols_return; 471 protos_sent++; 472 } 473 474 protocols_return: 475 cb->args[0] = protos_sent; 476 return skb->len; 477 } 478 479 /** 480 * netlbl_mgmt_version - Handle a VERSION message 481 * @skb: the NETLINK buffer 482 * @info: the Generic NETLINK info block 483 * 484 * Description: 485 * Process a user generated VERSION message and respond accordingly. Returns 486 * zero on success, negative values on failure. 487 * 488 */ 489 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) 490 { 491 int ret_val = -ENOMEM; 492 struct sk_buff *ans_skb = NULL; 493 void *data; 494 495 ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 496 if (ans_skb == NULL) 497 return -ENOMEM; 498 data = netlbl_netlink_hdr_put(ans_skb, 499 info->snd_pid, 500 info->snd_seq, 501 netlbl_mgmt_gnl_family.id, 502 0, 503 NLBL_MGMT_C_VERSION); 504 if (data == NULL) 505 goto version_failure; 506 507 ret_val = nla_put_u32(ans_skb, 508 NLBL_MGMT_A_VERSION, 509 NETLBL_PROTO_VERSION); 510 if (ret_val != 0) 511 goto version_failure; 512 513 genlmsg_end(ans_skb, data); 514 515 ret_val = genlmsg_unicast(ans_skb, info->snd_pid); 516 if (ret_val != 0) 517 goto version_failure; 518 return 0; 519 520 version_failure: 521 kfree_skb(ans_skb); 522 return ret_val; 523 } 524 525 526 /* 527 * NetLabel Generic NETLINK Command Definitions 528 */ 529 530 static struct genl_ops netlbl_mgmt_genl_c_add = { 531 .cmd = NLBL_MGMT_C_ADD, 532 .flags = GENL_ADMIN_PERM, 533 .policy = netlbl_mgmt_genl_policy, 534 .doit = netlbl_mgmt_add, 535 .dumpit = NULL, 536 }; 537 538 static struct genl_ops netlbl_mgmt_genl_c_remove = { 539 .cmd = NLBL_MGMT_C_REMOVE, 540 .flags = GENL_ADMIN_PERM, 541 .policy = netlbl_mgmt_genl_policy, 542 .doit = netlbl_mgmt_remove, 543 .dumpit = NULL, 544 }; 545 546 static struct genl_ops netlbl_mgmt_genl_c_listall = { 547 .cmd = NLBL_MGMT_C_LISTALL, 548 .flags = 0, 549 .policy = netlbl_mgmt_genl_policy, 550 .doit = NULL, 551 .dumpit = netlbl_mgmt_listall, 552 }; 553 554 static struct genl_ops netlbl_mgmt_genl_c_adddef = { 555 .cmd = NLBL_MGMT_C_ADDDEF, 556 .flags = GENL_ADMIN_PERM, 557 .policy = netlbl_mgmt_genl_policy, 558 .doit = netlbl_mgmt_adddef, 559 .dumpit = NULL, 560 }; 561 562 static struct genl_ops netlbl_mgmt_genl_c_removedef = { 563 .cmd = NLBL_MGMT_C_REMOVEDEF, 564 .flags = GENL_ADMIN_PERM, 565 .policy = netlbl_mgmt_genl_policy, 566 .doit = netlbl_mgmt_removedef, 567 .dumpit = NULL, 568 }; 569 570 static struct genl_ops netlbl_mgmt_genl_c_listdef = { 571 .cmd = NLBL_MGMT_C_LISTDEF, 572 .flags = 0, 573 .policy = netlbl_mgmt_genl_policy, 574 .doit = netlbl_mgmt_listdef, 575 .dumpit = NULL, 576 }; 577 578 static struct genl_ops netlbl_mgmt_genl_c_protocols = { 579 .cmd = NLBL_MGMT_C_PROTOCOLS, 580 .flags = 0, 581 .policy = netlbl_mgmt_genl_policy, 582 .doit = NULL, 583 .dumpit = netlbl_mgmt_protocols, 584 }; 585 586 static struct genl_ops netlbl_mgmt_genl_c_version = { 587 .cmd = NLBL_MGMT_C_VERSION, 588 .flags = 0, 589 .policy = netlbl_mgmt_genl_policy, 590 .doit = netlbl_mgmt_version, 591 .dumpit = NULL, 592 }; 593 594 /* 595 * NetLabel Generic NETLINK Protocol Functions 596 */ 597 598 /** 599 * netlbl_mgmt_genl_init - Register the NetLabel management component 600 * 601 * Description: 602 * Register the NetLabel management component with the Generic NETLINK 603 * mechanism. Returns zero on success, negative values on failure. 604 * 605 */ 606 int netlbl_mgmt_genl_init(void) 607 { 608 int ret_val; 609 610 ret_val = genl_register_family(&netlbl_mgmt_gnl_family); 611 if (ret_val != 0) 612 return ret_val; 613 614 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 615 &netlbl_mgmt_genl_c_add); 616 if (ret_val != 0) 617 return ret_val; 618 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 619 &netlbl_mgmt_genl_c_remove); 620 if (ret_val != 0) 621 return ret_val; 622 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 623 &netlbl_mgmt_genl_c_listall); 624 if (ret_val != 0) 625 return ret_val; 626 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 627 &netlbl_mgmt_genl_c_adddef); 628 if (ret_val != 0) 629 return ret_val; 630 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 631 &netlbl_mgmt_genl_c_removedef); 632 if (ret_val != 0) 633 return ret_val; 634 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 635 &netlbl_mgmt_genl_c_listdef); 636 if (ret_val != 0) 637 return ret_val; 638 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 639 &netlbl_mgmt_genl_c_protocols); 640 if (ret_val != 0) 641 return ret_val; 642 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 643 &netlbl_mgmt_genl_c_version); 644 if (ret_val != 0) 645 return ret_val; 646 647 return 0; 648 } 649