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 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 126 if (entry->type_def.cipsov4 == NULL) 127 goto add_failure; 128 ret_val = netlbl_domhsh_add(entry, &audit_info); 129 if (ret_val != 0) 130 cipso_v4_doi_putdef(entry->type_def.cipsov4); 131 break; 132 default: 133 goto add_failure; 134 } 135 if (ret_val != 0) 136 goto add_failure; 137 138 return 0; 139 140 add_failure: 141 if (entry) 142 kfree(entry->domain); 143 kfree(entry); 144 return ret_val; 145 } 146 147 /** 148 * netlbl_mgmt_remove - Handle a REMOVE message 149 * @skb: the NETLINK buffer 150 * @info: the Generic NETLINK info block 151 * 152 * Description: 153 * Process a user generated REMOVE message and remove the specified domain 154 * mappings. Returns zero on success, negative values on failure. 155 * 156 */ 157 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) 158 { 159 char *domain; 160 struct netlbl_audit audit_info; 161 162 if (!info->attrs[NLBL_MGMT_A_DOMAIN]) 163 return -EINVAL; 164 165 netlbl_netlink_auditinfo(skb, &audit_info); 166 167 domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); 168 return netlbl_domhsh_remove(domain, &audit_info); 169 } 170 171 /** 172 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL 173 * @entry: the domain mapping hash table entry 174 * @arg: the netlbl_domhsh_walk_arg structure 175 * 176 * Description: 177 * This function is designed to be used as a callback to the 178 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL 179 * message. Returns the size of the message on success, negative values on 180 * failure. 181 * 182 */ 183 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) 184 { 185 int ret_val = -ENOMEM; 186 struct netlbl_domhsh_walk_arg *cb_arg = arg; 187 void *data; 188 189 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, 190 cb_arg->seq, &netlbl_mgmt_gnl_family, 191 NLM_F_MULTI, NLBL_MGMT_C_LISTALL); 192 if (data == NULL) 193 goto listall_cb_failure; 194 195 ret_val = nla_put_string(cb_arg->skb, 196 NLBL_MGMT_A_DOMAIN, 197 entry->domain); 198 if (ret_val != 0) 199 goto listall_cb_failure; 200 ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); 201 if (ret_val != 0) 202 goto listall_cb_failure; 203 switch (entry->type) { 204 case NETLBL_NLTYPE_CIPSOV4: 205 ret_val = nla_put_u32(cb_arg->skb, 206 NLBL_MGMT_A_CV4DOI, 207 entry->type_def.cipsov4->doi); 208 if (ret_val != 0) 209 goto listall_cb_failure; 210 break; 211 } 212 213 cb_arg->seq++; 214 return genlmsg_end(cb_arg->skb, data); 215 216 listall_cb_failure: 217 genlmsg_cancel(cb_arg->skb, data); 218 return ret_val; 219 } 220 221 /** 222 * netlbl_mgmt_listall - Handle a LISTALL message 223 * @skb: the NETLINK buffer 224 * @cb: the NETLINK callback 225 * 226 * Description: 227 * Process a user generated LISTALL message and dumps the domain hash table in 228 * a form suitable for use in a kernel generated LISTALL message. Returns zero 229 * on success, negative values on failure. 230 * 231 */ 232 static int netlbl_mgmt_listall(struct sk_buff *skb, 233 struct netlink_callback *cb) 234 { 235 struct netlbl_domhsh_walk_arg cb_arg; 236 u32 skip_bkt = cb->args[0]; 237 u32 skip_chain = cb->args[1]; 238 239 cb_arg.nl_cb = cb; 240 cb_arg.skb = skb; 241 cb_arg.seq = cb->nlh->nlmsg_seq; 242 243 netlbl_domhsh_walk(&skip_bkt, 244 &skip_chain, 245 netlbl_mgmt_listall_cb, 246 &cb_arg); 247 248 cb->args[0] = skip_bkt; 249 cb->args[1] = skip_chain; 250 return skb->len; 251 } 252 253 /** 254 * netlbl_mgmt_adddef - Handle an ADDDEF message 255 * @skb: the NETLINK buffer 256 * @info: the Generic NETLINK info block 257 * 258 * Description: 259 * Process a user generated ADDDEF message and respond accordingly. Returns 260 * zero on success, negative values on failure. 261 * 262 */ 263 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 264 { 265 int ret_val = -EINVAL; 266 struct netlbl_dom_map *entry = NULL; 267 u32 tmp_val; 268 struct netlbl_audit audit_info; 269 270 if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) 271 goto adddef_failure; 272 273 netlbl_netlink_auditinfo(skb, &audit_info); 274 275 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 276 if (entry == NULL) { 277 ret_val = -ENOMEM; 278 goto adddef_failure; 279 } 280 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 281 282 switch (entry->type) { 283 case NETLBL_NLTYPE_UNLABELED: 284 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 285 break; 286 case NETLBL_NLTYPE_CIPSOV4: 287 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 288 goto adddef_failure; 289 290 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 291 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 292 if (entry->type_def.cipsov4 == NULL) 293 goto adddef_failure; 294 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 295 if (ret_val != 0) 296 cipso_v4_doi_putdef(entry->type_def.cipsov4); 297 break; 298 default: 299 goto adddef_failure; 300 } 301 if (ret_val != 0) 302 goto adddef_failure; 303 304 return 0; 305 306 adddef_failure: 307 kfree(entry); 308 return ret_val; 309 } 310 311 /** 312 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 313 * @skb: the NETLINK buffer 314 * @info: the Generic NETLINK info block 315 * 316 * Description: 317 * Process a user generated REMOVEDEF message and remove the default domain 318 * mapping. Returns zero on success, negative values on failure. 319 * 320 */ 321 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 322 { 323 struct netlbl_audit audit_info; 324 325 netlbl_netlink_auditinfo(skb, &audit_info); 326 327 return netlbl_domhsh_remove_default(&audit_info); 328 } 329 330 /** 331 * netlbl_mgmt_listdef - Handle a LISTDEF message 332 * @skb: the NETLINK buffer 333 * @info: the Generic NETLINK info block 334 * 335 * Description: 336 * Process a user generated LISTDEF message and dumps the default domain 337 * mapping in a form suitable for use in a kernel generated LISTDEF message. 338 * Returns zero on success, negative values on failure. 339 * 340 */ 341 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 342 { 343 int ret_val = -ENOMEM; 344 struct sk_buff *ans_skb = NULL; 345 void *data; 346 struct netlbl_dom_map *entry; 347 348 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 349 if (ans_skb == NULL) 350 return -ENOMEM; 351 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 352 0, NLBL_MGMT_C_LISTDEF); 353 if (data == NULL) 354 goto listdef_failure; 355 356 rcu_read_lock(); 357 entry = netlbl_domhsh_getentry(NULL); 358 if (entry == NULL) { 359 ret_val = -ENOENT; 360 goto listdef_failure_lock; 361 } 362 ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); 363 if (ret_val != 0) 364 goto listdef_failure_lock; 365 switch (entry->type) { 366 case NETLBL_NLTYPE_CIPSOV4: 367 ret_val = nla_put_u32(ans_skb, 368 NLBL_MGMT_A_CV4DOI, 369 entry->type_def.cipsov4->doi); 370 if (ret_val != 0) 371 goto listdef_failure_lock; 372 break; 373 } 374 rcu_read_unlock(); 375 376 genlmsg_end(ans_skb, data); 377 return genlmsg_reply(ans_skb, info); 378 379 listdef_failure_lock: 380 rcu_read_unlock(); 381 listdef_failure: 382 kfree_skb(ans_skb); 383 return ret_val; 384 } 385 386 /** 387 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 388 * @skb: the skb to write to 389 * @seq: the NETLINK sequence number 390 * @cb: the NETLINK callback 391 * @protocol: the NetLabel protocol to use in the message 392 * 393 * Description: 394 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 395 * answer a application's PROTOCOLS message. Returns the size of the message 396 * on success, negative values on failure. 397 * 398 */ 399 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 400 struct netlink_callback *cb, 401 u32 protocol) 402 { 403 int ret_val = -ENOMEM; 404 void *data; 405 406 data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 407 &netlbl_mgmt_gnl_family, NLM_F_MULTI, 408 NLBL_MGMT_C_PROTOCOLS); 409 if (data == NULL) 410 goto protocols_cb_failure; 411 412 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 413 if (ret_val != 0) 414 goto protocols_cb_failure; 415 416 return genlmsg_end(skb, data); 417 418 protocols_cb_failure: 419 genlmsg_cancel(skb, data); 420 return ret_val; 421 } 422 423 /** 424 * netlbl_mgmt_protocols - Handle a PROTOCOLS message 425 * @skb: the NETLINK buffer 426 * @cb: the NETLINK callback 427 * 428 * Description: 429 * Process a user generated PROTOCOLS message and respond accordingly. 430 * 431 */ 432 static int netlbl_mgmt_protocols(struct sk_buff *skb, 433 struct netlink_callback *cb) 434 { 435 u32 protos_sent = cb->args[0]; 436 437 if (protos_sent == 0) { 438 if (netlbl_mgmt_protocols_cb(skb, 439 cb, 440 NETLBL_NLTYPE_UNLABELED) < 0) 441 goto protocols_return; 442 protos_sent++; 443 } 444 if (protos_sent == 1) { 445 if (netlbl_mgmt_protocols_cb(skb, 446 cb, 447 NETLBL_NLTYPE_CIPSOV4) < 0) 448 goto protocols_return; 449 protos_sent++; 450 } 451 452 protocols_return: 453 cb->args[0] = protos_sent; 454 return skb->len; 455 } 456 457 /** 458 * netlbl_mgmt_version - Handle a VERSION message 459 * @skb: the NETLINK buffer 460 * @info: the Generic NETLINK info block 461 * 462 * Description: 463 * Process a user generated VERSION message and respond accordingly. Returns 464 * zero on success, negative values on failure. 465 * 466 */ 467 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) 468 { 469 int ret_val = -ENOMEM; 470 struct sk_buff *ans_skb = NULL; 471 void *data; 472 473 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 474 if (ans_skb == NULL) 475 return -ENOMEM; 476 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 477 0, NLBL_MGMT_C_VERSION); 478 if (data == NULL) 479 goto version_failure; 480 481 ret_val = nla_put_u32(ans_skb, 482 NLBL_MGMT_A_VERSION, 483 NETLBL_PROTO_VERSION); 484 if (ret_val != 0) 485 goto version_failure; 486 487 genlmsg_end(ans_skb, data); 488 return genlmsg_reply(ans_skb, info); 489 490 version_failure: 491 kfree_skb(ans_skb); 492 return ret_val; 493 } 494 495 496 /* 497 * NetLabel Generic NETLINK Command Definitions 498 */ 499 500 static struct genl_ops netlbl_mgmt_genl_ops[] = { 501 { 502 .cmd = NLBL_MGMT_C_ADD, 503 .flags = GENL_ADMIN_PERM, 504 .policy = netlbl_mgmt_genl_policy, 505 .doit = netlbl_mgmt_add, 506 .dumpit = NULL, 507 }, 508 { 509 .cmd = NLBL_MGMT_C_REMOVE, 510 .flags = GENL_ADMIN_PERM, 511 .policy = netlbl_mgmt_genl_policy, 512 .doit = netlbl_mgmt_remove, 513 .dumpit = NULL, 514 }, 515 { 516 .cmd = NLBL_MGMT_C_LISTALL, 517 .flags = 0, 518 .policy = netlbl_mgmt_genl_policy, 519 .doit = NULL, 520 .dumpit = netlbl_mgmt_listall, 521 }, 522 { 523 .cmd = NLBL_MGMT_C_ADDDEF, 524 .flags = GENL_ADMIN_PERM, 525 .policy = netlbl_mgmt_genl_policy, 526 .doit = netlbl_mgmt_adddef, 527 .dumpit = NULL, 528 }, 529 { 530 .cmd = NLBL_MGMT_C_REMOVEDEF, 531 .flags = GENL_ADMIN_PERM, 532 .policy = netlbl_mgmt_genl_policy, 533 .doit = netlbl_mgmt_removedef, 534 .dumpit = NULL, 535 }, 536 { 537 .cmd = NLBL_MGMT_C_LISTDEF, 538 .flags = 0, 539 .policy = netlbl_mgmt_genl_policy, 540 .doit = netlbl_mgmt_listdef, 541 .dumpit = NULL, 542 }, 543 { 544 .cmd = NLBL_MGMT_C_PROTOCOLS, 545 .flags = 0, 546 .policy = netlbl_mgmt_genl_policy, 547 .doit = NULL, 548 .dumpit = netlbl_mgmt_protocols, 549 }, 550 { 551 .cmd = NLBL_MGMT_C_VERSION, 552 .flags = 0, 553 .policy = netlbl_mgmt_genl_policy, 554 .doit = netlbl_mgmt_version, 555 .dumpit = NULL, 556 }, 557 }; 558 559 /* 560 * NetLabel Generic NETLINK Protocol Functions 561 */ 562 563 /** 564 * netlbl_mgmt_genl_init - Register the NetLabel management component 565 * 566 * Description: 567 * Register the NetLabel management component with the Generic NETLINK 568 * mechanism. Returns zero on success, negative values on failure. 569 * 570 */ 571 int __init netlbl_mgmt_genl_init(void) 572 { 573 int ret_val, i; 574 575 ret_val = genl_register_family(&netlbl_mgmt_gnl_family); 576 if (ret_val != 0) 577 return ret_val; 578 579 for (i = 0; i < ARRAY_SIZE(netlbl_mgmt_genl_ops); i++) { 580 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 581 &netlbl_mgmt_genl_ops[i]); 582 if (ret_val != 0) 583 return ret_val; 584 } 585 586 return 0; 587 } 588