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