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 = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, 192 cb_arg->seq, &netlbl_mgmt_gnl_family, 193 NLM_F_MULTI, NLBL_MGMT_C_LISTALL); 194 if (data == NULL) 195 goto listall_cb_failure; 196 197 ret_val = nla_put_string(cb_arg->skb, 198 NLBL_MGMT_A_DOMAIN, 199 entry->domain); 200 if (ret_val != 0) 201 goto listall_cb_failure; 202 ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); 203 if (ret_val != 0) 204 goto listall_cb_failure; 205 switch (entry->type) { 206 case NETLBL_NLTYPE_CIPSOV4: 207 ret_val = nla_put_u32(cb_arg->skb, 208 NLBL_MGMT_A_CV4DOI, 209 entry->type_def.cipsov4->doi); 210 if (ret_val != 0) 211 goto listall_cb_failure; 212 break; 213 } 214 215 cb_arg->seq++; 216 return genlmsg_end(cb_arg->skb, data); 217 218 listall_cb_failure: 219 genlmsg_cancel(cb_arg->skb, data); 220 return ret_val; 221 } 222 223 /** 224 * netlbl_mgmt_listall - Handle a LISTALL message 225 * @skb: the NETLINK buffer 226 * @cb: the NETLINK callback 227 * 228 * Description: 229 * Process a user generated LISTALL message and dumps the domain hash table in 230 * a form suitable for use in a kernel generated LISTALL message. Returns zero 231 * on success, negative values on failure. 232 * 233 */ 234 static int netlbl_mgmt_listall(struct sk_buff *skb, 235 struct netlink_callback *cb) 236 { 237 struct netlbl_domhsh_walk_arg cb_arg; 238 u32 skip_bkt = cb->args[0]; 239 u32 skip_chain = cb->args[1]; 240 241 cb_arg.nl_cb = cb; 242 cb_arg.skb = skb; 243 cb_arg.seq = cb->nlh->nlmsg_seq; 244 245 netlbl_domhsh_walk(&skip_bkt, 246 &skip_chain, 247 netlbl_mgmt_listall_cb, 248 &cb_arg); 249 250 cb->args[0] = skip_bkt; 251 cb->args[1] = skip_chain; 252 return skb->len; 253 } 254 255 /** 256 * netlbl_mgmt_adddef - Handle an ADDDEF message 257 * @skb: the NETLINK buffer 258 * @info: the Generic NETLINK info block 259 * 260 * Description: 261 * Process a user generated ADDDEF message and respond accordingly. Returns 262 * zero on success, negative values on failure. 263 * 264 */ 265 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 266 { 267 int ret_val = -EINVAL; 268 struct netlbl_dom_map *entry = NULL; 269 u32 tmp_val; 270 struct netlbl_audit audit_info; 271 272 if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) 273 goto adddef_failure; 274 275 netlbl_netlink_auditinfo(skb, &audit_info); 276 277 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 278 if (entry == NULL) { 279 ret_val = -ENOMEM; 280 goto adddef_failure; 281 } 282 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 283 284 switch (entry->type) { 285 case NETLBL_NLTYPE_UNLABELED: 286 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 287 break; 288 case NETLBL_NLTYPE_CIPSOV4: 289 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 290 goto adddef_failure; 291 292 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 293 /* We should be holding a rcu_read_lock() here while we hold 294 * the result but since the entry will always be deleted when 295 * the CIPSO DOI is deleted we aren't going to keep the 296 * lock. */ 297 rcu_read_lock(); 298 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 299 if (entry->type_def.cipsov4 == NULL) { 300 rcu_read_unlock(); 301 goto adddef_failure; 302 } 303 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 304 rcu_read_unlock(); 305 break; 306 default: 307 goto adddef_failure; 308 } 309 if (ret_val != 0) 310 goto adddef_failure; 311 312 return 0; 313 314 adddef_failure: 315 kfree(entry); 316 return ret_val; 317 } 318 319 /** 320 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 321 * @skb: the NETLINK buffer 322 * @info: the Generic NETLINK info block 323 * 324 * Description: 325 * Process a user generated REMOVEDEF message and remove the default domain 326 * mapping. Returns zero on success, negative values on failure. 327 * 328 */ 329 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 330 { 331 struct netlbl_audit audit_info; 332 333 netlbl_netlink_auditinfo(skb, &audit_info); 334 335 return netlbl_domhsh_remove_default(&audit_info); 336 } 337 338 /** 339 * netlbl_mgmt_listdef - Handle a LISTDEF message 340 * @skb: the NETLINK buffer 341 * @info: the Generic NETLINK info block 342 * 343 * Description: 344 * Process a user generated LISTDEF message and dumps the default domain 345 * mapping in a form suitable for use in a kernel generated LISTDEF message. 346 * Returns zero on success, negative values on failure. 347 * 348 */ 349 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 350 { 351 int ret_val = -ENOMEM; 352 struct sk_buff *ans_skb = NULL; 353 void *data; 354 struct netlbl_dom_map *entry; 355 356 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 357 if (ans_skb == NULL) 358 return -ENOMEM; 359 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 360 0, NLBL_MGMT_C_LISTDEF); 361 if (data == NULL) 362 goto listdef_failure; 363 364 rcu_read_lock(); 365 entry = netlbl_domhsh_getentry(NULL); 366 if (entry == NULL) { 367 ret_val = -ENOENT; 368 goto listdef_failure_lock; 369 } 370 ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); 371 if (ret_val != 0) 372 goto listdef_failure_lock; 373 switch (entry->type) { 374 case NETLBL_NLTYPE_CIPSOV4: 375 ret_val = nla_put_u32(ans_skb, 376 NLBL_MGMT_A_CV4DOI, 377 entry->type_def.cipsov4->doi); 378 if (ret_val != 0) 379 goto listdef_failure_lock; 380 break; 381 } 382 rcu_read_unlock(); 383 384 genlmsg_end(ans_skb, data); 385 386 ret_val = genlmsg_reply(ans_skb, info); 387 if (ret_val != 0) 388 goto listdef_failure; 389 return 0; 390 391 listdef_failure_lock: 392 rcu_read_unlock(); 393 listdef_failure: 394 kfree_skb(ans_skb); 395 return ret_val; 396 } 397 398 /** 399 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 400 * @skb: the skb to write to 401 * @seq: the NETLINK sequence number 402 * @cb: the NETLINK callback 403 * @protocol: the NetLabel protocol to use in the message 404 * 405 * Description: 406 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 407 * answer a application's PROTOCOLS message. Returns the size of the message 408 * on success, negative values on failure. 409 * 410 */ 411 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 412 struct netlink_callback *cb, 413 u32 protocol) 414 { 415 int ret_val = -ENOMEM; 416 void *data; 417 418 data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 419 &netlbl_mgmt_gnl_family, NLM_F_MULTI, 420 NLBL_MGMT_C_PROTOCOLS); 421 if (data == NULL) 422 goto protocols_cb_failure; 423 424 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 425 if (ret_val != 0) 426 goto protocols_cb_failure; 427 428 return genlmsg_end(skb, data); 429 430 protocols_cb_failure: 431 genlmsg_cancel(skb, data); 432 return ret_val; 433 } 434 435 /** 436 * netlbl_mgmt_protocols - Handle a PROTOCOLS message 437 * @skb: the NETLINK buffer 438 * @cb: the NETLINK callback 439 * 440 * Description: 441 * Process a user generated PROTOCOLS message and respond accordingly. 442 * 443 */ 444 static int netlbl_mgmt_protocols(struct sk_buff *skb, 445 struct netlink_callback *cb) 446 { 447 u32 protos_sent = cb->args[0]; 448 449 if (protos_sent == 0) { 450 if (netlbl_mgmt_protocols_cb(skb, 451 cb, 452 NETLBL_NLTYPE_UNLABELED) < 0) 453 goto protocols_return; 454 protos_sent++; 455 } 456 if (protos_sent == 1) { 457 if (netlbl_mgmt_protocols_cb(skb, 458 cb, 459 NETLBL_NLTYPE_CIPSOV4) < 0) 460 goto protocols_return; 461 protos_sent++; 462 } 463 464 protocols_return: 465 cb->args[0] = protos_sent; 466 return skb->len; 467 } 468 469 /** 470 * netlbl_mgmt_version - Handle a VERSION message 471 * @skb: the NETLINK buffer 472 * @info: the Generic NETLINK info block 473 * 474 * Description: 475 * Process a user generated VERSION message and respond accordingly. Returns 476 * zero on success, negative values on failure. 477 * 478 */ 479 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) 480 { 481 int ret_val = -ENOMEM; 482 struct sk_buff *ans_skb = NULL; 483 void *data; 484 485 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 486 if (ans_skb == NULL) 487 return -ENOMEM; 488 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 489 0, NLBL_MGMT_C_VERSION); 490 if (data == NULL) 491 goto version_failure; 492 493 ret_val = nla_put_u32(ans_skb, 494 NLBL_MGMT_A_VERSION, 495 NETLBL_PROTO_VERSION); 496 if (ret_val != 0) 497 goto version_failure; 498 499 genlmsg_end(ans_skb, data); 500 501 ret_val = genlmsg_reply(ans_skb, info); 502 if (ret_val != 0) 503 goto version_failure; 504 return 0; 505 506 version_failure: 507 kfree_skb(ans_skb); 508 return ret_val; 509 } 510 511 512 /* 513 * NetLabel Generic NETLINK Command Definitions 514 */ 515 516 static struct genl_ops netlbl_mgmt_genl_c_add = { 517 .cmd = NLBL_MGMT_C_ADD, 518 .flags = GENL_ADMIN_PERM, 519 .policy = netlbl_mgmt_genl_policy, 520 .doit = netlbl_mgmt_add, 521 .dumpit = NULL, 522 }; 523 524 static struct genl_ops netlbl_mgmt_genl_c_remove = { 525 .cmd = NLBL_MGMT_C_REMOVE, 526 .flags = GENL_ADMIN_PERM, 527 .policy = netlbl_mgmt_genl_policy, 528 .doit = netlbl_mgmt_remove, 529 .dumpit = NULL, 530 }; 531 532 static struct genl_ops netlbl_mgmt_genl_c_listall = { 533 .cmd = NLBL_MGMT_C_LISTALL, 534 .flags = 0, 535 .policy = netlbl_mgmt_genl_policy, 536 .doit = NULL, 537 .dumpit = netlbl_mgmt_listall, 538 }; 539 540 static struct genl_ops netlbl_mgmt_genl_c_adddef = { 541 .cmd = NLBL_MGMT_C_ADDDEF, 542 .flags = GENL_ADMIN_PERM, 543 .policy = netlbl_mgmt_genl_policy, 544 .doit = netlbl_mgmt_adddef, 545 .dumpit = NULL, 546 }; 547 548 static struct genl_ops netlbl_mgmt_genl_c_removedef = { 549 .cmd = NLBL_MGMT_C_REMOVEDEF, 550 .flags = GENL_ADMIN_PERM, 551 .policy = netlbl_mgmt_genl_policy, 552 .doit = netlbl_mgmt_removedef, 553 .dumpit = NULL, 554 }; 555 556 static struct genl_ops netlbl_mgmt_genl_c_listdef = { 557 .cmd = NLBL_MGMT_C_LISTDEF, 558 .flags = 0, 559 .policy = netlbl_mgmt_genl_policy, 560 .doit = netlbl_mgmt_listdef, 561 .dumpit = NULL, 562 }; 563 564 static struct genl_ops netlbl_mgmt_genl_c_protocols = { 565 .cmd = NLBL_MGMT_C_PROTOCOLS, 566 .flags = 0, 567 .policy = netlbl_mgmt_genl_policy, 568 .doit = NULL, 569 .dumpit = netlbl_mgmt_protocols, 570 }; 571 572 static struct genl_ops netlbl_mgmt_genl_c_version = { 573 .cmd = NLBL_MGMT_C_VERSION, 574 .flags = 0, 575 .policy = netlbl_mgmt_genl_policy, 576 .doit = netlbl_mgmt_version, 577 .dumpit = NULL, 578 }; 579 580 /* 581 * NetLabel Generic NETLINK Protocol Functions 582 */ 583 584 /** 585 * netlbl_mgmt_genl_init - Register the NetLabel management component 586 * 587 * Description: 588 * Register the NetLabel management component with the Generic NETLINK 589 * mechanism. Returns zero on success, negative values on failure. 590 * 591 */ 592 int netlbl_mgmt_genl_init(void) 593 { 594 int ret_val; 595 596 ret_val = genl_register_family(&netlbl_mgmt_gnl_family); 597 if (ret_val != 0) 598 return ret_val; 599 600 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 601 &netlbl_mgmt_genl_c_add); 602 if (ret_val != 0) 603 return ret_val; 604 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 605 &netlbl_mgmt_genl_c_remove); 606 if (ret_val != 0) 607 return ret_val; 608 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 609 &netlbl_mgmt_genl_c_listall); 610 if (ret_val != 0) 611 return ret_val; 612 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 613 &netlbl_mgmt_genl_c_adddef); 614 if (ret_val != 0) 615 return ret_val; 616 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 617 &netlbl_mgmt_genl_c_removedef); 618 if (ret_val != 0) 619 return ret_val; 620 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 621 &netlbl_mgmt_genl_c_listdef); 622 if (ret_val != 0) 623 return ret_val; 624 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 625 &netlbl_mgmt_genl_c_protocols); 626 if (ret_val != 0) 627 return ret_val; 628 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 629 &netlbl_mgmt_genl_c_version); 630 if (ret_val != 0) 631 return ret_val; 632 633 return 0; 634 } 635