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