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(&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 cipso_v4_doi_putdef(doi_def); 579 rcu_read_unlock(); 580 581 genlmsg_end(ans_skb, data); 582 return genlmsg_reply(ans_skb, info); 583 584 list_retry: 585 /* XXX - this limit is a guesstimate */ 586 if (nlsze_mult < 4) { 587 cipso_v4_doi_putdef(doi_def); 588 rcu_read_unlock(); 589 kfree_skb(ans_skb); 590 nlsze_mult *= 2; 591 goto list_start; 592 } 593 list_failure_lock: 594 cipso_v4_doi_putdef(doi_def); 595 rcu_read_unlock(); 596 list_failure: 597 kfree_skb(ans_skb); 598 return ret_val; 599 } 600 601 /** 602 * netlbl_cipsov4_listall_cb - cipso_v4_doi_walk() callback for LISTALL 603 * @doi_def: the CIPSOv4 DOI definition 604 * @arg: the netlbl_cipsov4_doiwalk_arg structure 605 * 606 * Description: 607 * This function is designed to be used as a callback to the 608 * cipso_v4_doi_walk() function for use in generating a response for a LISTALL 609 * message. Returns the size of the message on success, negative values on 610 * failure. 611 * 612 */ 613 static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg) 614 { 615 int ret_val = -ENOMEM; 616 struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg; 617 void *data; 618 619 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, 620 cb_arg->seq, &netlbl_cipsov4_gnl_family, 621 NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL); 622 if (data == NULL) 623 goto listall_cb_failure; 624 625 ret_val = nla_put_u32(cb_arg->skb, NLBL_CIPSOV4_A_DOI, doi_def->doi); 626 if (ret_val != 0) 627 goto listall_cb_failure; 628 ret_val = nla_put_u32(cb_arg->skb, 629 NLBL_CIPSOV4_A_MTYPE, 630 doi_def->type); 631 if (ret_val != 0) 632 goto listall_cb_failure; 633 634 genlmsg_end(cb_arg->skb, data); 635 return 0; 636 637 listall_cb_failure: 638 genlmsg_cancel(cb_arg->skb, data); 639 return ret_val; 640 } 641 642 /** 643 * netlbl_cipsov4_listall - Handle a LISTALL message 644 * @skb: the NETLINK buffer 645 * @cb: the NETLINK callback 646 * 647 * Description: 648 * Process a user generated LISTALL message and respond accordingly. Returns 649 * zero on success and negative values on error. 650 * 651 */ 652 static int netlbl_cipsov4_listall(struct sk_buff *skb, 653 struct netlink_callback *cb) 654 { 655 struct netlbl_cipsov4_doiwalk_arg cb_arg; 656 u32 doi_skip = cb->args[0]; 657 658 cb_arg.nl_cb = cb; 659 cb_arg.skb = skb; 660 cb_arg.seq = cb->nlh->nlmsg_seq; 661 662 cipso_v4_doi_walk(&doi_skip, netlbl_cipsov4_listall_cb, &cb_arg); 663 664 cb->args[0] = doi_skip; 665 return skb->len; 666 } 667 668 /** 669 * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE 670 * @entry: LSM domain mapping entry 671 * @arg: the netlbl_domhsh_walk_arg structure 672 * 673 * Description: 674 * This function is intended for use by netlbl_cipsov4_remove() as the callback 675 * for the netlbl_domhsh_walk() function; it removes LSM domain map entries 676 * which are associated with the CIPSO DOI specified in @arg. Returns zero on 677 * success, negative values on failure. 678 * 679 */ 680 static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg) 681 { 682 struct netlbl_domhsh_walk_arg *cb_arg = arg; 683 684 if (entry->def.type == NETLBL_NLTYPE_CIPSOV4 && 685 entry->def.cipso->doi == cb_arg->doi) 686 return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info); 687 688 return 0; 689 } 690 691 /** 692 * netlbl_cipsov4_remove - Handle a REMOVE message 693 * @skb: the NETLINK buffer 694 * @info: the Generic NETLINK info block 695 * 696 * Description: 697 * Process a user generated REMOVE message and respond accordingly. Returns 698 * zero on success, negative values on failure. 699 * 700 */ 701 static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) 702 { 703 int ret_val = -EINVAL; 704 struct netlbl_domhsh_walk_arg cb_arg; 705 struct netlbl_audit audit_info; 706 u32 skip_bkt = 0; 707 u32 skip_chain = 0; 708 709 if (!info->attrs[NLBL_CIPSOV4_A_DOI]) 710 return -EINVAL; 711 712 netlbl_netlink_auditinfo(&audit_info); 713 cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); 714 cb_arg.audit_info = &audit_info; 715 ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, 716 netlbl_cipsov4_remove_cb, &cb_arg); 717 if (ret_val == 0 || ret_val == -ENOENT) { 718 ret_val = cipso_v4_doi_remove(cb_arg.doi, &audit_info); 719 if (ret_val == 0) 720 atomic_dec(&netlabel_mgmt_protocount); 721 } 722 723 return ret_val; 724 } 725 726 /* 727 * NetLabel Generic NETLINK Command Definitions 728 */ 729 730 static const struct genl_small_ops netlbl_cipsov4_ops[] = { 731 { 732 .cmd = NLBL_CIPSOV4_C_ADD, 733 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 734 .flags = GENL_ADMIN_PERM, 735 .doit = netlbl_cipsov4_add, 736 .dumpit = NULL, 737 }, 738 { 739 .cmd = NLBL_CIPSOV4_C_REMOVE, 740 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 741 .flags = GENL_ADMIN_PERM, 742 .doit = netlbl_cipsov4_remove, 743 .dumpit = NULL, 744 }, 745 { 746 .cmd = NLBL_CIPSOV4_C_LIST, 747 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 748 .flags = 0, 749 .doit = netlbl_cipsov4_list, 750 .dumpit = NULL, 751 }, 752 { 753 .cmd = NLBL_CIPSOV4_C_LISTALL, 754 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 755 .flags = 0, 756 .doit = NULL, 757 .dumpit = netlbl_cipsov4_listall, 758 }, 759 }; 760 761 static struct genl_family netlbl_cipsov4_gnl_family __ro_after_init = { 762 .hdrsize = 0, 763 .name = NETLBL_NLTYPE_CIPSOV4_NAME, 764 .version = NETLBL_PROTO_VERSION, 765 .maxattr = NLBL_CIPSOV4_A_MAX, 766 .policy = netlbl_cipsov4_genl_policy, 767 .module = THIS_MODULE, 768 .small_ops = netlbl_cipsov4_ops, 769 .n_small_ops = ARRAY_SIZE(netlbl_cipsov4_ops), 770 }; 771 772 /* 773 * NetLabel Generic NETLINK Protocol Functions 774 */ 775 776 /** 777 * netlbl_cipsov4_genl_init - Register the CIPSOv4 NetLabel component 778 * 779 * Description: 780 * Register the CIPSOv4 packet NetLabel component with the Generic NETLINK 781 * mechanism. Returns zero on success, negative values on failure. 782 * 783 */ 784 int __init netlbl_cipsov4_genl_init(void) 785 { 786 return genl_register_family(&netlbl_cipsov4_gnl_family); 787 } 788