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 return genlmsg_reply(ans_skb, info); 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 return genlmsg_reply(ans_skb, info); 501 502 version_failure: 503 kfree_skb(ans_skb); 504 return ret_val; 505 } 506 507 508 /* 509 * NetLabel Generic NETLINK Command Definitions 510 */ 511 512 static struct genl_ops netlbl_mgmt_genl_ops[] = { 513 { 514 .cmd = NLBL_MGMT_C_ADD, 515 .flags = GENL_ADMIN_PERM, 516 .policy = netlbl_mgmt_genl_policy, 517 .doit = netlbl_mgmt_add, 518 .dumpit = NULL, 519 }, 520 { 521 .cmd = NLBL_MGMT_C_REMOVE, 522 .flags = GENL_ADMIN_PERM, 523 .policy = netlbl_mgmt_genl_policy, 524 .doit = netlbl_mgmt_remove, 525 .dumpit = NULL, 526 }, 527 { 528 .cmd = NLBL_MGMT_C_LISTALL, 529 .flags = 0, 530 .policy = netlbl_mgmt_genl_policy, 531 .doit = NULL, 532 .dumpit = netlbl_mgmt_listall, 533 }, 534 { 535 .cmd = NLBL_MGMT_C_ADDDEF, 536 .flags = GENL_ADMIN_PERM, 537 .policy = netlbl_mgmt_genl_policy, 538 .doit = netlbl_mgmt_adddef, 539 .dumpit = NULL, 540 }, 541 { 542 .cmd = NLBL_MGMT_C_REMOVEDEF, 543 .flags = GENL_ADMIN_PERM, 544 .policy = netlbl_mgmt_genl_policy, 545 .doit = netlbl_mgmt_removedef, 546 .dumpit = NULL, 547 }, 548 { 549 .cmd = NLBL_MGMT_C_LISTDEF, 550 .flags = 0, 551 .policy = netlbl_mgmt_genl_policy, 552 .doit = netlbl_mgmt_listdef, 553 .dumpit = NULL, 554 }, 555 { 556 .cmd = NLBL_MGMT_C_PROTOCOLS, 557 .flags = 0, 558 .policy = netlbl_mgmt_genl_policy, 559 .doit = NULL, 560 .dumpit = netlbl_mgmt_protocols, 561 }, 562 { 563 .cmd = NLBL_MGMT_C_VERSION, 564 .flags = 0, 565 .policy = netlbl_mgmt_genl_policy, 566 .doit = netlbl_mgmt_version, 567 .dumpit = NULL, 568 }, 569 }; 570 571 /* 572 * NetLabel Generic NETLINK Protocol Functions 573 */ 574 575 /** 576 * netlbl_mgmt_genl_init - Register the NetLabel management component 577 * 578 * Description: 579 * Register the NetLabel management component with the Generic NETLINK 580 * mechanism. Returns zero on success, negative values on failure. 581 * 582 */ 583 int __init netlbl_mgmt_genl_init(void) 584 { 585 int ret_val, i; 586 587 ret_val = genl_register_family(&netlbl_mgmt_gnl_family); 588 if (ret_val != 0) 589 return ret_val; 590 591 for (i = 0; i < ARRAY_SIZE(netlbl_mgmt_genl_ops); i++) { 592 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 593 &netlbl_mgmt_genl_ops[i]); 594 if (ret_val != 0) 595 return ret_val; 596 } 597 598 return 0; 599 } 600