1 /* 2 * NetLabel CIPSO/IPv4 Support 3 * 4 * This file defines the CIPSO/IPv4 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 <linux/audit.h> 36 #include <net/sock.h> 37 #include <net/netlink.h> 38 #include <net/genetlink.h> 39 #include <net/netlabel.h> 40 #include <net/cipso_ipv4.h> 41 #include <asm/atomic.h> 42 43 #include "netlabel_user.h" 44 #include "netlabel_cipso_v4.h" 45 #include "netlabel_mgmt.h" 46 #include "netlabel_domainhash.h" 47 48 /* Argument struct for cipso_v4_doi_walk() */ 49 struct netlbl_cipsov4_doiwalk_arg { 50 struct netlink_callback *nl_cb; 51 struct sk_buff *skb; 52 u32 seq; 53 }; 54 55 /* Argument struct for netlbl_domhsh_walk() */ 56 struct netlbl_domhsh_walk_arg { 57 struct netlbl_audit *audit_info; 58 u32 doi; 59 }; 60 61 /* NetLabel Generic NETLINK CIPSOv4 family */ 62 static struct genl_family netlbl_cipsov4_gnl_family = { 63 .id = GENL_ID_GENERATE, 64 .hdrsize = 0, 65 .name = NETLBL_NLTYPE_CIPSOV4_NAME, 66 .version = NETLBL_PROTO_VERSION, 67 .maxattr = NLBL_CIPSOV4_A_MAX, 68 }; 69 70 /* NetLabel Netlink attribute policy */ 71 static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1] = { 72 [NLBL_CIPSOV4_A_DOI] = { .type = NLA_U32 }, 73 [NLBL_CIPSOV4_A_MTYPE] = { .type = NLA_U32 }, 74 [NLBL_CIPSOV4_A_TAG] = { .type = NLA_U8 }, 75 [NLBL_CIPSOV4_A_TAGLST] = { .type = NLA_NESTED }, 76 [NLBL_CIPSOV4_A_MLSLVLLOC] = { .type = NLA_U32 }, 77 [NLBL_CIPSOV4_A_MLSLVLREM] = { .type = NLA_U32 }, 78 [NLBL_CIPSOV4_A_MLSLVL] = { .type = NLA_NESTED }, 79 [NLBL_CIPSOV4_A_MLSLVLLST] = { .type = NLA_NESTED }, 80 [NLBL_CIPSOV4_A_MLSCATLOC] = { .type = NLA_U32 }, 81 [NLBL_CIPSOV4_A_MLSCATREM] = { .type = NLA_U32 }, 82 [NLBL_CIPSOV4_A_MLSCAT] = { .type = NLA_NESTED }, 83 [NLBL_CIPSOV4_A_MLSCATLST] = { .type = NLA_NESTED }, 84 }; 85 86 /* 87 * Helper Functions 88 */ 89 90 /** 91 * netlbl_cipsov4_add_common - Parse the common sections of a ADD message 92 * @info: the Generic NETLINK info block 93 * @doi_def: the CIPSO V4 DOI definition 94 * 95 * Description: 96 * Parse the common sections of a ADD message and fill in the related values 97 * in @doi_def. Returns zero on success, negative values on failure. 98 * 99 */ 100 static int netlbl_cipsov4_add_common(struct genl_info *info, 101 struct cipso_v4_doi *doi_def) 102 { 103 struct nlattr *nla; 104 int nla_rem; 105 u32 iter = 0; 106 107 doi_def->doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); 108 109 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST], 110 NLBL_CIPSOV4_A_MAX, 111 netlbl_cipsov4_genl_policy) != 0) 112 return -EINVAL; 113 114 nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem) 115 if (nla_type(nla) == NLBL_CIPSOV4_A_TAG) { 116 if (iter >= CIPSO_V4_TAG_MAXCNT) 117 return -EINVAL; 118 doi_def->tags[iter++] = nla_get_u8(nla); 119 } 120 while (iter < CIPSO_V4_TAG_MAXCNT) 121 doi_def->tags[iter++] = CIPSO_V4_TAG_INVALID; 122 123 return 0; 124 } 125 126 /* 127 * NetLabel Command Handlers 128 */ 129 130 /** 131 * netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition 132 * @info: the Generic NETLINK info block 133 * 134 * Description: 135 * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD 136 * message and add it to the CIPSO V4 engine. Return zero on success and 137 * non-zero on error. 138 * 139 */ 140 static int netlbl_cipsov4_add_std(struct genl_info *info) 141 { 142 int ret_val = -EINVAL; 143 struct cipso_v4_doi *doi_def = NULL; 144 struct nlattr *nla_a; 145 struct nlattr *nla_b; 146 int nla_a_rem; 147 int nla_b_rem; 148 u32 iter; 149 150 if (!info->attrs[NLBL_CIPSOV4_A_TAGLST] || 151 !info->attrs[NLBL_CIPSOV4_A_MLSLVLLST]) 152 return -EINVAL; 153 154 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], 155 NLBL_CIPSOV4_A_MAX, 156 netlbl_cipsov4_genl_policy) != 0) 157 return -EINVAL; 158 159 doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); 160 if (doi_def == NULL) 161 return -ENOMEM; 162 doi_def->map.std = kzalloc(sizeof(*doi_def->map.std), GFP_KERNEL); 163 if (doi_def->map.std == NULL) { 164 ret_val = -ENOMEM; 165 goto add_std_failure; 166 } 167 doi_def->type = CIPSO_V4_MAP_TRANS; 168 169 ret_val = netlbl_cipsov4_add_common(info, doi_def); 170 if (ret_val != 0) 171 goto add_std_failure; 172 ret_val = -EINVAL; 173 174 nla_for_each_nested(nla_a, 175 info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], 176 nla_a_rem) 177 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) { 178 if (nla_validate_nested(nla_a, 179 NLBL_CIPSOV4_A_MAX, 180 netlbl_cipsov4_genl_policy) != 0) 181 goto add_std_failure; 182 nla_for_each_nested(nla_b, nla_a, nla_b_rem) 183 switch (nla_type(nla_b)) { 184 case NLBL_CIPSOV4_A_MLSLVLLOC: 185 if (nla_get_u32(nla_b) > 186 CIPSO_V4_MAX_LOC_LVLS) 187 goto add_std_failure; 188 if (nla_get_u32(nla_b) >= 189 doi_def->map.std->lvl.local_size) 190 doi_def->map.std->lvl.local_size = 191 nla_get_u32(nla_b) + 1; 192 break; 193 case NLBL_CIPSOV4_A_MLSLVLREM: 194 if (nla_get_u32(nla_b) > 195 CIPSO_V4_MAX_REM_LVLS) 196 goto add_std_failure; 197 if (nla_get_u32(nla_b) >= 198 doi_def->map.std->lvl.cipso_size) 199 doi_def->map.std->lvl.cipso_size = 200 nla_get_u32(nla_b) + 1; 201 break; 202 } 203 } 204 doi_def->map.std->lvl.local = kcalloc(doi_def->map.std->lvl.local_size, 205 sizeof(u32), 206 GFP_KERNEL); 207 if (doi_def->map.std->lvl.local == NULL) { 208 ret_val = -ENOMEM; 209 goto add_std_failure; 210 } 211 doi_def->map.std->lvl.cipso = kcalloc(doi_def->map.std->lvl.cipso_size, 212 sizeof(u32), 213 GFP_KERNEL); 214 if (doi_def->map.std->lvl.cipso == NULL) { 215 ret_val = -ENOMEM; 216 goto add_std_failure; 217 } 218 for (iter = 0; iter < doi_def->map.std->lvl.local_size; iter++) 219 doi_def->map.std->lvl.local[iter] = CIPSO_V4_INV_LVL; 220 for (iter = 0; iter < doi_def->map.std->lvl.cipso_size; iter++) 221 doi_def->map.std->lvl.cipso[iter] = CIPSO_V4_INV_LVL; 222 nla_for_each_nested(nla_a, 223 info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], 224 nla_a_rem) 225 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) { 226 struct nlattr *lvl_loc; 227 struct nlattr *lvl_rem; 228 229 lvl_loc = nla_find_nested(nla_a, 230 NLBL_CIPSOV4_A_MLSLVLLOC); 231 lvl_rem = nla_find_nested(nla_a, 232 NLBL_CIPSOV4_A_MLSLVLREM); 233 if (lvl_loc == NULL || lvl_rem == NULL) 234 goto add_std_failure; 235 doi_def->map.std->lvl.local[nla_get_u32(lvl_loc)] = 236 nla_get_u32(lvl_rem); 237 doi_def->map.std->lvl.cipso[nla_get_u32(lvl_rem)] = 238 nla_get_u32(lvl_loc); 239 } 240 241 if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) { 242 if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST], 243 NLBL_CIPSOV4_A_MAX, 244 netlbl_cipsov4_genl_policy) != 0) 245 goto add_std_failure; 246 247 nla_for_each_nested(nla_a, 248 info->attrs[NLBL_CIPSOV4_A_MLSCATLST], 249 nla_a_rem) 250 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) { 251 if (nla_validate_nested(nla_a, 252 NLBL_CIPSOV4_A_MAX, 253 netlbl_cipsov4_genl_policy) != 0) 254 goto add_std_failure; 255 nla_for_each_nested(nla_b, nla_a, nla_b_rem) 256 switch (nla_type(nla_b)) { 257 case NLBL_CIPSOV4_A_MLSCATLOC: 258 if (nla_get_u32(nla_b) > 259 CIPSO_V4_MAX_LOC_CATS) 260 goto add_std_failure; 261 if (nla_get_u32(nla_b) >= 262 doi_def->map.std->cat.local_size) 263 doi_def->map.std->cat.local_size = 264 nla_get_u32(nla_b) + 1; 265 break; 266 case NLBL_CIPSOV4_A_MLSCATREM: 267 if (nla_get_u32(nla_b) > 268 CIPSO_V4_MAX_REM_CATS) 269 goto add_std_failure; 270 if (nla_get_u32(nla_b) >= 271 doi_def->map.std->cat.cipso_size) 272 doi_def->map.std->cat.cipso_size = 273 nla_get_u32(nla_b) + 1; 274 break; 275 } 276 } 277 doi_def->map.std->cat.local = kcalloc( 278 doi_def->map.std->cat.local_size, 279 sizeof(u32), 280 GFP_KERNEL); 281 if (doi_def->map.std->cat.local == NULL) { 282 ret_val = -ENOMEM; 283 goto add_std_failure; 284 } 285 doi_def->map.std->cat.cipso = kcalloc( 286 doi_def->map.std->cat.cipso_size, 287 sizeof(u32), 288 GFP_KERNEL); 289 if (doi_def->map.std->cat.cipso == NULL) { 290 ret_val = -ENOMEM; 291 goto add_std_failure; 292 } 293 for (iter = 0; iter < doi_def->map.std->cat.local_size; iter++) 294 doi_def->map.std->cat.local[iter] = CIPSO_V4_INV_CAT; 295 for (iter = 0; iter < doi_def->map.std->cat.cipso_size; iter++) 296 doi_def->map.std->cat.cipso[iter] = CIPSO_V4_INV_CAT; 297 nla_for_each_nested(nla_a, 298 info->attrs[NLBL_CIPSOV4_A_MLSCATLST], 299 nla_a_rem) 300 if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) { 301 struct nlattr *cat_loc; 302 struct nlattr *cat_rem; 303 304 cat_loc = nla_find_nested(nla_a, 305 NLBL_CIPSOV4_A_MLSCATLOC); 306 cat_rem = nla_find_nested(nla_a, 307 NLBL_CIPSOV4_A_MLSCATREM); 308 if (cat_loc == NULL || cat_rem == NULL) 309 goto add_std_failure; 310 doi_def->map.std->cat.local[ 311 nla_get_u32(cat_loc)] = 312 nla_get_u32(cat_rem); 313 doi_def->map.std->cat.cipso[ 314 nla_get_u32(cat_rem)] = 315 nla_get_u32(cat_loc); 316 } 317 } 318 319 ret_val = cipso_v4_doi_add(doi_def); 320 if (ret_val != 0) 321 goto add_std_failure; 322 return 0; 323 324 add_std_failure: 325 if (doi_def) 326 cipso_v4_doi_free(doi_def); 327 return ret_val; 328 } 329 330 /** 331 * netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition 332 * @info: the Generic NETLINK info block 333 * 334 * Description: 335 * Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message 336 * and add it to the CIPSO V4 engine. Return zero on success and non-zero on 337 * error. 338 * 339 */ 340 static int netlbl_cipsov4_add_pass(struct genl_info *info) 341 { 342 int ret_val; 343 struct cipso_v4_doi *doi_def = NULL; 344 345 if (!info->attrs[NLBL_CIPSOV4_A_TAGLST]) 346 return -EINVAL; 347 348 doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); 349 if (doi_def == NULL) 350 return -ENOMEM; 351 doi_def->type = CIPSO_V4_MAP_PASS; 352 353 ret_val = netlbl_cipsov4_add_common(info, doi_def); 354 if (ret_val != 0) 355 goto add_pass_failure; 356 357 ret_val = cipso_v4_doi_add(doi_def); 358 if (ret_val != 0) 359 goto add_pass_failure; 360 return 0; 361 362 add_pass_failure: 363 cipso_v4_doi_free(doi_def); 364 return ret_val; 365 } 366 367 /** 368 * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition 369 * @info: the Generic NETLINK info block 370 * 371 * Description: 372 * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD 373 * message and add it to the CIPSO V4 engine. Return zero on success and 374 * non-zero on error. 375 * 376 */ 377 static int netlbl_cipsov4_add_local(struct genl_info *info) 378 { 379 int ret_val; 380 struct cipso_v4_doi *doi_def = NULL; 381 382 if (!info->attrs[NLBL_CIPSOV4_A_TAGLST]) 383 return -EINVAL; 384 385 doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); 386 if (doi_def == NULL) 387 return -ENOMEM; 388 doi_def->type = CIPSO_V4_MAP_LOCAL; 389 390 ret_val = netlbl_cipsov4_add_common(info, doi_def); 391 if (ret_val != 0) 392 goto add_local_failure; 393 394 ret_val = cipso_v4_doi_add(doi_def); 395 if (ret_val != 0) 396 goto add_local_failure; 397 return 0; 398 399 add_local_failure: 400 cipso_v4_doi_free(doi_def); 401 return ret_val; 402 } 403 404 /** 405 * netlbl_cipsov4_add - Handle an ADD message 406 * @skb: the NETLINK buffer 407 * @info: the Generic NETLINK info block 408 * 409 * Description: 410 * Create a new DOI definition based on the given ADD message and add it to the 411 * CIPSO V4 engine. Returns zero on success, negative values on failure. 412 * 413 */ 414 static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info) 415 416 { 417 int ret_val = -EINVAL; 418 u32 type; 419 u32 doi; 420 const char *type_str = "(unknown)"; 421 struct audit_buffer *audit_buf; 422 struct netlbl_audit audit_info; 423 424 if (!info->attrs[NLBL_CIPSOV4_A_DOI] || 425 !info->attrs[NLBL_CIPSOV4_A_MTYPE]) 426 return -EINVAL; 427 428 doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); 429 netlbl_netlink_auditinfo(skb, &audit_info); 430 431 type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]); 432 switch (type) { 433 case CIPSO_V4_MAP_TRANS: 434 type_str = "trans"; 435 ret_val = netlbl_cipsov4_add_std(info); 436 break; 437 case CIPSO_V4_MAP_PASS: 438 type_str = "pass"; 439 ret_val = netlbl_cipsov4_add_pass(info); 440 break; 441 case CIPSO_V4_MAP_LOCAL: 442 type_str = "local"; 443 ret_val = netlbl_cipsov4_add_local(info); 444 break; 445 } 446 if (ret_val == 0) 447 atomic_inc(&netlabel_mgmt_protocount); 448 449 audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, 450 &audit_info); 451 if (audit_buf != NULL) { 452 audit_log_format(audit_buf, 453 " cipso_doi=%u cipso_type=%s res=%u", 454 doi, 455 type_str, 456 ret_val == 0 ? 1 : 0); 457 audit_log_end(audit_buf); 458 } 459 460 return ret_val; 461 } 462 463 /** 464 * netlbl_cipsov4_list - Handle a LIST message 465 * @skb: the NETLINK buffer 466 * @info: the Generic NETLINK info block 467 * 468 * Description: 469 * Process a user generated LIST message and respond accordingly. While the 470 * response message generated by the kernel is straightforward, determining 471 * before hand the size of the buffer to allocate is not (we have to generate 472 * the message to know the size). In order to keep this function sane what we 473 * do is allocate a buffer of NLMSG_GOODSIZE and try to fit the response in 474 * that size, if we fail then we restart with a larger buffer and try again. 475 * We continue in this manner until we hit a limit of failed attempts then we 476 * give up and just send an error message. Returns zero on success and 477 * negative values on error. 478 * 479 */ 480 static int netlbl_cipsov4_list(struct sk_buff *skb, struct genl_info *info) 481 { 482 int ret_val; 483 struct sk_buff *ans_skb = NULL; 484 u32 nlsze_mult = 1; 485 void *data; 486 u32 doi; 487 struct nlattr *nla_a; 488 struct nlattr *nla_b; 489 struct cipso_v4_doi *doi_def; 490 u32 iter; 491 492 if (!info->attrs[NLBL_CIPSOV4_A_DOI]) { 493 ret_val = -EINVAL; 494 goto list_failure; 495 } 496 497 list_start: 498 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE * nlsze_mult, GFP_KERNEL); 499 if (ans_skb == NULL) { 500 ret_val = -ENOMEM; 501 goto list_failure; 502 } 503 data = genlmsg_put_reply(ans_skb, info, &netlbl_cipsov4_gnl_family, 504 0, NLBL_CIPSOV4_C_LIST); 505 if (data == NULL) { 506 ret_val = -ENOMEM; 507 goto list_failure; 508 } 509 510 doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); 511 512 rcu_read_lock(); 513 doi_def = cipso_v4_doi_getdef(doi); 514 if (doi_def == NULL) { 515 ret_val = -EINVAL; 516 goto list_failure_lock; 517 } 518 519 ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type); 520 if (ret_val != 0) 521 goto list_failure_lock; 522 523 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_TAGLST); 524 if (nla_a == NULL) { 525 ret_val = -ENOMEM; 526 goto list_failure_lock; 527 } 528 for (iter = 0; 529 iter < CIPSO_V4_TAG_MAXCNT && 530 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID; 531 iter++) { 532 ret_val = nla_put_u8(ans_skb, 533 NLBL_CIPSOV4_A_TAG, 534 doi_def->tags[iter]); 535 if (ret_val != 0) 536 goto list_failure_lock; 537 } 538 nla_nest_end(ans_skb, nla_a); 539 540 switch (doi_def->type) { 541 case CIPSO_V4_MAP_TRANS: 542 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST); 543 if (nla_a == NULL) { 544 ret_val = -ENOMEM; 545 goto list_failure_lock; 546 } 547 for (iter = 0; 548 iter < doi_def->map.std->lvl.local_size; 549 iter++) { 550 if (doi_def->map.std->lvl.local[iter] == 551 CIPSO_V4_INV_LVL) 552 continue; 553 554 nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVL); 555 if (nla_b == NULL) { 556 ret_val = -ENOMEM; 557 goto list_retry; 558 } 559 ret_val = nla_put_u32(ans_skb, 560 NLBL_CIPSOV4_A_MLSLVLLOC, 561 iter); 562 if (ret_val != 0) 563 goto list_retry; 564 ret_val = nla_put_u32(ans_skb, 565 NLBL_CIPSOV4_A_MLSLVLREM, 566 doi_def->map.std->lvl.local[iter]); 567 if (ret_val != 0) 568 goto list_retry; 569 nla_nest_end(ans_skb, nla_b); 570 } 571 nla_nest_end(ans_skb, nla_a); 572 573 nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCATLST); 574 if (nla_a == NULL) { 575 ret_val = -ENOMEM; 576 goto list_retry; 577 } 578 for (iter = 0; 579 iter < doi_def->map.std->cat.local_size; 580 iter++) { 581 if (doi_def->map.std->cat.local[iter] == 582 CIPSO_V4_INV_CAT) 583 continue; 584 585 nla_b = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSCAT); 586 if (nla_b == NULL) { 587 ret_val = -ENOMEM; 588 goto list_retry; 589 } 590 ret_val = nla_put_u32(ans_skb, 591 NLBL_CIPSOV4_A_MLSCATLOC, 592 iter); 593 if (ret_val != 0) 594 goto list_retry; 595 ret_val = nla_put_u32(ans_skb, 596 NLBL_CIPSOV4_A_MLSCATREM, 597 doi_def->map.std->cat.local[iter]); 598 if (ret_val != 0) 599 goto list_retry; 600 nla_nest_end(ans_skb, nla_b); 601 } 602 nla_nest_end(ans_skb, nla_a); 603 604 break; 605 } 606 rcu_read_unlock(); 607 608 genlmsg_end(ans_skb, data); 609 return genlmsg_reply(ans_skb, info); 610 611 list_retry: 612 /* XXX - this limit is a guesstimate */ 613 if (nlsze_mult < 4) { 614 rcu_read_unlock(); 615 kfree_skb(ans_skb); 616 nlsze_mult *= 2; 617 goto list_start; 618 } 619 list_failure_lock: 620 rcu_read_unlock(); 621 list_failure: 622 kfree_skb(ans_skb); 623 return ret_val; 624 } 625 626 /** 627 * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL 628 * @doi_def: the CIPSOv4 DOI definition 629 * @arg: the netlbl_cipsov4_doiwalk_arg structure 630 * 631 * Description: 632 * This function is designed to be used as a callback to the 633 * cipso_v4_doi_walk() function for use in generating a response for a LISTALL 634 * message. Returns the size of the message on success, negative values on 635 * failure. 636 * 637 */ 638 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg) 639 { 640 int ret_val = -ENOMEM; 641 struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg; 642 void *data; 643 644 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, 645 cb_arg->seq, &netlbl_cipsov4_gnl_family, 646 NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL); 647 if (data == NULL) 648 goto listall_cb_failure; 649 650 ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi); 651 if (ret_val != 0) 652 goto listall_cb_failure; 653 ret_val = nla_put_u32(cb_arg->skb, 654 NLBL_CIPSOV4_A_MTYPE, 655 doi_def->type); 656 if (ret_val != 0) 657 goto listall_cb_failure; 658 659 return genlmsg_end(cb_arg->skb, data); 660 661 listall_cb_failure: 662 genlmsg_cancel(cb_arg->skb, data); 663 return ret_val; 664 } 665 666 /** 667 * netlbl_cipsov4_listall - Handle a LISTALL message 668 * @skb: the NETLINK buffer 669 * @cb: the NETLINK callback 670 * 671 * Description: 672 * Process a user generated LISTALL message and respond accordingly. Returns 673 * zero on success and negative values on error. 674 * 675 */ 676 static int netlbl_cipsov4_listall(struct sk_buff *skb, 677 struct netlink_callback *cb) 678 { 679 struct netlbl_cipsov4_doiwalk_arg cb_arg; 680 u32 doi_skip = cb->args[0]; 681 682 cb_arg.nl_cb = cb; 683 cb_arg.skb = skb; 684 cb_arg.seq = cb->nlh->nlmsg_seq; 685 686 cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg); 687 688 cb->args[0] = doi_skip; 689 return skb->len; 690 } 691 692 /** 693 * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE 694 * @entry: LSM domain mapping entry 695 * @arg: the netlbl_domhsh_walk_arg structure 696 * 697 * Description: 698 * This function is intended for use by netlbl_cipsov4_remove() as the callback 699 * for the netlbl_domhsh_walk() function; it removes LSM domain map entries 700 * which are associated with the CIPSO DOI specified in @arg. Returns zero on 701 * success, negative values on failure. 702 * 703 */ 704 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg) 705 { 706 struct netlbl_domhsh_walk_arg *cb_arg = arg; 707 708 if (entry->type == NETLBL_NLTYPE_CIPSOV4 && 709 entry->type_def.cipsov4->doi == cb_arg->doi) 710 return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info); 711 712 return 0; 713 } 714 715 /** 716 * netlbl_cipsov4_remove - Handle a REMOVE message 717 * @skb: the NETLINK buffer 718 * @info: the Generic NETLINK info block 719 * 720 * Description: 721 * Process a user generated REMOVE message and respond accordingly. Returns 722 * zero on success, negative values on failure. 723 * 724 */ 725 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) 726 { 727 int ret_val = -EINVAL; 728 u32 doi = 0; 729 struct netlbl_domhsh_walk_arg cb_arg; 730 struct audit_buffer *audit_buf; 731 struct netlbl_audit audit_info; 732 u32 skip_bkt = 0; 733 u32 skip_chain = 0; 734 735 if (!info->attrs[NLBL_CIPSOV4_A_DOI]) 736 return -EINVAL; 737 738 doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); 739 netlbl_netlink_auditinfo(skb, &audit_info); 740 741 cb_arg.doi = doi; 742 cb_arg.audit_info = &audit_info; 743 ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, 744 netlbl_cipsov4_remove_cb, &cb_arg); 745 if (ret_val == 0 || ret_val == -ENOENT) { 746 ret_val = cipso_v4_doi_remove(doi, &audit_info); 747 if (ret_val == 0) 748 atomic_dec(&netlabel_mgmt_protocount); 749 } 750 751 audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL, 752 &audit_info); 753 if (audit_buf != NULL) { 754 audit_log_format(audit_buf, 755 " cipso_doi=%u res=%u", 756 doi, 757 ret_val == 0 ? 1 : 0); 758 audit_log_end(audit_buf); 759 } 760 761 return ret_val; 762 } 763 764 /* 765 * NetLabel Generic NETLINK Command Definitions 766 */ 767 768 static struct genl_ops netlbl_cipsov4_ops[] = { 769 { 770 .cmd = NLBL_CIPSOV4_C_ADD, 771 .flags = GENL_ADMIN_PERM, 772 .policy = netlbl_cipsov4_genl_policy, 773 .doit = netlbl_cipsov4_add, 774 .dumpit = NULL, 775 }, 776 { 777 .cmd = NLBL_CIPSOV4_C_REMOVE, 778 .flags = GENL_ADMIN_PERM, 779 .policy = netlbl_cipsov4_genl_policy, 780 .doit = netlbl_cipsov4_remove, 781 .dumpit = NULL, 782 }, 783 { 784 .cmd = NLBL_CIPSOV4_C_LIST, 785 .flags = 0, 786 .policy = netlbl_cipsov4_genl_policy, 787 .doit = netlbl_cipsov4_list, 788 .dumpit = NULL, 789 }, 790 { 791 .cmd = NLBL_CIPSOV4_C_LISTALL, 792 .flags = 0, 793 .policy = netlbl_cipsov4_genl_policy, 794 .doit = NULL, 795 .dumpit = netlbl_cipsov4_listall, 796 }, 797 }; 798 799 /* 800 * NetLabel Generic NETLINK Protocol Functions 801 */ 802 803 /** 804 * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component 805 * 806 * Description: 807 * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK 808 * mechanism. Returns zero on success, negative values on failure. 809 * 810 */ 811 int __init netlbl_cipsov4_genl_init(void) 812 { 813 int ret_val, i; 814 815 ret_val = genl_register_family(&netlbl_cipsov4_gnl_family); 816 if (ret_val != 0) 817 return ret_val; 818 819 for (i = 0; i < ARRAY_SIZE(netlbl_cipsov4_ops); i++) { 820 ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family, 821 &netlbl_cipsov4_ops[i]); 822 if (ret_val != 0) 823 return ret_val; 824 } 825 826 return 0; 827 } 828