1 #include <stdarg.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <inttypes.h> 6 #include <sys/types.h> 7 #include <unistd.h> 8 9 #include <arpa/inet.h> 10 #include <netinet/in.h> 11 #ifndef IPPROTO_DCCP 12 #define IPPROTO_DCCP 33 13 #endif 14 #ifndef IPPROTO_SCTP 15 #define IPPROTO_SCTP 132 16 #endif 17 18 #include <sepol/policydb/avtab.h> 19 #include <sepol/policydb/conditional.h> 20 #include <sepol/policydb/flask.h> 21 #include <sepol/policydb/hashtab.h> 22 #include <sepol/policydb/polcaps.h> 23 #include <sepol/policydb/policydb.h> 24 #include <sepol/policydb/services.h> 25 #include <sepol/policydb/util.h> 26 27 #include "kernel_to_common.h" 28 29 30 static char *cond_expr_to_str(struct policydb *pdb, struct cond_expr *expr) 31 { 32 struct cond_expr *curr; 33 struct strs *stack; 34 char *new_val; 35 char *str = NULL; 36 int rc; 37 38 rc = strs_stack_init(&stack); 39 if (rc != 0) { 40 goto exit; 41 } 42 43 for (curr = expr; curr != NULL; curr = curr->next) { 44 if (curr->expr_type == COND_BOOL) { 45 char *val1 = pdb->p_bool_val_to_name[curr->bool - 1]; 46 new_val = create_str("%s", 1, val1); 47 } else { 48 const char *op; 49 uint32_t num_params; 50 char *val1 = NULL; 51 char *val2 = NULL; 52 53 switch(curr->expr_type) { 54 case COND_NOT: op = "!"; num_params = 1; break; 55 case COND_OR: op = "||"; num_params = 2; break; 56 case COND_AND: op = "&&"; num_params = 2; break; 57 case COND_XOR: op = "^"; num_params = 2; break; 58 case COND_EQ: op = "=="; num_params = 2; break; 59 case COND_NEQ: op = "!="; num_params = 2; break; 60 default: 61 sepol_log_err("Unknown conditional operator: %i", curr->expr_type); 62 goto exit; 63 } 64 65 if (num_params == 2) { 66 val2 = strs_stack_pop(stack); 67 if (!val2) { 68 sepol_log_err("Invalid conditional expression"); 69 goto exit; 70 } 71 } 72 val1 = strs_stack_pop(stack); 73 if (!val1) { 74 sepol_log_err("Invalid conditional expression"); 75 free(val2); 76 goto exit; 77 } 78 if (num_params == 2) { 79 new_val = create_str("(%s %s %s)", 3, val1, op, val2); 80 free(val2); 81 } else { 82 new_val = create_str("%s %s", 2, op, val1); 83 } 84 free(val1); 85 } 86 if (!new_val) { 87 sepol_log_err("Invalid conditional expression"); 88 goto exit; 89 } 90 rc = strs_stack_push(stack, new_val); 91 if (rc != 0) { 92 sepol_log_err("Out of memory"); 93 goto exit; 94 } 95 } 96 97 new_val = strs_stack_pop(stack); 98 if (!new_val || !strs_stack_empty(stack)) { 99 sepol_log_err("Invalid conditional expression"); 100 goto exit; 101 } 102 103 str = new_val; 104 105 strs_stack_destroy(&stack); 106 return str; 107 108 exit: 109 while ((new_val = strs_stack_pop(stack)) != NULL) { 110 free(new_val); 111 } 112 strs_stack_destroy(&stack); 113 114 return NULL; 115 } 116 117 static char *constraint_expr_to_str(struct policydb *pdb, struct constraint_expr *expr, int *use_mls) 118 { 119 struct constraint_expr *curr; 120 struct strs *stack = NULL; 121 char *new_val = NULL; 122 const char *op; 123 char *str = NULL; 124 int rc; 125 126 *use_mls = 0; 127 128 rc = strs_stack_init(&stack); 129 if (rc != 0) { 130 goto exit; 131 } 132 133 for (curr = expr; curr; curr = curr->next) { 134 if (curr->expr_type == CEXPR_ATTR || curr->expr_type == CEXPR_NAMES) { 135 const char *attr1 = NULL; 136 const char *attr2 = NULL; 137 138 switch (curr->op) { 139 case CEXPR_EQ: op = "=="; break; 140 case CEXPR_NEQ: op = "!="; break; 141 case CEXPR_DOM: op = "dom"; break; 142 case CEXPR_DOMBY: op = "domby"; break; 143 case CEXPR_INCOMP: op = "incomp"; break; 144 default: 145 sepol_log_err("Unknown constraint operator: %i", curr->op); 146 goto exit; 147 } 148 149 switch (curr->attr) { 150 case CEXPR_USER: attr1 ="u1"; attr2 ="u2"; break; 151 case CEXPR_USER | CEXPR_TARGET: attr1 ="u2"; attr2 =""; break; 152 case CEXPR_USER | CEXPR_XTARGET: attr1 ="u3"; attr2 =""; break; 153 case CEXPR_ROLE: attr1 ="r1"; attr2 ="r2"; break; 154 case CEXPR_ROLE | CEXPR_TARGET: attr1 ="r2"; attr2 =""; break; 155 case CEXPR_ROLE | CEXPR_XTARGET: attr1 ="r3"; attr2 =""; break; 156 case CEXPR_TYPE: attr1 ="t1"; attr2 ="t2"; break; 157 case CEXPR_TYPE | CEXPR_TARGET: attr1 ="t2"; attr2 =""; break; 158 case CEXPR_TYPE | CEXPR_XTARGET: attr1 ="t3"; attr2 =""; break; 159 case CEXPR_L1L2: attr1 ="l1"; attr2 ="l2"; break; 160 case CEXPR_L1H2: attr1 ="l1"; attr2 ="h2"; break; 161 case CEXPR_H1L2: attr1 ="h1"; attr2 ="l2"; break; 162 case CEXPR_H1H2: attr1 ="h1"; attr2 ="h2"; break; 163 case CEXPR_L1H1: attr1 ="l1"; attr2 ="h1"; break; 164 case CEXPR_L2H2: attr1 ="l2"; attr2 ="h2"; break; 165 default: 166 sepol_log_err("Unknown constraint attribute: %i", curr->attr); 167 goto exit; 168 } 169 170 if (curr->attr >= CEXPR_XTARGET) { 171 *use_mls = 1; 172 } 173 174 if (curr->expr_type == CEXPR_ATTR) { 175 new_val = create_str("%s %s %s", 3, attr1, op, attr2); 176 } else { 177 char *names = NULL; 178 if (curr->attr & CEXPR_TYPE) { 179 struct type_set *ts = curr->type_names; 180 names = ebitmap_to_str(&ts->types, pdb->p_type_val_to_name, 1); 181 } else if (curr->attr & CEXPR_USER) { 182 names = ebitmap_to_str(&curr->names, pdb->p_user_val_to_name, 1); 183 } else if (curr->attr & CEXPR_ROLE) { 184 names = ebitmap_to_str(&curr->names, pdb->p_role_val_to_name, 1); 185 } 186 if (!names) { 187 goto exit; 188 } 189 new_val = create_str("%s %s %s", 3, attr1, op, names); 190 free(names); 191 } 192 } else { 193 uint32_t num_params; 194 char *val1 = NULL; 195 char *val2 = NULL; 196 197 switch (curr->expr_type) { 198 case CEXPR_NOT: op = "not"; num_params = 1; break; 199 case CEXPR_AND: op = "and"; num_params = 2; break; 200 case CEXPR_OR: op = "or"; num_params = 2; break; 201 default: 202 sepol_log_err("Unknown constraint expression type: %i", curr->expr_type); 203 goto exit; 204 } 205 206 if (num_params == 2) { 207 val2 = strs_stack_pop(stack); 208 if (!val2) { 209 sepol_log_err("Invalid constraint expression"); 210 goto exit; 211 } 212 } 213 val1 = strs_stack_pop(stack); 214 if (!val1) { 215 sepol_log_err("Invalid constraint expression"); 216 goto exit; 217 } 218 219 if (num_params == 2) { 220 new_val = create_str("(%s %s %s)", 3, val1, op, val2); 221 free(val2); 222 } else { 223 new_val = create_str("%s (%s)", 2, op, val1); 224 } 225 free(val1); 226 } 227 if (!new_val) { 228 goto exit; 229 } 230 rc = strs_stack_push(stack, new_val); 231 if (rc != 0) { 232 sepol_log_err("Out of memory"); 233 goto exit; 234 } 235 } 236 237 new_val = strs_stack_pop(stack); 238 if (!new_val || !strs_stack_empty(stack)) { 239 sepol_log_err("Invalid constraint expression"); 240 goto exit; 241 } 242 243 str = new_val; 244 245 strs_stack_destroy(&stack); 246 247 return str; 248 249 exit: 250 while ((new_val = strs_stack_pop(stack)) != NULL) { 251 free(new_val); 252 } 253 strs_stack_destroy(&stack); 254 255 return NULL; 256 } 257 258 static int class_constraint_rules_to_strs(struct policydb *pdb, char *classkey, 259 class_datum_t *class, 260 struct constraint_node *constraint_rules, 261 struct strs *mls_list, 262 struct strs *non_mls_list) 263 { 264 struct constraint_node *curr; 265 struct strs *strs; 266 const char *format_str, *flavor; 267 char *perms, *expr; 268 int is_mls; 269 int rc = 0; 270 271 for (curr = constraint_rules; curr != NULL; curr = curr->next) { 272 expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); 273 if (!expr) { 274 rc = -1; 275 goto exit; 276 } 277 278 perms = sepol_av_to_string(pdb, class->s.value, curr->permissions); 279 if (strchr(perms, ' ')) { 280 format_str = "%s %s { %s } %s;"; 281 } else { 282 format_str = "%s %s %s %s"; 283 } 284 if (is_mls) { 285 flavor = "mlsconstrain"; 286 strs = mls_list; 287 } else { 288 flavor = "constrain"; 289 strs = non_mls_list; 290 } 291 292 rc = strs_create_and_add(strs, format_str, 4, 293 flavor, classkey, perms+1, expr); 294 free(expr); 295 if (rc != 0) { 296 goto exit; 297 } 298 } 299 300 return 0; 301 exit: 302 sepol_log_err("Error gathering constraint rules\n"); 303 return rc; 304 } 305 306 static int class_validatetrans_rules_to_strs(struct policydb *pdb, char *classkey, 307 struct constraint_node *validatetrans_rules, 308 struct strs *mls_list, 309 struct strs *non_mls_list) 310 { 311 struct constraint_node *curr; 312 struct strs *strs; 313 const char *flavor; 314 char *expr; 315 int is_mls; 316 int rc = 0; 317 318 for (curr = validatetrans_rules; curr != NULL; curr = curr->next) { 319 expr = constraint_expr_to_str(pdb, curr->expr, &is_mls); 320 if (!expr) { 321 rc = -1; 322 goto exit; 323 } 324 325 if (is_mls) { 326 flavor = "mlsvalidatetrans"; 327 strs = mls_list; 328 } else { 329 flavor = "validatetrans"; 330 strs = non_mls_list; 331 } 332 333 rc = strs_create_and_add(strs, "%s %s %s;", 3, flavor, classkey, expr); 334 free(expr); 335 if (rc != 0) { 336 goto exit; 337 } 338 } 339 340 exit: 341 return rc; 342 } 343 344 static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, struct strs *non_mls_strs) 345 { 346 class_datum_t *class; 347 char *name; 348 unsigned i; 349 int rc = 0; 350 351 for (i=0; i < pdb->p_classes.nprim; i++) { 352 class = pdb->class_val_to_struct[i]; 353 if (class->constraints) { 354 name = pdb->p_class_val_to_name[i]; 355 rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs); 356 if (rc != 0) { 357 goto exit; 358 } 359 } 360 } 361 362 strs_sort(mls_strs); 363 strs_sort(non_mls_strs); 364 365 exit: 366 return rc; 367 } 368 369 static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_strs, struct strs *non_mls_strs) 370 { 371 class_datum_t *class; 372 char *name; 373 unsigned i; 374 int rc = 0; 375 376 for (i=0; i < pdb->p_classes.nprim; i++) { 377 class = pdb->class_val_to_struct[i]; 378 if (class->validatetrans) { 379 name = pdb->p_class_val_to_name[i]; 380 rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs); 381 if (rc != 0) { 382 goto exit; 383 } 384 } 385 } 386 387 strs_sort(mls_strs); 388 strs_sort(non_mls_strs); 389 390 exit: 391 return rc; 392 } 393 394 static int write_handle_unknown_to_conf(FILE *out, struct policydb *pdb) 395 { 396 const char *action; 397 398 switch (pdb->handle_unknown) { 399 case SEPOL_DENY_UNKNOWN: 400 action = "deny"; 401 break; 402 case SEPOL_REJECT_UNKNOWN: 403 action = "reject"; 404 break; 405 case SEPOL_ALLOW_UNKNOWN: 406 action = "allow"; 407 break; 408 default: 409 sepol_log_err("Unknown value for handle-unknown: %i", pdb->handle_unknown); 410 return -1; 411 } 412 413 sepol_printf(out, "# handle_unknown %s\n", action); 414 415 return 0; 416 } 417 418 static int write_class_decl_rules_to_conf(FILE *out, struct policydb *pdb) 419 { 420 char *name; 421 unsigned i; 422 423 for (i=0; i < pdb->p_classes.nprim; i++) { 424 name = pdb->p_class_val_to_name[i]; 425 sepol_printf(out, "class %s\n", name); 426 } 427 428 return 0; 429 } 430 431 static int write_sids_to_conf(FILE *out, const char *const *sid_to_str, 432 unsigned num_sids, struct ocontext *isids) 433 { 434 struct ocontext *isid; 435 struct strs *strs; 436 char *sid; 437 char unknown[18]; 438 unsigned i; 439 int rc; 440 441 rc = strs_init(&strs, num_sids+1); 442 if (rc != 0) { 443 goto exit; 444 } 445 446 for (isid = isids; isid != NULL; isid = isid->next) { 447 i = isid->sid[0]; 448 if (i < num_sids) { 449 sid = (char *)sid_to_str[i]; 450 } else { 451 snprintf(unknown, 18, "%s%u", "UNKNOWN", i); 452 sid = strdup(unknown); 453 } 454 rc = strs_add_at_index(strs, sid, i); 455 if (rc != 0) { 456 goto exit; 457 } 458 } 459 460 for (i=0; i<strs_num_items(strs); i++) { 461 sid = strs_read_at_index(strs, i); 462 if (!sid) { 463 continue; 464 } 465 sepol_printf(out, "sid %s\n", sid); 466 } 467 468 exit: 469 for (i=num_sids; i<strs_num_items(strs); i++) { 470 sid = strs_read_at_index(strs, i); 471 free(sid); 472 } 473 strs_destroy(&strs); 474 if (rc != 0) { 475 sepol_log_err("Error writing sid rules to policy.conf\n"); 476 } 477 478 return rc; 479 } 480 481 static int write_sid_decl_rules_to_conf(FILE *out, struct policydb *pdb) 482 { 483 int rc = 0; 484 485 if (pdb->target_platform == SEPOL_TARGET_SELINUX) { 486 rc = write_sids_to_conf(out, selinux_sid_to_str, SELINUX_SID_SZ, 487 pdb->ocontexts[0]); 488 } else if (pdb->target_platform == SEPOL_TARGET_XEN) { 489 rc = write_sids_to_conf(out, xen_sid_to_str, XEN_SID_SZ, 490 pdb->ocontexts[0]); 491 } else { 492 sepol_log_err("Unknown target platform: %i", pdb->target_platform); 493 rc = -1; 494 } 495 496 return rc; 497 } 498 static char *class_or_common_perms_to_str(symtab_t *permtab) 499 { 500 struct strs *strs; 501 char *perms = NULL; 502 int rc = 0; 503 504 rc = strs_init(&strs, permtab->nprim); 505 if (rc != 0) { 506 goto exit; 507 } 508 509 rc = hashtab_map(permtab->table, hashtab_ordered_to_strs, strs); 510 if (rc != 0) { 511 goto exit; 512 } 513 514 if (strs_num_items(strs) > 0) { 515 perms = strs_to_str(strs); 516 } 517 518 exit: 519 strs_destroy(&strs); 520 521 return perms; 522 } 523 524 static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb) 525 { 526 class_datum_t *class; 527 common_datum_t *common; 528 int *used; 529 char *name, *perms; 530 unsigned i; 531 int rc = 0; 532 533 /* common */ 534 used = calloc(pdb->p_commons.nprim, sizeof(*used)); 535 if (!used) { 536 sepol_log_err("Out of memory"); 537 rc = -1; 538 goto exit; 539 } 540 for (i=0; i < pdb->p_classes.nprim; i++) { 541 class = pdb->class_val_to_struct[i]; 542 name = class->comkey; 543 if (!name) continue; 544 common = hashtab_search(pdb->p_commons.table, name); 545 if (!common) { 546 rc = -1; 547 free(used); 548 goto exit; 549 } 550 /* Only write common rule once */ 551 if (!used[common->s.value-1]) { 552 perms = class_or_common_perms_to_str(&common->permissions); 553 if (!perms) { 554 rc = -1; 555 free(used); 556 goto exit; 557 } 558 sepol_printf(out, "common %s { %s }\n", name, perms); 559 free(perms); 560 used[common->s.value-1] = 1; 561 } 562 } 563 free(used); 564 565 /* class */ 566 for (i=0; i < pdb->p_classes.nprim; i++) { 567 class = pdb->class_val_to_struct[i]; 568 name = pdb->p_class_val_to_name[i]; 569 sepol_printf(out, "class %s", name); 570 if (class->comkey) { 571 sepol_printf(out, " inherits %s", class->comkey); 572 } 573 perms = class_or_common_perms_to_str(&class->permissions); 574 if (perms) { 575 sepol_printf(out, " { %s }", perms); 576 free(perms); 577 } 578 sepol_printf(out, "\n"); 579 } 580 581 exit: 582 if (rc != 0) { 583 sepol_log_err("Error writing class rules to policy.conf\n"); 584 } 585 586 return rc; 587 } 588 589 static int write_default_user_to_conf(FILE *out, char *class_name, class_datum_t *class) 590 { 591 const char *dft; 592 593 switch (class->default_user) { 594 case DEFAULT_SOURCE: 595 dft = "source"; 596 break; 597 case DEFAULT_TARGET: 598 dft = "target"; 599 break; 600 default: 601 sepol_log_err("Unknown default role value: %i", class->default_user); 602 return -1; 603 } 604 sepol_printf(out, "default_user { %s } %s;\n", class_name, dft); 605 606 return 0; 607 } 608 609 static int write_default_role_to_conf(FILE *out, char *class_name, class_datum_t *class) 610 { 611 const char *dft; 612 613 switch (class->default_role) { 614 case DEFAULT_SOURCE: 615 dft = "source"; 616 break; 617 case DEFAULT_TARGET: 618 dft = "target"; 619 break; 620 default: 621 sepol_log_err("Unknown default role value: %i", class->default_role); 622 return -1; 623 } 624 sepol_printf(out, "default_role { %s } %s;\n", class_name, dft); 625 626 return 0; 627 } 628 629 static int write_default_type_to_conf(FILE *out, char *class_name, class_datum_t *class) 630 { 631 const char *dft; 632 633 switch (class->default_type) { 634 case DEFAULT_SOURCE: 635 dft = "source"; 636 break; 637 case DEFAULT_TARGET: 638 dft = "target"; 639 break; 640 default: 641 sepol_log_err("Unknown default type value: %i", class->default_type); 642 return -1; 643 } 644 sepol_printf(out, "default_type { %s } %s;\n", class_name, dft); 645 646 return 0; 647 } 648 649 static int write_default_range_to_conf(FILE *out, char *class_name, class_datum_t *class) 650 { 651 const char *dft; 652 653 switch (class->default_range) { 654 case DEFAULT_SOURCE_LOW: 655 dft = "source low"; 656 break; 657 case DEFAULT_SOURCE_HIGH: 658 dft = "source high"; 659 break; 660 case DEFAULT_SOURCE_LOW_HIGH: 661 dft = "source low-high"; 662 break; 663 case DEFAULT_TARGET_LOW: 664 dft = "target low"; 665 break; 666 case DEFAULT_TARGET_HIGH: 667 dft = "target high"; 668 break; 669 case DEFAULT_TARGET_LOW_HIGH: 670 dft = "target low-high"; 671 break; 672 default: 673 sepol_log_err("Unknown default type value: %i", class->default_range); 674 return -1; 675 } 676 sepol_printf(out, "default_range { %s } %s;\n", class_name, dft); 677 678 return 0; 679 } 680 681 static int write_default_rules_to_conf(FILE *out, struct policydb *pdb) 682 { 683 class_datum_t *class; 684 unsigned i; 685 int rc = 0; 686 687 /* default_user */ 688 for (i=0; i < pdb->p_classes.nprim; i++) { 689 class = pdb->class_val_to_struct[i]; 690 if (class->default_user != 0) { 691 rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class); 692 if (rc != 0) { 693 goto exit; 694 } 695 } 696 } 697 698 /* default_role */ 699 for (i=0; i < pdb->p_classes.nprim; i++) { 700 class = pdb->class_val_to_struct[i]; 701 if (class->default_role != 0) { 702 rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class); 703 if (rc != 0) { 704 goto exit; 705 } 706 } 707 } 708 709 /* default_type */ 710 for (i=0; i < pdb->p_classes.nprim; i++) { 711 class = pdb->class_val_to_struct[i]; 712 if (class->default_type != 0) { 713 rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class); 714 if (rc != 0) { 715 goto exit; 716 } 717 } 718 } 719 720 if (!pdb->mls) { 721 return 0; 722 } 723 724 /* default_range */ 725 for (i=0; i < pdb->p_classes.nprim; i++) { 726 class = pdb->class_val_to_struct[i]; 727 if (class->default_range != 0) { 728 rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class); 729 if (rc != 0) { 730 goto exit; 731 } 732 } 733 } 734 735 exit: 736 if (rc != 0) { 737 sepol_log_err("Error writing default rules to policy.conf\n"); 738 } 739 740 return rc; 741 } 742 743 static int map_sensitivity_aliases_to_strs(char *key, void *data, void *args) 744 { 745 level_datum_t *sens = data; 746 struct strs *strs = args; 747 int rc = 0; 748 749 if (sens->isalias) { 750 rc = strs_add(strs, key); 751 } 752 753 return rc; 754 } 755 756 static int write_sensitivity_rules_to_conf(FILE *out, struct policydb *pdb) 757 { 758 level_datum_t *level; 759 struct strs *strs; 760 char **sens_alias_map = NULL; 761 char *name, *prev, *alias; 762 unsigned i, j, num; 763 int rc = 0; 764 765 rc = strs_init(&strs, pdb->p_levels.nprim); 766 if (rc != 0) { 767 goto exit; 768 } 769 770 rc = hashtab_map(pdb->p_levels.table, map_sensitivity_aliases_to_strs, strs); 771 if (rc != 0) { 772 goto exit; 773 } 774 775 num = strs_num_items(strs); 776 777 if (num > 0) { 778 sens_alias_map = calloc(sizeof(*sens_alias_map), pdb->p_levels.nprim); 779 if (!sens_alias_map) { 780 rc = -1; 781 goto exit; 782 } 783 784 /* map aliases to sensitivities */ 785 for (i=0; i < num; i++) { 786 name = strs_read_at_index(strs, i); 787 level = hashtab_search(pdb->p_levels.table, name); 788 if (!level) { 789 rc = -1; 790 goto exit; 791 } 792 j = level->level->sens - 1; 793 if (!sens_alias_map[j]) { 794 sens_alias_map[j] = strdup(name); 795 } else { 796 alias = sens_alias_map[j]; 797 sens_alias_map[j] = create_str("%s %s", 2, alias, name); 798 free(alias); 799 if (!sens_alias_map[j]) { 800 rc = -1; 801 goto exit; 802 } 803 } 804 } 805 } 806 807 /* sensitivities */ 808 for (i=0; i < pdb->p_levels.nprim; i++) { 809 name = pdb->p_sens_val_to_name[i]; 810 if (!name) continue; 811 level = hashtab_search(pdb->p_levels.table, name); 812 if (!level) { 813 rc = -1; 814 goto exit; 815 } 816 if (level->isalias) continue; 817 818 if (sens_alias_map && sens_alias_map[i]) { 819 alias = sens_alias_map[i]; 820 if (strchr(alias, ' ')) { 821 sepol_printf(out, "sensitivity %s alias { %s };\n", name, alias); 822 } else { 823 sepol_printf(out, "sensitivity %s alias %s;\n", name, alias); 824 } 825 } else { 826 sepol_printf(out, "sensitivity %s;\n", name); 827 } 828 } 829 830 /* dominance */ 831 sepol_printf(out, "dominance { "); 832 prev = NULL; 833 for (i=0; i < pdb->p_levels.nprim; i++) { 834 name = pdb->p_sens_val_to_name[i]; 835 if (!name) continue; 836 level = hashtab_search(pdb->p_levels.table, name); 837 if (!level) { 838 rc = -1; 839 goto exit; 840 } 841 if (level->isalias) continue; 842 843 if (prev) { 844 sepol_printf(out, "%s ", prev); 845 } 846 prev = name; 847 } 848 if (prev) { 849 sepol_printf(out, "%s", prev); 850 } 851 sepol_printf(out, " }\n"); 852 853 exit: 854 if (sens_alias_map) { 855 for (i=0; i < pdb->p_levels.nprim; i++) { 856 free(sens_alias_map[i]); 857 } 858 free(sens_alias_map); 859 } 860 861 strs_destroy(&strs); 862 863 if (rc != 0) { 864 sepol_log_err("Error writing sensitivity rules to CIL\n"); 865 } 866 867 return rc; 868 } 869 870 static int map_category_aliases_to_strs(char *key, void *data, void *args) 871 { 872 cat_datum_t *cat = data; 873 struct strs *strs = args; 874 int rc = 0; 875 876 if (cat->isalias) { 877 rc = strs_add(strs, key); 878 } 879 880 return rc; 881 } 882 883 static int write_category_rules_to_conf(FILE *out, struct policydb *pdb) 884 { 885 cat_datum_t *cat; 886 struct strs *strs; 887 char **cat_alias_map = NULL; 888 char *name, *alias; 889 unsigned i, j, num; 890 int rc = 0; 891 892 rc = strs_init(&strs, pdb->p_levels.nprim); 893 if (rc != 0) { 894 goto exit; 895 } 896 897 rc = hashtab_map(pdb->p_cats.table, map_category_aliases_to_strs, strs); 898 if (rc != 0) { 899 goto exit; 900 } 901 902 num = strs_num_items(strs); 903 904 if (num > 0) { 905 cat_alias_map = calloc(sizeof(*cat_alias_map), pdb->p_cats.nprim); 906 if (!cat_alias_map) { 907 rc = -1; 908 goto exit; 909 } 910 911 /* map aliases to categories */ 912 for (i=0; i < num; i++) { 913 name = strs_read_at_index(strs, i); 914 cat = hashtab_search(pdb->p_cats.table, name); 915 if (!cat) { 916 rc = -1; 917 goto exit; 918 } 919 j = cat->s.value - 1; 920 if (!cat_alias_map[j]) { 921 cat_alias_map[j] = strdup(name); 922 } else { 923 alias = cat_alias_map[j]; 924 cat_alias_map[j] = create_str("%s %s", 2, alias, name); 925 free(alias); 926 if (!cat_alias_map[j]) { 927 rc = -1; 928 goto exit; 929 } 930 } 931 } 932 } 933 934 /* categories */ 935 for (i=0; i < pdb->p_cats.nprim; i++) { 936 name = pdb->p_cat_val_to_name[i]; 937 if (!name) continue; 938 cat = hashtab_search(pdb->p_cats.table, name); 939 if (!cat) { 940 rc = -1; 941 goto exit; 942 } 943 if (cat->isalias) continue; 944 945 if (cat_alias_map && cat_alias_map[i]) { 946 alias = cat_alias_map[i]; 947 if (strchr(alias, ' ')) { 948 sepol_printf(out, "category %s alias { %s };\n", name, alias); 949 } else { 950 sepol_printf(out, "category %s alias %s;\n", name, alias); 951 } 952 } else { 953 sepol_printf(out, "category %s;\n", name); 954 } 955 } 956 957 exit: 958 if (cat_alias_map) { 959 for (i=0; i < pdb->p_cats.nprim; i++) { 960 free(cat_alias_map[i]); 961 } 962 free(cat_alias_map); 963 } 964 965 strs_destroy(&strs); 966 967 if (rc != 0) { 968 sepol_log_err("Error writing category rules to policy.conf\n"); 969 } 970 971 return rc; 972 } 973 974 static size_t cats_ebitmap_len(struct ebitmap *cats, char **val_to_name) 975 { 976 struct ebitmap_node *node; 977 uint32_t i, start, range; 978 size_t len = 0; 979 980 range = 0; 981 ebitmap_for_each_bit(cats, node, i) { 982 if (!ebitmap_get_bit(cats, i)) 983 continue; 984 985 if (range == 0) 986 start = i; 987 988 range++; 989 990 if (ebitmap_get_bit(cats, i+1)) 991 continue; 992 993 len += strlen(val_to_name[start]) + 1; 994 if (range > 1) { 995 len += strlen(val_to_name[i]) + 1; 996 } 997 998 range = 0; 999 } 1000 1001 return len; 1002 } 1003 1004 static char *cats_ebitmap_to_str(struct ebitmap *cats, char **val_to_name) 1005 { 1006 struct ebitmap_node *node; 1007 uint32_t i, start, range, first; 1008 char *catsbuf, *p; 1009 const char *fmt; 1010 char sep; 1011 int len, remaining; 1012 1013 remaining = (int)cats_ebitmap_len(cats, val_to_name); 1014 catsbuf = malloc(remaining); 1015 if (!catsbuf) { 1016 goto exit; 1017 } 1018 1019 p = catsbuf; 1020 1021 first = 1; 1022 range = 0; 1023 ebitmap_for_each_bit(cats, node, i) { 1024 if (!ebitmap_get_bit(cats, i)) 1025 continue; 1026 1027 if (range == 0) 1028 start = i; 1029 1030 range++; 1031 1032 if (ebitmap_get_bit(cats, i+1)) 1033 continue; 1034 1035 if (range > 1) { 1036 sep = (range == 2) ? ',' : '.'; 1037 fmt = first ? "%s%c%s" : ",%s%c%s"; 1038 len = snprintf(p, remaining, fmt, 1039 val_to_name[start], sep, val_to_name[i]); 1040 } else { 1041 fmt = first ? "%s" : ",%s"; 1042 len = snprintf(p, remaining, fmt, val_to_name[start]); 1043 1044 } 1045 if (len < 0 || len >= remaining) { 1046 goto exit; 1047 } 1048 p += len; 1049 remaining -= len; 1050 first = 0; 1051 range = 0; 1052 } 1053 1054 *p = '\0'; 1055 1056 return catsbuf; 1057 1058 exit: 1059 free(catsbuf); 1060 return NULL; 1061 } 1062 1063 static int write_level_rules_to_conf(FILE *out, struct policydb *pdb) 1064 { 1065 level_datum_t *level; 1066 char *name, *cats; 1067 unsigned i; 1068 int rc = 0; 1069 1070 for (i=0; i < pdb->p_levels.nprim; i++) { 1071 name = pdb->p_sens_val_to_name[i]; 1072 if (!name) continue; 1073 level = hashtab_search(pdb->p_levels.table, name); 1074 if (!level) { 1075 rc = -1; 1076 goto exit; 1077 } 1078 if (level->isalias) continue; 1079 1080 if (ebitmap_cardinality(&level->level->cat) > 0) { 1081 cats = cats_ebitmap_to_str(&level->level->cat, pdb->p_cat_val_to_name); 1082 sepol_printf(out, "level %s:%s;\n", name, cats); 1083 free(cats); 1084 } else { 1085 sepol_printf(out, "level %s;\n", name); 1086 } 1087 } 1088 1089 exit: 1090 if (rc != 0) { 1091 sepol_log_err("Error writing level rules to policy.conf\n"); 1092 } 1093 1094 return rc; 1095 } 1096 1097 static int write_mls_rules_to_conf(FILE *out, struct policydb *pdb) 1098 { 1099 int rc = 0; 1100 1101 if (!pdb->mls) { 1102 return 0; 1103 } 1104 1105 rc = write_sensitivity_rules_to_conf(out, pdb); 1106 if (rc != 0) { 1107 goto exit; 1108 } 1109 1110 rc = write_category_rules_to_conf(out, pdb); 1111 if (rc != 0) { 1112 goto exit; 1113 } 1114 1115 rc = write_level_rules_to_conf(out, pdb); 1116 if (rc != 0) { 1117 goto exit; 1118 } 1119 1120 exit: 1121 if (rc != 0) { 1122 sepol_log_err("Error writing mls rules to policy.conf\n"); 1123 } 1124 1125 return rc; 1126 } 1127 1128 static int write_polcap_rules_to_conf(FILE *out, struct policydb *pdb) 1129 { 1130 struct strs *strs; 1131 struct ebitmap_node *node; 1132 const char *name; 1133 uint32_t i; 1134 int rc = 0; 1135 1136 rc = strs_init(&strs, 32); 1137 if (rc != 0) { 1138 goto exit; 1139 } 1140 1141 ebitmap_for_each_bit(&pdb->policycaps, node, i) { 1142 if (!ebitmap_get_bit(&pdb->policycaps, i)) continue; 1143 1144 name = sepol_polcap_getname(i); 1145 if (name == NULL) { 1146 sepol_log_err("Unknown policy capability id: %i", i); 1147 rc = -1; 1148 goto exit; 1149 } 1150 1151 rc = strs_create_and_add(strs, "policycap %s;", 1, name); 1152 if (rc != 0) { 1153 goto exit; 1154 } 1155 } 1156 1157 strs_sort(strs); 1158 strs_write_each(strs, out); 1159 1160 exit: 1161 strs_free_all(strs); 1162 strs_destroy(&strs); 1163 1164 if (rc != 0) { 1165 sepol_log_err("Error writing polcap rules to policy.conf\n"); 1166 } 1167 1168 return rc; 1169 } 1170 1171 static int write_type_attributes_to_conf(FILE *out, struct policydb *pdb) 1172 { 1173 type_datum_t *type; 1174 char *name; 1175 struct strs *strs; 1176 unsigned i, num; 1177 int rc = 0; 1178 1179 rc = strs_init(&strs, pdb->p_types.nprim); 1180 if (rc != 0) { 1181 goto exit; 1182 } 1183 1184 for (i=0; i < pdb->p_types.nprim; i++) { 1185 type = pdb->type_val_to_struct[i]; 1186 if (type->flavor == TYPE_ATTRIB) { 1187 rc = strs_add(strs, pdb->p_type_val_to_name[i]); 1188 if (rc != 0) { 1189 goto exit; 1190 } 1191 } 1192 } 1193 1194 strs_sort(strs); 1195 1196 num = strs_num_items(strs); 1197 for (i = 0; i < num; i++) { 1198 name = strs_read_at_index(strs, i); 1199 if (!name) { 1200 rc = -1; 1201 goto exit; 1202 } 1203 sepol_printf(out, "attribute %s;\n", name); 1204 } 1205 1206 exit: 1207 strs_destroy(&strs); 1208 1209 if (rc != 0) { 1210 sepol_log_err("Error writing typeattribute rules to policy.conf\n"); 1211 } 1212 1213 return rc; 1214 } 1215 1216 static int write_role_attributes_to_conf(FILE *out, struct policydb *pdb) 1217 { 1218 role_datum_t *role; 1219 char *name; 1220 struct strs *strs; 1221 unsigned i, num; 1222 int rc = 0; 1223 1224 rc = strs_init(&strs, pdb->p_roles.nprim); 1225 if (rc != 0) { 1226 goto exit; 1227 } 1228 1229 for (i=0; i < pdb->p_roles.nprim; i++) { 1230 role = pdb->role_val_to_struct[i]; 1231 if (role && role->flavor == ROLE_ATTRIB) { 1232 rc = strs_add(strs, pdb->p_role_val_to_name[i]); 1233 if (rc != 0) { 1234 goto exit; 1235 } 1236 } 1237 } 1238 1239 strs_sort(strs); 1240 1241 num = strs_num_items(strs); 1242 for (i=0; i<num; i++) { 1243 name = strs_read_at_index(strs, i); 1244 if (!name) { 1245 rc = -1; 1246 goto exit; 1247 } 1248 sepol_printf(out, "attribute_role %s;\n", name); 1249 } 1250 1251 exit: 1252 strs_destroy(&strs); 1253 1254 if (rc != 0) { 1255 sepol_log_err("Error writing roleattribute rules to policy.conf\n"); 1256 } 1257 1258 return rc; 1259 } 1260 1261 static int map_boolean_to_strs(char *key, void *data, void *args) 1262 { 1263 struct strs *strs = (struct strs *)args; 1264 struct cond_bool_datum *boolean = data; 1265 const char *value; 1266 1267 value = boolean->state ? "true" : "false"; 1268 1269 return strs_create_and_add(strs, "bool %s %s;", 2, key, value); 1270 } 1271 1272 static int write_boolean_decl_rules_to_conf(FILE *out, struct policydb *pdb) 1273 { 1274 struct strs *strs; 1275 int rc = 0; 1276 1277 rc = strs_init(&strs, 32); 1278 if (rc != 0) { 1279 goto exit; 1280 } 1281 1282 rc = hashtab_map(pdb->p_bools.table, map_boolean_to_strs, strs); 1283 if (rc != 0) { 1284 goto exit; 1285 } 1286 1287 strs_sort(strs); 1288 strs_write_each(strs, out); 1289 1290 exit: 1291 strs_free_all(strs); 1292 strs_destroy(&strs); 1293 1294 if (rc != 0) { 1295 sepol_log_err("Error writing boolean declarations to policy.conf\n"); 1296 } 1297 1298 return rc; 1299 } 1300 1301 static int write_type_decl_rules_to_conf(FILE *out, struct policydb *pdb) 1302 { 1303 type_datum_t *type; 1304 struct strs *strs; 1305 char *name; 1306 unsigned i, num; 1307 int rc = 0; 1308 1309 rc = strs_init(&strs, pdb->p_types.nprim); 1310 if (rc != 0) { 1311 goto exit; 1312 } 1313 1314 for (i=0; i < pdb->p_types.nprim; i++) { 1315 type = pdb->type_val_to_struct[i]; 1316 if (type->flavor == TYPE_TYPE && type->primary) { 1317 rc = strs_add(strs, pdb->p_type_val_to_name[i]); 1318 if (rc != 0) { 1319 goto exit; 1320 } 1321 } 1322 } 1323 1324 strs_sort(strs); 1325 1326 num = strs_num_items(strs); 1327 for (i=0; i<num; i++) { 1328 name = strs_read_at_index(strs, i); 1329 if (!name) { 1330 rc = -1; 1331 goto exit; 1332 } 1333 sepol_printf(out, "type %s;\n", name); 1334 } 1335 1336 exit: 1337 strs_destroy(&strs); 1338 1339 if (rc != 0) { 1340 sepol_log_err("Error writing type declarations to policy.con\n"); 1341 } 1342 1343 return rc; 1344 } 1345 1346 static int write_type_alias_rules_to_conf(FILE *out, struct policydb *pdb) 1347 { 1348 type_datum_t *alias; 1349 struct strs *strs; 1350 char *name; 1351 char *type; 1352 unsigned i, num; 1353 int rc = 0; 1354 1355 rc = strs_init(&strs, pdb->p_types.nprim); 1356 if (rc != 0) { 1357 goto exit; 1358 } 1359 1360 for (i=0; i < pdb->p_types.nprim; i++) { 1361 alias = pdb->type_val_to_struct[i]; 1362 if (!alias->primary) { 1363 rc = strs_add(strs, pdb->p_type_val_to_name[i]); 1364 if (rc != 0) { 1365 goto exit; 1366 } 1367 } 1368 } 1369 1370 strs_sort(strs); 1371 1372 num = strs_num_items(strs); 1373 1374 for (i=0; i<num; i++) { 1375 name = strs_read_at_index(strs, i); 1376 if (!name) { 1377 rc = -1; 1378 goto exit; 1379 } 1380 alias = hashtab_search(pdb->p_types.table, name); 1381 if (!alias) { 1382 rc = -1; 1383 goto exit; 1384 } 1385 type = pdb->p_type_val_to_name[alias->s.value - 1]; 1386 sepol_printf(out, "typealias %s %s;\n", type, name); 1387 } 1388 1389 exit: 1390 strs_destroy(&strs); 1391 1392 if (rc != 0) { 1393 sepol_log_err("Error writing type alias rules to policy.conf\n"); 1394 } 1395 1396 return rc; 1397 } 1398 1399 static int write_type_bounds_rules_to_conf(FILE *out, struct policydb *pdb) 1400 { 1401 type_datum_t *type; 1402 struct strs *strs; 1403 char *parent; 1404 char *child; 1405 unsigned i, num; 1406 int rc = 0; 1407 1408 rc = strs_init(&strs, pdb->p_types.nprim); 1409 if (rc != 0) { 1410 goto exit; 1411 } 1412 1413 for (i=0; i < pdb->p_types.nprim; i++) { 1414 type = pdb->type_val_to_struct[i]; 1415 if (type->flavor == TYPE_TYPE) { 1416 if (type->bounds > 0) { 1417 rc = strs_add(strs, pdb->p_type_val_to_name[i]); 1418 if (rc != 0) { 1419 goto exit; 1420 } 1421 } 1422 } 1423 } 1424 1425 strs_sort(strs); 1426 1427 num = strs_num_items(strs); 1428 for (i=0; i<num; i++) { 1429 child = strs_read_at_index(strs, i); 1430 if (!child) { 1431 rc = -1; 1432 goto exit; 1433 } 1434 type = hashtab_search(pdb->p_types.table, child); 1435 if (!type) { 1436 rc = -1; 1437 goto exit; 1438 } 1439 parent = pdb->p_type_val_to_name[type->bounds - 1]; 1440 sepol_printf(out, "typebounds %s %s;\n", parent, child); 1441 } 1442 1443 exit: 1444 strs_destroy(&strs); 1445 1446 if (rc != 0) { 1447 sepol_log_err("Error writing type bounds rules to policy.conf\n"); 1448 } 1449 1450 return rc; 1451 } 1452 1453 static char *attr_strs_to_str(struct strs *strs) 1454 { 1455 char *str = NULL; 1456 size_t len = 0; 1457 char *p; 1458 unsigned i; 1459 int rc; 1460 1461 if (strs->num == 0) { 1462 goto exit; 1463 } 1464 1465 /* 2*strs->num - 1 because ", " follows all but last attr (followed by '\0') */ 1466 len = strs_len_items(strs) + 2*strs->num - 1; 1467 str = malloc(len); 1468 if (!str) { 1469 sepol_log_err("Out of memory"); 1470 goto exit; 1471 } 1472 1473 p = str; 1474 for (i=0; i<strs->num; i++) { 1475 if (!strs->list[i]) continue; 1476 len = strlen(strs->list[i]); 1477 rc = snprintf(p, len+1, "%s", strs->list[i]); 1478 if (rc < 0 || rc > (int)len) { 1479 free(str); 1480 str = NULL; 1481 goto exit; 1482 } 1483 p += len; 1484 if (i < strs->num - 1) { 1485 *p++ = ','; 1486 *p++ = ' '; 1487 } 1488 } 1489 1490 *p = '\0'; 1491 1492 exit: 1493 return str; 1494 } 1495 1496 static char *attrmap_to_str(struct ebitmap *map, char **val_to_name) 1497 { 1498 struct strs *strs; 1499 char *str = NULL; 1500 int rc; 1501 1502 rc = strs_init(&strs, 32); 1503 if (rc != 0) { 1504 goto exit; 1505 } 1506 1507 rc = ebitmap_to_strs(map, strs, val_to_name); 1508 if (rc != 0) { 1509 goto exit; 1510 } 1511 1512 strs_sort(strs); 1513 1514 str = attr_strs_to_str(strs); 1515 1516 exit: 1517 strs_destroy(&strs); 1518 1519 return str; 1520 } 1521 1522 static int write_type_attribute_sets_to_conf(FILE *out, struct policydb *pdb) 1523 { 1524 type_datum_t *type; 1525 struct strs *strs; 1526 ebitmap_t attrmap; 1527 char *name, *attrs; 1528 unsigned i; 1529 int rc; 1530 1531 rc = strs_init(&strs, pdb->p_types.nprim); 1532 if (rc != 0) { 1533 goto exit; 1534 } 1535 1536 for (i=0; i < pdb->p_types.nprim; i++) { 1537 type = pdb->type_val_to_struct[i]; 1538 if (type->flavor != TYPE_TYPE || !type->primary) continue; 1539 if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue; 1540 1541 rc = ebitmap_cpy(&attrmap, &pdb->type_attr_map[i]); 1542 if (rc != 0) { 1543 goto exit; 1544 } 1545 rc = ebitmap_set_bit(&attrmap, i, 0); 1546 if (rc != 0) { 1547 ebitmap_destroy(&attrmap); 1548 goto exit; 1549 } 1550 name = pdb->p_type_val_to_name[i]; 1551 attrs = attrmap_to_str(&attrmap, pdb->p_type_val_to_name); 1552 ebitmap_destroy(&attrmap); 1553 if (!attrs) { 1554 rc = -1; 1555 goto exit; 1556 } 1557 1558 rc = strs_create_and_add(strs, "typeattribute %s %s;", 1559 2, name, attrs); 1560 free(attrs); 1561 if (rc != 0) { 1562 goto exit; 1563 } 1564 } 1565 1566 strs_sort(strs); 1567 strs_write_each(strs, out); 1568 1569 exit: 1570 strs_free_all(strs); 1571 strs_destroy(&strs); 1572 1573 if (rc != 0) { 1574 sepol_log_err("Error writing typeattributeset rules to policy.conf\n"); 1575 } 1576 1577 return rc; 1578 } 1579 1580 static int write_type_permissive_rules_to_conf(FILE *out, struct policydb *pdb) 1581 { 1582 struct strs *strs; 1583 char *name; 1584 struct ebitmap_node *node; 1585 unsigned i, num; 1586 int rc = 0; 1587 1588 rc = strs_init(&strs, pdb->p_types.nprim); 1589 if (rc != 0) { 1590 goto exit; 1591 } 1592 1593 ebitmap_for_each_bit(&pdb->permissive_map, node, i) { 1594 if (!ebitmap_get_bit(&pdb->permissive_map, i)) continue; 1595 rc = strs_add(strs, pdb->p_type_val_to_name[i-1]); 1596 if (rc != 0) { 1597 goto exit; 1598 } 1599 } 1600 1601 strs_sort(strs); 1602 1603 num = strs_num_items(strs); 1604 for (i=0; i<num; i++) { 1605 name = strs_read_at_index(strs, i); 1606 if (!name) { 1607 rc = -1; 1608 goto exit; 1609 } 1610 sepol_printf(out, "permissive %s;\n", name); 1611 } 1612 1613 exit: 1614 strs_destroy(&strs); 1615 1616 if (rc != 0) { 1617 sepol_log_err("Error writing typepermissive rules to policy.conf\n"); 1618 } 1619 1620 return rc; 1621 } 1622 1623 static char *avtab_node_to_str(struct policydb *pdb, avtab_key_t *key, avtab_datum_t *datum) 1624 { 1625 uint32_t data = datum->data; 1626 type_datum_t *type; 1627 const char *flavor, *src, *tgt, *class, *perms, *new; 1628 char *rule = NULL; 1629 1630 switch (0xFFF & key->specified) { 1631 case AVTAB_ALLOWED: 1632 flavor = "allow"; 1633 break; 1634 case AVTAB_AUDITALLOW: 1635 flavor = "auditallow"; 1636 break; 1637 case AVTAB_AUDITDENY: 1638 flavor = "dontaudit"; 1639 data = ~data; 1640 break; 1641 case AVTAB_XPERMS_ALLOWED: 1642 flavor = "allowxperm"; 1643 break; 1644 case AVTAB_XPERMS_AUDITALLOW: 1645 flavor = "auditallowxperm"; 1646 break; 1647 case AVTAB_XPERMS_DONTAUDIT: 1648 flavor = "dontauditxperm"; 1649 break; 1650 case AVTAB_TRANSITION: 1651 flavor = "type_transition"; 1652 break; 1653 case AVTAB_MEMBER: 1654 flavor = "type_member"; 1655 break; 1656 case AVTAB_CHANGE: 1657 flavor = "type_change"; 1658 break; 1659 default: 1660 sepol_log_err("Unknown avtab type: %i", key->specified); 1661 goto exit; 1662 } 1663 1664 src = pdb->p_type_val_to_name[key->source_type - 1]; 1665 tgt = pdb->p_type_val_to_name[key->target_type - 1]; 1666 if (key->source_type == key->target_type && !(key->specified & AVTAB_TYPE)) { 1667 type = pdb->type_val_to_struct[key->source_type - 1]; 1668 if (type->flavor != TYPE_ATTRIB) { 1669 tgt = "self"; 1670 } 1671 } 1672 class = pdb->p_class_val_to_name[key->target_class - 1]; 1673 1674 if (key->specified & AVTAB_AV) { 1675 perms = sepol_av_to_string(pdb, key->target_class, data); 1676 if (perms == NULL) { 1677 sepol_log_err("Failed to generate permission string"); 1678 goto exit; 1679 } 1680 rule = create_str("%s %s %s:%s { %s };", 5, 1681 flavor, src, tgt, class, perms+1); 1682 } else if (key->specified & AVTAB_XPERMS) { 1683 perms = sepol_extended_perms_to_string(datum->xperms); 1684 if (perms == NULL) { 1685 sepol_log_err("Failed to generate extended permission string"); 1686 goto exit; 1687 } 1688 1689 rule = create_str("%s %s %s:%s %s;", 5, flavor, src, tgt, class, perms); 1690 } else { 1691 new = pdb->p_type_val_to_name[data - 1]; 1692 1693 rule = create_str("%s %s %s:%s %s;", 5, flavor, src, tgt, class, new); 1694 } 1695 1696 if (!rule) { 1697 goto exit; 1698 } 1699 1700 return rule; 1701 1702 exit: 1703 return NULL; 1704 } 1705 1706 struct map_avtab_args { 1707 struct policydb *pdb; 1708 uint32_t flavor; 1709 struct strs *strs; 1710 }; 1711 1712 static int map_avtab_write_helper(avtab_key_t *key, avtab_datum_t *datum, void *args) 1713 { 1714 struct map_avtab_args *map_args = args; 1715 uint32_t flavor = map_args->flavor; 1716 struct policydb *pdb = map_args->pdb; 1717 struct strs *strs = map_args->strs; 1718 char *rule; 1719 int rc = 0; 1720 1721 if (key->specified & flavor) { 1722 rule = avtab_node_to_str(pdb, key, datum); 1723 if (!rule) { 1724 rc = -1; 1725 goto exit; 1726 } 1727 rc = strs_add(strs, rule); 1728 if (rc != 0) { 1729 free(rule); 1730 goto exit; 1731 } 1732 } 1733 1734 exit: 1735 return rc; 1736 } 1737 1738 static int write_avtab_flavor_to_conf(FILE *out, struct policydb *pdb, uint32_t flavor, int indent) 1739 { 1740 struct map_avtab_args args; 1741 struct strs *strs; 1742 int rc = 0; 1743 1744 rc = strs_init(&strs, 1000); 1745 if (rc != 0) { 1746 goto exit; 1747 } 1748 1749 args.pdb = pdb; 1750 args.flavor = flavor; 1751 args.strs = strs; 1752 1753 rc = avtab_map(&pdb->te_avtab, map_avtab_write_helper, &args); 1754 if (rc != 0) { 1755 goto exit; 1756 } 1757 1758 strs_sort(strs); 1759 strs_write_each_indented(strs, out, indent); 1760 1761 exit: 1762 strs_free_all(strs); 1763 strs_destroy(&strs); 1764 1765 return rc; 1766 } 1767 1768 static int write_avtab_to_conf(FILE *out, struct policydb *pdb, int indent) 1769 { 1770 unsigned i; 1771 int rc = 0; 1772 1773 for (i = 0; i < AVTAB_FLAVORS_SZ; i++) { 1774 rc = write_avtab_flavor_to_conf(out, pdb, avtab_flavors[i], indent); 1775 if (rc != 0) { 1776 goto exit; 1777 } 1778 } 1779 1780 exit: 1781 if (rc != 0) { 1782 sepol_log_err("Error writing avtab rules to policy.conf\n"); 1783 } 1784 1785 return rc; 1786 } 1787 1788 struct map_filename_trans_args { 1789 struct policydb *pdb; 1790 struct strs *strs; 1791 }; 1792 1793 static int map_filename_trans_to_str(hashtab_key_t key, void *data, void *arg) 1794 { 1795 filename_trans_t *ft = (filename_trans_t *)key; 1796 filename_trans_datum_t *datum = data; 1797 struct map_filename_trans_args *map_args = arg; 1798 struct policydb *pdb = map_args->pdb; 1799 struct strs *strs = map_args->strs; 1800 char *src, *tgt, *class, *filename, *new; 1801 1802 src = pdb->p_type_val_to_name[ft->stype - 1]; 1803 tgt = pdb->p_type_val_to_name[ft->ttype - 1]; 1804 class = pdb->p_class_val_to_name[ft->tclass - 1]; 1805 filename = ft->name; 1806 new = pdb->p_type_val_to_name[datum->otype - 1]; 1807 1808 return strs_create_and_add(strs, "type_transition %s %s:%s %s \"%s\";", 5, 1809 src, tgt, class, new, filename); 1810 } 1811 1812 static int write_filename_trans_rules_to_conf(FILE *out, struct policydb *pdb) 1813 { 1814 struct map_filename_trans_args args; 1815 struct strs *strs; 1816 int rc = 0; 1817 1818 rc = strs_init(&strs, 100); 1819 if (rc != 0) { 1820 goto exit; 1821 } 1822 1823 args.pdb = pdb; 1824 args.strs = strs; 1825 1826 rc = hashtab_map(pdb->filename_trans, map_filename_trans_to_str, &args); 1827 if (rc != 0) { 1828 goto exit; 1829 } 1830 1831 strs_sort(strs); 1832 strs_write_each(strs, out); 1833 1834 exit: 1835 strs_free_all(strs); 1836 strs_destroy(&strs); 1837 1838 if (rc != 0) { 1839 sepol_log_err("Error writing filename typetransition rules to policy.conf\n"); 1840 } 1841 1842 return rc; 1843 } 1844 1845 static char *level_to_str(struct policydb *pdb, struct mls_level *level) 1846 { 1847 ebitmap_t *cats = &level->cat; 1848 char *level_str = NULL; 1849 char *sens_str = pdb->p_sens_val_to_name[level->sens - 1]; 1850 char *cats_str; 1851 1852 if (ebitmap_cardinality(cats) > 0) { 1853 cats_str = cats_ebitmap_to_str(cats, pdb->p_cat_val_to_name); 1854 level_str = create_str("%s:%s", 2, sens_str, cats_str); 1855 free(cats_str); 1856 } else { 1857 level_str = create_str("%s", 1, sens_str); 1858 } 1859 1860 return level_str; 1861 } 1862 1863 static char *range_to_str(struct policydb *pdb, mls_range_t *range) 1864 { 1865 char *low = NULL; 1866 char *high = NULL; 1867 char *range_str = NULL; 1868 1869 low = level_to_str(pdb, &range->level[0]); 1870 if (!low) { 1871 goto exit; 1872 } 1873 1874 high = level_to_str(pdb, &range->level[1]); 1875 if (!high) { 1876 goto exit; 1877 } 1878 1879 range_str = create_str("%s - %s", 2, low, high); 1880 1881 exit: 1882 free(low); 1883 free(high); 1884 1885 return range_str; 1886 } 1887 1888 struct map_range_trans_args { 1889 struct policydb *pdb; 1890 struct strs *strs; 1891 }; 1892 1893 static int map_range_trans_to_str(hashtab_key_t key, void *data, void *arg) 1894 { 1895 range_trans_t *rt = (range_trans_t *)key; 1896 mls_range_t *mls_range = data; 1897 struct map_range_trans_args *map_args = arg; 1898 struct policydb *pdb = map_args->pdb; 1899 struct strs *strs = map_args->strs; 1900 char *src, *tgt, *class, *range; 1901 int rc; 1902 1903 src = pdb->p_type_val_to_name[rt->source_type - 1]; 1904 tgt = pdb->p_type_val_to_name[rt->target_type - 1]; 1905 class = pdb->p_class_val_to_name[rt->target_class - 1]; 1906 range = range_to_str(pdb, mls_range); 1907 if (!range) { 1908 rc = -1; 1909 goto exit; 1910 } 1911 1912 rc = strs_create_and_add(strs, "range_transition %s %s:%s %s;", 4, 1913 src, tgt, class, range); 1914 free(range); 1915 if (rc != 0) { 1916 goto exit; 1917 } 1918 1919 exit: 1920 return rc; 1921 } 1922 1923 static int write_range_trans_rules_to_conf(FILE *out, struct policydb *pdb) 1924 { 1925 struct map_range_trans_args args; 1926 struct strs *strs; 1927 int rc = 0; 1928 1929 rc = strs_init(&strs, 100); 1930 if (rc != 0) { 1931 goto exit; 1932 } 1933 1934 args.pdb = pdb; 1935 args.strs = strs; 1936 1937 rc = hashtab_map(pdb->range_tr, map_range_trans_to_str, &args); 1938 if (rc != 0) { 1939 goto exit; 1940 } 1941 1942 strs_sort(strs); 1943 strs_write_each(strs, out); 1944 1945 exit: 1946 strs_free_all(strs); 1947 strs_destroy(&strs); 1948 1949 if (rc != 0) { 1950 sepol_log_err("Error writing range transition rules to policy.conf\n"); 1951 } 1952 1953 return rc; 1954 } 1955 1956 static int write_cond_av_list_to_conf(FILE *out, struct policydb *pdb, cond_av_list_t *cond_list, int indent) 1957 { 1958 cond_av_list_t *cond_av; 1959 avtab_ptr_t node; 1960 uint32_t flavor; 1961 avtab_key_t *key; 1962 avtab_datum_t *datum; 1963 struct strs *strs; 1964 char *rule; 1965 unsigned i; 1966 int rc; 1967 1968 for (i = 0; i < AVTAB_FLAVORS_SZ; i++) { 1969 flavor = avtab_flavors[i]; 1970 rc = strs_init(&strs, 64); 1971 if (rc != 0) { 1972 goto exit; 1973 } 1974 1975 for (cond_av = cond_list; cond_av != NULL; cond_av = cond_av->next) { 1976 node = cond_av->node; 1977 key = &node->key; 1978 datum = &node->datum; 1979 if (key->specified & flavor) { 1980 rule = avtab_node_to_str(pdb, key, datum); 1981 if (!rule) { 1982 rc = -1; 1983 goto exit; 1984 } 1985 rc = strs_add(strs, rule); 1986 if (rc != 0) { 1987 free(rule); 1988 goto exit; 1989 } 1990 } 1991 } 1992 1993 strs_sort(strs); 1994 strs_write_each_indented(strs, out, indent); 1995 strs_free_all(strs); 1996 strs_destroy(&strs); 1997 } 1998 1999 return 0; 2000 2001 exit: 2002 strs_free_all(strs); 2003 strs_destroy(&strs); 2004 return rc; 2005 } 2006 2007 struct cond_data { 2008 char *expr; 2009 struct cond_node *cond; 2010 }; 2011 2012 static int cond_node_cmp(const void *a, const void *b) 2013 { 2014 const struct cond_data *aa = a; 2015 const struct cond_data *bb = b; 2016 return strcmp(aa->expr, bb->expr); 2017 } 2018 2019 static int write_cond_nodes_to_conf(FILE *out, struct policydb *pdb) 2020 { 2021 struct cond_data *cond_data; 2022 char *expr; 2023 struct cond_node *cond; 2024 unsigned i, num; 2025 int rc = 0; 2026 2027 num = 0; 2028 for (cond = pdb->cond_list; cond != NULL; cond = cond->next) { 2029 num++; 2030 } 2031 2032 if (num == 0) { 2033 return 0; 2034 } 2035 2036 cond_data = calloc(sizeof(struct cond_data), num); 2037 if (!cond_data) { 2038 rc = -1; 2039 goto exit; 2040 } 2041 2042 i = 0; 2043 for (cond = pdb->cond_list; cond != NULL; cond = cond->next) { 2044 cond_data[i].cond = cond; 2045 expr = cond_expr_to_str(pdb, cond->expr); 2046 if (!expr) { 2047 num = i; 2048 goto exit; 2049 } 2050 cond_data[i].expr = expr; 2051 i++; 2052 } 2053 2054 qsort(cond_data, num, sizeof(*cond_data), cond_node_cmp); 2055 2056 for (i=0; i<num; i++) { 2057 expr = cond_data[i].expr; 2058 cond = cond_data[i].cond; 2059 2060 sepol_printf(out, "if (%s) {\n", expr); 2061 2062 if (cond->true_list != NULL) { 2063 rc = write_cond_av_list_to_conf(out, pdb, cond->true_list, 1); 2064 if (rc != 0) { 2065 goto exit; 2066 } 2067 } 2068 2069 if (cond->false_list != NULL) { 2070 sepol_printf(out, "} else {\n"); 2071 rc = write_cond_av_list_to_conf(out, pdb, cond->false_list, 1); 2072 if (rc != 0) { 2073 goto exit; 2074 } 2075 } 2076 sepol_printf(out, "}\n"); 2077 } 2078 2079 exit: 2080 if (cond_data) { 2081 for (i=0; i<num; i++) { 2082 free(cond_data[i].expr); 2083 } 2084 free(cond_data); 2085 } 2086 2087 if (rc != 0) { 2088 sepol_log_err("Error writing conditional rules to policy.conf\n"); 2089 } 2090 2091 return rc; 2092 } 2093 2094 static int write_role_decl_rules_to_conf(FILE *out, struct policydb *pdb) 2095 { 2096 struct role_datum *role; 2097 struct strs *strs; 2098 char *name, *types, *p1, *p2; 2099 unsigned i, num; 2100 int rc = 0; 2101 2102 rc = strs_init(&strs, pdb->p_roles.nprim); 2103 if (rc != 0) { 2104 goto exit; 2105 } 2106 2107 /* Start at 1 to skip object_r */ 2108 for (i=1; i < pdb->p_roles.nprim; i++) { 2109 role = pdb->role_val_to_struct[i]; 2110 if (role && role->flavor == ROLE_ROLE) { 2111 rc = strs_add(strs, pdb->p_role_val_to_name[i]); 2112 if (rc != 0) { 2113 goto exit; 2114 } 2115 } 2116 } 2117 2118 strs_sort(strs); 2119 2120 num = strs_num_items(strs); 2121 2122 for (i=0; i<num; i++) { 2123 name = strs_read_at_index(strs, i); 2124 if (!name) { 2125 continue; 2126 } 2127 sepol_printf(out, "role %s;\n", name); 2128 } 2129 2130 for (i=0; i<num; i++) { 2131 name = strs_read_at_index(strs, i); 2132 if (!name) continue; 2133 role = hashtab_search(pdb->p_roles.table, name); 2134 if (!role) { 2135 rc = -1; 2136 goto exit; 2137 } 2138 if (ebitmap_cardinality(&role->types.types) == 0) continue; 2139 types = ebitmap_to_str(&role->types.types, pdb->p_type_val_to_name, 1); 2140 if (!types) { 2141 rc = -1; 2142 goto exit; 2143 } 2144 if (strlen(types) > 900) { 2145 p1 = types; 2146 while (p1) { 2147 p2 = p1; 2148 while (p2 - p1 < 600) { 2149 p2 = strchr(p2, ' '); 2150 if (!p2) 2151 break; 2152 p2++; 2153 } 2154 if (p2) { 2155 *(p2-1) = '\0'; 2156 } 2157 sepol_printf(out, "role %s types { %s };\n", name, p1); 2158 p1 = p2; 2159 } 2160 } else { 2161 sepol_printf(out, "role %s types { %s };\n", name, types); 2162 } 2163 free(types); 2164 } 2165 2166 exit: 2167 strs_destroy(&strs); 2168 2169 if (rc != 0) { 2170 sepol_log_err("Error writing role declarations to policy.conf\n"); 2171 } 2172 2173 return rc; 2174 } 2175 2176 static int write_role_transition_rules_to_conf(FILE *out, struct policydb *pdb) 2177 { 2178 role_trans_t *curr = pdb->role_tr; 2179 struct strs *strs; 2180 char *role, *type, *class, *new; 2181 int rc = 0; 2182 2183 rc = strs_init(&strs, 32); 2184 if (rc != 0) { 2185 goto exit; 2186 } 2187 2188 while (curr) { 2189 role = pdb->p_role_val_to_name[curr->role - 1]; 2190 type = pdb->p_type_val_to_name[curr->type - 1]; 2191 class = pdb->p_class_val_to_name[curr->tclass - 1]; 2192 new = pdb->p_role_val_to_name[curr->new_role - 1]; 2193 2194 rc = strs_create_and_add(strs, "role_transition %s %s:%s %s;", 4, 2195 role, type, class, new); 2196 if (rc != 0) { 2197 goto exit; 2198 } 2199 2200 curr = curr->next; 2201 } 2202 2203 strs_sort(strs); 2204 strs_write_each(strs, out); 2205 2206 exit: 2207 strs_free_all(strs); 2208 strs_destroy(&strs); 2209 2210 if (rc != 0) { 2211 sepol_log_err("Error writing role transition rules to policy.conf\n"); 2212 } 2213 2214 return rc; 2215 } 2216 2217 static int write_role_allow_rules_to_conf(FILE *out, struct policydb *pdb) 2218 { 2219 role_allow_t *curr = pdb->role_allow; 2220 struct strs *strs; 2221 char *role, *new; 2222 int rc = 0; 2223 2224 rc = strs_init(&strs, 32); 2225 if (rc != 0) { 2226 goto exit; 2227 } 2228 2229 while (curr) { 2230 role = pdb->p_role_val_to_name[curr->role - 1]; 2231 new = pdb->p_role_val_to_name[curr->new_role - 1]; 2232 2233 rc = strs_create_and_add(strs, "allow %s %s;", 2, role, new); 2234 if (rc != 0) { 2235 goto exit; 2236 } 2237 2238 curr = curr->next; 2239 } 2240 2241 strs_sort(strs); 2242 strs_write_each(strs, out); 2243 2244 exit: 2245 strs_free_all(strs); 2246 strs_destroy(&strs); 2247 2248 if (rc != 0) { 2249 sepol_log_err("Error writing role allow rules to policy.conf\n"); 2250 } 2251 2252 return rc; 2253 } 2254 2255 static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb) 2256 { 2257 struct user_datum *user; 2258 struct strs *strs; 2259 char *name, *roles, *level, *range; 2260 unsigned i, num; 2261 int rc = 0; 2262 2263 rc = strs_init(&strs, pdb->p_users.nprim); 2264 if (rc != 0) { 2265 goto exit; 2266 } 2267 2268 for (i=0; i < pdb->p_users.nprim; i++) { 2269 rc = strs_add(strs, pdb->p_user_val_to_name[i]); 2270 if (rc != 0) { 2271 goto exit; 2272 } 2273 } 2274 2275 strs_sort(strs); 2276 2277 num = strs_num_items(strs); 2278 2279 for (i=0; i<num; i++) { 2280 name = strs_read_at_index(strs, i); 2281 if (!name) { 2282 continue; 2283 } 2284 user = hashtab_search(pdb->p_users.table, name); 2285 if (!user) { 2286 rc = -1; 2287 goto exit; 2288 } 2289 sepol_printf(out, "user %s", name); 2290 2291 if (ebitmap_cardinality(&user->roles.roles) > 0) { 2292 roles = ebitmap_to_str(&user->roles.roles, 2293 pdb->p_role_val_to_name, 1); 2294 if (!roles) { 2295 rc = -1; 2296 goto exit; 2297 } 2298 if (strchr(roles, ' ')) { 2299 sepol_printf(out, " roles { %s }", roles); 2300 } else { 2301 sepol_printf(out, " roles %s", roles); 2302 } 2303 free(roles); 2304 } 2305 2306 if (pdb->mls) { 2307 level = level_to_str(pdb, &user->exp_dfltlevel); 2308 if (!level) { 2309 rc = -1; 2310 goto exit; 2311 } 2312 sepol_printf(out, " level %s", level); 2313 free(level); 2314 2315 range = range_to_str(pdb, &user->exp_range); 2316 if (!range) { 2317 rc = -1; 2318 goto exit; 2319 } 2320 sepol_printf(out, " range %s", range); 2321 free(range); 2322 } 2323 sepol_printf(out, ";\n"); 2324 } 2325 2326 strs_destroy(&strs); 2327 2328 exit: 2329 if (rc != 0) { 2330 sepol_log_err("Error writing user declarations to policy.conf\n"); 2331 } 2332 2333 return rc; 2334 } 2335 2336 static char *context_to_str(struct policydb *pdb, struct context_struct *con) 2337 { 2338 char *user, *role, *type, *range; 2339 char *ctx = NULL; 2340 2341 user = pdb->p_user_val_to_name[con->user - 1]; 2342 role = pdb->p_role_val_to_name[con->role - 1]; 2343 type = pdb->p_type_val_to_name[con->type - 1]; 2344 2345 if (pdb->mls) { 2346 range = range_to_str(pdb, &con->range); 2347 ctx = create_str("%s:%s:%s:%s", 4, user, role, type, range); 2348 free(range); 2349 } else { 2350 ctx = create_str("%s:%s:%s", 3, user, role, type); 2351 } 2352 2353 return ctx; 2354 } 2355 2356 static int write_sid_context_rules_to_conf(FILE *out, struct policydb *pdb, const char *const *sid_to_str, unsigned num_sids) 2357 { 2358 struct ocontext *isid; 2359 struct strs *strs; 2360 char *sid; 2361 char unknown[18]; 2362 char *ctx, *rule; 2363 unsigned i; 2364 int rc; 2365 2366 rc = strs_init(&strs, 32); 2367 if (rc != 0) { 2368 goto exit; 2369 } 2370 2371 for (isid = pdb->ocontexts[0]; isid != NULL; isid = isid->next) { 2372 i = isid->sid[0]; 2373 if (i < num_sids) { 2374 sid = (char *)sid_to_str[i]; 2375 } else { 2376 snprintf(unknown, 18, "%s%u", "UNKNOWN", i); 2377 sid = unknown; 2378 } 2379 2380 ctx = context_to_str(pdb, &isid->context[0]); 2381 if (!ctx) { 2382 rc = -1; 2383 goto exit; 2384 } 2385 2386 rule = create_str("sid %s %s", 2, sid, ctx); 2387 free(ctx); 2388 if (!rule) { 2389 rc = -1; 2390 goto exit; 2391 } 2392 2393 rc = strs_add_at_index(strs, rule, i); 2394 if (rc != 0) { 2395 free(rule); 2396 goto exit; 2397 } 2398 } 2399 2400 strs_write_each(strs, out); 2401 2402 exit: 2403 strs_free_all(strs); 2404 strs_destroy(&strs); 2405 2406 if (rc != 0) { 2407 sepol_log_err("Error writing sidcontext rules to policy.conf\n"); 2408 } 2409 2410 return rc; 2411 } 2412 2413 static int write_selinux_isid_rules_to_conf(FILE *out, struct policydb *pdb) 2414 { 2415 return write_sid_context_rules_to_conf(out, pdb, selinux_sid_to_str, 2416 SELINUX_SID_SZ); 2417 } 2418 2419 static int write_selinux_fsuse_rules_to_conf(FILE *out, struct policydb *pdb) 2420 { 2421 struct ocontext *fsuse; 2422 const char *behavior; 2423 char *name, *ctx; 2424 int rc = 0; 2425 2426 for (fsuse = pdb->ocontexts[5]; fsuse != NULL; fsuse = fsuse->next) { 2427 switch (fsuse->v.behavior) { 2428 case SECURITY_FS_USE_XATTR: behavior = "xattr"; break; 2429 case SECURITY_FS_USE_TRANS: behavior = "trans"; break; 2430 case SECURITY_FS_USE_TASK: behavior = "task"; break; 2431 default: 2432 sepol_log_err("Unknown fsuse behavior: %i", fsuse->v.behavior); 2433 rc = -1; 2434 goto exit; 2435 } 2436 2437 name = fsuse->u.name; 2438 ctx = context_to_str(pdb, &fsuse->context[0]); 2439 if (!ctx) { 2440 rc = -1; 2441 goto exit; 2442 } 2443 2444 sepol_printf(out, "fs_use_%s %s %s;\n", behavior, name, ctx); 2445 2446 free(ctx); 2447 } 2448 2449 exit: 2450 if (rc != 0) { 2451 sepol_log_err("Error writing fsuse rules to policy.conf\n"); 2452 } 2453 2454 return rc; 2455 } 2456 2457 static int write_genfscon_rules_to_conf(FILE *out, struct policydb *pdb) 2458 { 2459 struct genfs *genfs; 2460 struct ocontext *ocon; 2461 struct strs *strs; 2462 char *fstype, *name, *ctx; 2463 int rc; 2464 2465 rc = strs_init(&strs, 32); 2466 if (rc != 0) { 2467 goto exit; 2468 } 2469 2470 for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) { 2471 for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) { 2472 fstype = genfs->fstype; 2473 name = ocon->u.name; 2474 2475 ctx = context_to_str(pdb, &ocon->context[0]); 2476 if (!ctx) { 2477 rc = -1; 2478 goto exit; 2479 } 2480 2481 rc = strs_create_and_add(strs, "genfscon %s %s %s", 3, 2482 fstype, name, ctx); 2483 free(ctx); 2484 if (rc != 0) { 2485 goto exit; 2486 } 2487 } 2488 } 2489 2490 strs_sort(strs); 2491 strs_write_each(strs, out); 2492 2493 exit: 2494 strs_free_all(strs); 2495 strs_destroy(&strs); 2496 2497 if (rc != 0) { 2498 sepol_log_err("Error writing genfscon rules to policy.conf\n"); 2499 } 2500 2501 return rc; 2502 } 2503 2504 static int write_selinux_port_rules_to_conf(FILE *out, struct policydb *pdb) 2505 { 2506 struct ocontext *portcon; 2507 const char *protocol; 2508 uint16_t low; 2509 uint16_t high; 2510 char low_high_str[44]; /* 2^64 <= 20 digits so "low-high" <= 44 chars */ 2511 char *ctx; 2512 int rc = 0; 2513 2514 for (portcon = pdb->ocontexts[2]; portcon != NULL; portcon = portcon->next) { 2515 switch (portcon->u.port.protocol) { 2516 case IPPROTO_TCP: protocol = "tcp"; break; 2517 case IPPROTO_UDP: protocol = "udp"; break; 2518 case IPPROTO_DCCP: protocol = "dccp"; break; 2519 case IPPROTO_SCTP: protocol = "sctp"; break; 2520 default: 2521 sepol_log_err("Unknown portcon protocol: %i", portcon->u.port.protocol); 2522 rc = -1; 2523 goto exit; 2524 } 2525 2526 low = portcon->u.port.low_port; 2527 high = portcon->u.port.high_port; 2528 if (low == high) { 2529 rc = snprintf(low_high_str, 44, "%u", low); 2530 } else { 2531 rc = snprintf(low_high_str, 44, "%u-%u", low, high); 2532 } 2533 if (rc < 0 || rc >= 44) { 2534 rc = -1; 2535 goto exit; 2536 } 2537 2538 ctx = context_to_str(pdb, &portcon->context[0]); 2539 if (!ctx) { 2540 rc = -1; 2541 goto exit; 2542 } 2543 2544 sepol_printf(out, "portcon %s %s %s\n", protocol, low_high_str, ctx); 2545 2546 free(ctx); 2547 } 2548 2549 rc = 0; 2550 2551 exit: 2552 if (rc != 0) { 2553 sepol_log_err("Error writing portcon rules to policy.conf\n"); 2554 } 2555 2556 return rc; 2557 } 2558 2559 static int write_selinux_netif_rules_to_conf(FILE *out, struct policydb *pdb) 2560 { 2561 struct ocontext *netif; 2562 char *name, *ctx1, *ctx2; 2563 int rc = 0; 2564 2565 for (netif = pdb->ocontexts[3]; netif != NULL; netif = netif->next) { 2566 name = netif->u.name; 2567 ctx1 = context_to_str(pdb, &netif->context[0]); 2568 if (!ctx1) { 2569 rc = -1; 2570 goto exit; 2571 } 2572 ctx2 = context_to_str(pdb, &netif->context[1]); 2573 if (!ctx2) { 2574 free(ctx1); 2575 rc = -1; 2576 goto exit; 2577 } 2578 2579 sepol_printf(out, "netifcon %s %s %s\n", name, ctx1, ctx2); 2580 2581 free(ctx1); 2582 free(ctx2); 2583 } 2584 2585 exit: 2586 if (rc != 0) { 2587 sepol_log_err("Error writing netifcon rules to policy.conf\n"); 2588 } 2589 2590 return rc; 2591 } 2592 2593 static int write_selinux_node_rules_to_conf(FILE *out, struct policydb *pdb) 2594 { 2595 struct ocontext *node; 2596 char addr[INET_ADDRSTRLEN]; 2597 char mask[INET_ADDRSTRLEN]; 2598 char *ctx; 2599 int rc = 0; 2600 2601 for (node = pdb->ocontexts[4]; node != NULL; node = node->next) { 2602 if (inet_ntop(AF_INET, &node->u.node.addr, addr, INET_ADDRSTRLEN) == NULL) { 2603 sepol_log_err("Nodecon address is invalid: %s", strerror(errno)); 2604 rc = -1; 2605 goto exit; 2606 } 2607 2608 if (inet_ntop(AF_INET, &node->u.node.mask, mask, INET_ADDRSTRLEN) == NULL) { 2609 sepol_log_err("Nodecon mask is invalid: %s", strerror(errno)); 2610 rc = -1; 2611 goto exit; 2612 } 2613 2614 ctx = context_to_str(pdb, &node->context[0]); 2615 if (!ctx) { 2616 rc = -1; 2617 goto exit; 2618 } 2619 2620 sepol_printf(out, "nodecon %s %s %s\n", addr, mask, ctx); 2621 2622 free(ctx); 2623 } 2624 2625 exit: 2626 if (rc != 0) { 2627 sepol_log_err("Error writing nodecon rules to policy.conf\n"); 2628 } 2629 2630 return rc; 2631 } 2632 2633 2634 static int write_selinux_node6_rules_to_conf(FILE *out, struct policydb *pdb) 2635 { 2636 struct ocontext *node6; 2637 char addr[INET6_ADDRSTRLEN]; 2638 char mask[INET6_ADDRSTRLEN]; 2639 char *ctx; 2640 int rc = 0; 2641 2642 for (node6 = pdb->ocontexts[6]; node6 != NULL; node6 = node6->next) { 2643 if (inet_ntop(AF_INET6, &node6->u.node6.addr, addr, INET6_ADDRSTRLEN) == NULL) { 2644 sepol_log_err("Nodecon address is invalid: %s", strerror(errno)); 2645 rc = -1; 2646 goto exit; 2647 } 2648 2649 if (inet_ntop(AF_INET6, &node6->u.node6.mask, mask, INET6_ADDRSTRLEN) == NULL) { 2650 sepol_log_err("Nodecon mask is invalid: %s", strerror(errno)); 2651 rc = -1; 2652 goto exit; 2653 } 2654 2655 ctx = context_to_str(pdb, &node6->context[0]); 2656 if (!ctx) { 2657 rc = -1; 2658 goto exit; 2659 } 2660 2661 sepol_printf(out, "nodecon %s %s %s\n", addr, mask, ctx); 2662 2663 free(ctx); 2664 } 2665 2666 exit: 2667 if (rc != 0) { 2668 sepol_log_err("Error writing nodecon rules to policy.conf\n"); 2669 } 2670 2671 return rc; 2672 } 2673 2674 static int write_selinux_ibpkey_rules_to_conf(FILE *out, struct policydb *pdb) 2675 { 2676 struct ocontext *ibpkeycon; 2677 char subnet_prefix_str[INET6_ADDRSTRLEN]; 2678 struct in6_addr subnet_prefix = IN6ADDR_ANY_INIT; 2679 uint16_t low; 2680 uint16_t high; 2681 char low_high_str[44]; /* 2^64 <= 20 digits so "low-high" <= 44 chars */ 2682 char *ctx; 2683 int rc = 0; 2684 2685 for (ibpkeycon = pdb->ocontexts[OCON_IBPKEY]; ibpkeycon != NULL; 2686 ibpkeycon = ibpkeycon->next) { 2687 memcpy(&subnet_prefix.s6_addr, &ibpkeycon->u.ibpkey.subnet_prefix, 2688 sizeof(ibpkeycon->u.ibpkey.subnet_prefix)); 2689 2690 if (inet_ntop(AF_INET6, &subnet_prefix.s6_addr, 2691 subnet_prefix_str, INET6_ADDRSTRLEN) == NULL) { 2692 sepol_log_err("ibpkeycon address is invalid: %s", 2693 strerror(errno)); 2694 rc = -1; 2695 goto exit; 2696 } 2697 2698 low = ibpkeycon->u.ibpkey.low_pkey; 2699 high = ibpkeycon->u.ibpkey.high_pkey; 2700 if (low == high) { 2701 rc = snprintf(low_high_str, 44, "%u", low); 2702 } else { 2703 rc = snprintf(low_high_str, 44, "%u-%u", low, high); 2704 } 2705 if (rc < 0 || rc >= 44) { 2706 rc = -1; 2707 goto exit; 2708 } 2709 2710 ctx = context_to_str(pdb, &ibpkeycon->context[0]); 2711 if (!ctx) { 2712 rc = -1; 2713 goto exit; 2714 } 2715 2716 sepol_printf(out, "ibpkeycon %s %s %s\n", subnet_prefix_str, 2717 low_high_str, ctx); 2718 2719 free(ctx); 2720 } 2721 2722 rc = 0; 2723 2724 exit: 2725 if (rc != 0) { 2726 sepol_log_err("Error writing ibpkeycon rules to policy.conf\n"); 2727 } 2728 2729 return rc; 2730 } 2731 2732 static int write_selinux_ibendport_rules_to_conf(FILE *out, struct policydb *pdb) 2733 { 2734 struct ocontext *ibendportcon; 2735 char port_str[4]; 2736 char *ctx; 2737 int rc = 0; 2738 2739 for (ibendportcon = pdb->ocontexts[OCON_IBENDPORT]; 2740 ibendportcon != NULL; ibendportcon = ibendportcon->next) { 2741 rc = snprintf(port_str, 4, "%u", ibendportcon->u.ibendport.port); 2742 if (rc < 0 || rc >= 4) { 2743 rc = -1; 2744 goto exit; 2745 } 2746 2747 ctx = context_to_str(pdb, &ibendportcon->context[0]); 2748 if (!ctx) { 2749 rc = -1; 2750 goto exit; 2751 } 2752 2753 sepol_printf(out, "ibendportcon %s %s %s\n", ibendportcon->u.ibendport.dev_name, port_str, ctx); 2754 2755 free(ctx); 2756 } 2757 2758 rc = 0; 2759 2760 exit: 2761 if (rc != 0) { 2762 sepol_log_err("Error writing ibendportcon rules to policy.conf\n"); 2763 } 2764 2765 return rc; 2766 } 2767 2768 static int write_xen_isid_rules_to_conf(FILE *out, struct policydb *pdb) 2769 { 2770 return write_sid_context_rules_to_conf(out, pdb, xen_sid_to_str, XEN_SID_SZ); 2771 } 2772 2773 2774 static int write_xen_pirq_rules_to_conf(FILE *out, struct policydb *pdb) 2775 { 2776 struct ocontext *pirq; 2777 char pirq_str[21]; /* 2^64-1 <= 20 digits */ 2778 char *ctx; 2779 int rc = 0; 2780 2781 for (pirq = pdb->ocontexts[1]; pirq != NULL; pirq = pirq->next) { 2782 rc = snprintf(pirq_str, 21, "%i", pirq->u.pirq); 2783 if (rc < 0 || rc >= 21) { 2784 fprintf(stderr,"error1\n"); 2785 rc = -1; 2786 goto exit; 2787 } 2788 2789 ctx = context_to_str(pdb, &pirq->context[0]); 2790 if (!ctx) { 2791 rc = -1; 2792 fprintf(stderr,"error2\n"); 2793 goto exit; 2794 } 2795 2796 sepol_printf(out, "pirqcon %s %s\n", pirq_str, ctx); 2797 2798 free(ctx); 2799 } 2800 2801 rc = 0; 2802 2803 exit: 2804 if (rc != 0) { 2805 sepol_log_err("Error writing pirqcon rules to policy.conf\n"); 2806 } 2807 2808 return rc; 2809 } 2810 2811 static int write_xen_ioport_rules_to_conf(FILE *out, struct policydb *pdb) 2812 { 2813 struct ocontext *ioport; 2814 uint32_t low; 2815 uint32_t high; 2816 char low_high_str[40]; /* 2^64-1 <= 16 digits (hex) so low-high < 40 chars */ 2817 char *ctx; 2818 int rc = 0; 2819 2820 for (ioport = pdb->ocontexts[2]; ioport != NULL; ioport = ioport->next) { 2821 low = ioport->u.ioport.low_ioport; 2822 high = ioport->u.ioport.high_ioport; 2823 if (low == high) { 2824 rc = snprintf(low_high_str, 40, "0x%x", low); 2825 } else { 2826 rc = snprintf(low_high_str, 40, "0x%x-0x%x", low, high); 2827 } 2828 if (rc < 0 || rc >= 40) { 2829 rc = -1; 2830 goto exit; 2831 } 2832 2833 ctx = context_to_str(pdb, &ioport->context[0]); 2834 if (!ctx) { 2835 rc = -1; 2836 goto exit; 2837 } 2838 2839 sepol_printf(out, "ioportcon %s %s\n", low_high_str, ctx); 2840 2841 free(ctx); 2842 } 2843 2844 rc = 0; 2845 2846 exit: 2847 if (rc != 0) { 2848 sepol_log_err("Error writing ioportcon rules to policy.conf\n"); 2849 } 2850 2851 return rc; 2852 } 2853 2854 static int write_xen_iomem_rules_to_conf(FILE *out, struct policydb *pdb) 2855 { 2856 struct ocontext *iomem; 2857 uint64_t low; 2858 uint64_t high; 2859 char low_high_str[40]; /* 2^64-1 <= 16 digits (hex) so low-high < 40 chars */ 2860 char *ctx; 2861 int rc = 0; 2862 2863 for (iomem = pdb->ocontexts[3]; iomem != NULL; iomem = iomem->next) { 2864 low = iomem->u.iomem.low_iomem; 2865 high = iomem->u.iomem.high_iomem; 2866 if (low == high) { 2867 rc = snprintf(low_high_str, 40, "0x%"PRIx64, low); 2868 } else { 2869 rc = snprintf(low_high_str, 40, "0x%"PRIx64"-0x%"PRIx64, low, high); 2870 } 2871 if (rc < 0 || rc >= 40) { 2872 rc = -1; 2873 goto exit; 2874 } 2875 2876 ctx = context_to_str(pdb, &iomem->context[0]); 2877 if (!ctx) { 2878 rc = -1; 2879 goto exit; 2880 } 2881 2882 sepol_printf(out, "iomemcon %s %s\n", low_high_str, ctx); 2883 2884 free(ctx); 2885 } 2886 2887 rc = 0; 2888 2889 exit: 2890 if (rc != 0) { 2891 sepol_log_err("Error writing iomemcon rules to policy.conf\n"); 2892 } 2893 2894 return rc; 2895 } 2896 2897 static int write_xen_pcidevice_rules_to_conf(FILE *out, struct policydb *pdb) 2898 { 2899 struct ocontext *pcid; 2900 char device_str[20]; /* 2^64-1 <= 16 digits (hex) so < 19 chars */ 2901 char *ctx; 2902 int rc = 0; 2903 2904 for (pcid = pdb->ocontexts[4]; pcid != NULL; pcid = pcid->next) { 2905 rc = snprintf(device_str, 20, "0x%lx", (unsigned long)pcid->u.device); 2906 if (rc < 0 || rc >= 20) { 2907 rc = -1; 2908 goto exit; 2909 } 2910 2911 ctx = context_to_str(pdb, &pcid->context[0]); 2912 if (!ctx) { 2913 rc = -1; 2914 goto exit; 2915 } 2916 2917 sepol_printf(out, "pcidevicecon %s %s\n", device_str, ctx); 2918 2919 free(ctx); 2920 } 2921 2922 rc = 0; 2923 2924 exit: 2925 if (rc != 0) { 2926 sepol_log_err("Error writing pcidevicecon rules to policy.conf\n"); 2927 } 2928 2929 return rc; 2930 } 2931 2932 static int write_xen_devicetree_rules_to_conf(FILE *out, struct policydb *pdb) 2933 { 2934 struct ocontext *dtree; 2935 char *name, *ctx; 2936 int rc = 0; 2937 2938 for (dtree = pdb->ocontexts[5]; dtree != NULL; dtree = dtree->next) { 2939 name = dtree->u.name; 2940 ctx = context_to_str(pdb, &dtree->context[0]); 2941 if (!ctx) { 2942 rc = -1; 2943 goto exit; 2944 } 2945 2946 sepol_printf(out, "devicetreecon %s %s\n", name, ctx); 2947 2948 free(ctx); 2949 } 2950 2951 exit: 2952 if (rc != 0) { 2953 sepol_log_err("Error writing devicetreecon rules to policy.conf\n"); 2954 } 2955 2956 return rc; 2957 } 2958 2959 int sepol_kernel_policydb_to_conf(FILE *out, struct policydb *pdb) 2960 { 2961 struct strs *mls_constraints = NULL; 2962 struct strs *non_mls_constraints = NULL; 2963 struct strs *mls_validatetrans = NULL; 2964 struct strs *non_mls_validatetrans = NULL; 2965 int rc = 0; 2966 2967 rc = strs_init(&mls_constraints, 32); 2968 if (rc != 0) { 2969 goto exit; 2970 } 2971 2972 rc = strs_init(&non_mls_constraints, 32); 2973 if (rc != 0) { 2974 goto exit; 2975 } 2976 2977 rc = strs_init(&mls_validatetrans, 32); 2978 if (rc != 0) { 2979 goto exit; 2980 } 2981 2982 rc = strs_init(&non_mls_validatetrans, 32); 2983 if (rc != 0) { 2984 goto exit; 2985 } 2986 2987 if (pdb == NULL) { 2988 sepol_log_err("No policy"); 2989 rc = -1; 2990 goto exit; 2991 } 2992 2993 if (pdb->policy_type != SEPOL_POLICY_KERN) { 2994 sepol_log_err("Policy is not a kernel policy"); 2995 rc = -1; 2996 goto exit; 2997 } 2998 2999 rc = constraint_rules_to_strs(pdb, mls_constraints, non_mls_constraints); 3000 if (rc != 0) { 3001 goto exit; 3002 } 3003 3004 rc = validatetrans_rules_to_strs(pdb, mls_validatetrans, non_mls_validatetrans); 3005 if (rc != 0) { 3006 goto exit; 3007 } 3008 3009 rc = write_handle_unknown_to_conf(out, pdb); 3010 if (rc != 0) { 3011 goto exit; 3012 } 3013 3014 rc = write_class_decl_rules_to_conf(out, pdb); 3015 if (rc != 0) { 3016 goto exit; 3017 } 3018 3019 rc = write_sid_decl_rules_to_conf(out, pdb); 3020 if (rc != 0) { 3021 goto exit; 3022 } 3023 3024 rc = write_class_and_common_rules_to_conf(out, pdb); 3025 if (rc != 0) { 3026 goto exit; 3027 } 3028 3029 rc = write_default_rules_to_conf(out, pdb); 3030 if (rc != 0) { 3031 goto exit; 3032 } 3033 3034 rc = write_mls_rules_to_conf(out, pdb); 3035 if (rc != 0) { 3036 goto exit; 3037 } 3038 3039 strs_write_each(mls_constraints, out); 3040 strs_write_each(mls_validatetrans, out); 3041 3042 rc = write_polcap_rules_to_conf(out, pdb); 3043 if (rc != 0) { 3044 goto exit; 3045 } 3046 3047 rc = write_type_attributes_to_conf(out, pdb); 3048 if (rc != 0) { 3049 goto exit; 3050 } 3051 3052 rc = write_role_attributes_to_conf(out, pdb); 3053 if (rc != 0) { 3054 goto exit; 3055 } 3056 3057 rc = write_boolean_decl_rules_to_conf(out, pdb); 3058 if (rc != 0) { 3059 goto exit; 3060 } 3061 3062 rc = write_type_decl_rules_to_conf(out, pdb); 3063 if (rc != 0) { 3064 goto exit; 3065 } 3066 3067 rc = write_type_alias_rules_to_conf(out, pdb); 3068 if (rc != 0) { 3069 goto exit; 3070 } 3071 3072 rc = write_type_bounds_rules_to_conf(out, pdb); 3073 if (rc != 0) { 3074 goto exit; 3075 } 3076 3077 rc = write_type_attribute_sets_to_conf(out, pdb); 3078 if (rc != 0) { 3079 goto exit; 3080 } 3081 3082 rc = write_type_permissive_rules_to_conf(out, pdb); 3083 if (rc != 0) { 3084 goto exit; 3085 } 3086 3087 rc = write_avtab_to_conf(out, pdb, 0); 3088 if (rc != 0) { 3089 goto exit; 3090 } 3091 write_filename_trans_rules_to_conf(out, pdb); 3092 3093 if (pdb->mls) { 3094 rc = write_range_trans_rules_to_conf(out, pdb); 3095 if (rc != 0) { 3096 goto exit; 3097 } 3098 } 3099 3100 rc = write_cond_nodes_to_conf(out, pdb); 3101 if (rc != 0) { 3102 goto exit; 3103 } 3104 3105 rc = write_role_decl_rules_to_conf(out, pdb); 3106 if (rc != 0) { 3107 goto exit; 3108 } 3109 3110 rc = write_role_transition_rules_to_conf(out, pdb); 3111 if (rc != 0) { 3112 goto exit; 3113 } 3114 3115 rc = write_role_allow_rules_to_conf(out, pdb); 3116 if (rc != 0) { 3117 goto exit; 3118 } 3119 3120 rc = write_user_decl_rules_to_conf(out, pdb); 3121 if (rc != 0) { 3122 goto exit; 3123 } 3124 3125 strs_write_each(non_mls_constraints, out); 3126 strs_write_each(non_mls_validatetrans, out); 3127 3128 rc = sort_ocontexts(pdb); 3129 if (rc != 0) { 3130 goto exit; 3131 } 3132 3133 if (pdb->target_platform == SEPOL_TARGET_SELINUX) { 3134 rc = write_selinux_isid_rules_to_conf(out, pdb); 3135 if (rc != 0) { 3136 goto exit; 3137 } 3138 3139 rc = write_selinux_fsuse_rules_to_conf(out, pdb); 3140 if (rc != 0) { 3141 goto exit; 3142 } 3143 3144 rc = write_genfscon_rules_to_conf(out, pdb); 3145 if (rc != 0) { 3146 goto exit; 3147 } 3148 3149 rc = write_selinux_port_rules_to_conf(out, pdb); 3150 if (rc != 0) { 3151 goto exit; 3152 } 3153 3154 rc = write_selinux_netif_rules_to_conf(out, pdb); 3155 if (rc != 0) { 3156 goto exit; 3157 } 3158 3159 rc = write_selinux_node_rules_to_conf(out, pdb); 3160 if (rc != 0) { 3161 goto exit; 3162 } 3163 3164 rc = write_selinux_node6_rules_to_conf(out, pdb); 3165 if (rc != 0) { 3166 goto exit; 3167 } 3168 3169 rc = write_selinux_ibpkey_rules_to_conf(out, pdb); 3170 if (rc != 0) { 3171 goto exit; 3172 } 3173 3174 rc = write_selinux_ibendport_rules_to_conf(out, pdb); 3175 if (rc != 0) { 3176 goto exit; 3177 } 3178 } else if (pdb->target_platform == SEPOL_TARGET_XEN) { 3179 rc = write_xen_isid_rules_to_conf(out, pdb); 3180 if (rc != 0) { 3181 goto exit; 3182 } 3183 3184 rc = write_genfscon_rules_to_conf(out, pdb); 3185 if (rc != 0) { 3186 goto exit; 3187 } 3188 3189 rc = write_xen_pirq_rules_to_conf(out, pdb); 3190 if (rc != 0) { 3191 goto exit; 3192 } 3193 3194 rc = write_xen_iomem_rules_to_conf(out, pdb); 3195 if (rc != 0) { 3196 goto exit; 3197 } 3198 3199 rc = write_xen_ioport_rules_to_conf(out, pdb); 3200 if (rc != 0) { 3201 goto exit; 3202 } 3203 3204 rc = write_xen_pcidevice_rules_to_conf(out, pdb); 3205 if (rc != 0) { 3206 goto exit; 3207 } 3208 3209 rc = write_xen_devicetree_rules_to_conf(out, pdb); 3210 if (rc != 0) { 3211 goto exit; 3212 } 3213 } 3214 3215 exit: 3216 strs_free_all(mls_constraints); 3217 strs_destroy(&mls_constraints); 3218 strs_free_all(non_mls_constraints); 3219 strs_destroy(&non_mls_constraints); 3220 strs_free_all(mls_validatetrans); 3221 strs_destroy(&mls_validatetrans); 3222 strs_free_all(non_mls_validatetrans); 3223 strs_destroy(&non_mls_validatetrans); 3224 3225 return rc; 3226 } 3227