1 /* Author : Stephen Smalley, <sds (at) epoch.ncsc.mil> */ 2 /* 3 * Updated: Trusted Computer Solutions, Inc. <dgoeddel (at) trustedcs.com> 4 * 5 * Support for enhanced MLS infrastructure. 6 * 7 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24 /* FLASK */ 25 26 /* 27 * Implementation of the multi-level security (MLS) policy. 28 */ 29 30 #include <sepol/policydb/policydb.h> 31 #include <sepol/policydb/services.h> 32 #include <sepol/policydb/flask.h> 33 #include <sepol/policydb/context.h> 34 35 #include <stdlib.h> 36 37 #include "handle.h" 38 #include "debug.h" 39 #include "private.h" 40 #include "mls.h" 41 42 int mls_to_string(sepol_handle_t * handle, 43 const policydb_t * policydb, 44 const context_struct_t * mls, char **str) 45 { 46 47 char *ptr = NULL, *ptr2 = NULL; 48 49 /* Temporary buffer - length + NULL terminator */ 50 int len = mls_compute_context_len(policydb, mls) + 1; 51 52 ptr = (char *)malloc(len); 53 if (ptr == NULL) 54 goto omem; 55 56 /* Final string w/ ':' cut off */ 57 ptr2 = (char *)malloc(len - 1); 58 if (ptr2 == NULL) 59 goto omem; 60 61 mls_sid_to_context(policydb, mls, &ptr); 62 ptr -= len - 1; 63 strcpy(ptr2, ptr + 1); 64 65 free(ptr); 66 *str = ptr2; 67 return STATUS_SUCCESS; 68 69 omem: 70 ERR(handle, "out of memory, could not convert mls context to string"); 71 72 free(ptr); 73 free(ptr2); 74 return STATUS_ERR; 75 76 } 77 78 int mls_from_string(sepol_handle_t * handle, 79 const policydb_t * policydb, 80 const char *str, context_struct_t * mls) 81 { 82 83 char *tmp = strdup(str); 84 char *tmp_cp = tmp; 85 if (!tmp) 86 goto omem; 87 88 if (mls_context_to_sid(policydb, '$', &tmp_cp, mls) < 0) { 89 ERR(handle, "invalid MLS context %s", str); 90 free(tmp); 91 goto err; 92 } 93 94 free(tmp); 95 return STATUS_SUCCESS; 96 97 omem: 98 ERR(handle, "out of memory"); 99 100 err: 101 ERR(handle, "could not construct mls context structure"); 102 return STATUS_ERR; 103 } 104 105 /* 106 * Return the length in bytes for the MLS fields of the 107 * security context string representation of `context'. 108 */ 109 int mls_compute_context_len(const policydb_t * policydb, 110 const context_struct_t * context) 111 { 112 113 unsigned int i, l, len, range; 114 ebitmap_node_t *cnode; 115 116 if (!policydb->mls) 117 return 0; 118 119 len = 1; /* for the beginning ":" */ 120 for (l = 0; l < 2; l++) { 121 range = 0; 122 len += 123 strlen(policydb-> 124 p_sens_val_to_name[context->range.level[l].sens - 125 1]); 126 127 ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) { 128 if (ebitmap_node_get_bit(cnode, i)) { 129 if (range) { 130 range++; 131 continue; 132 } 133 134 len += 135 strlen(policydb->p_cat_val_to_name[i]) + 1; 136 range++; 137 } else { 138 if (range > 1) 139 len += 140 strlen(policydb-> 141 p_cat_val_to_name[i - 1]) + 142 1; 143 range = 0; 144 } 145 } 146 /* Handle case where last category is the end of range */ 147 if (range > 1) 148 len += strlen(policydb->p_cat_val_to_name[i - 1]) + 1; 149 150 if (l == 0) { 151 if (mls_level_eq(&context->range.level[0], 152 &context->range.level[1])) 153 break; 154 else 155 len++; 156 } 157 } 158 159 return len; 160 } 161 162 /* 163 * Write the security context string representation of 164 * the MLS fields of `context' into the string `*scontext'. 165 * Update `*scontext' to point to the end of the MLS fields. 166 */ 167 void mls_sid_to_context(const policydb_t * policydb, 168 const context_struct_t * context, char **scontext) 169 { 170 171 char *scontextp; 172 unsigned int i, l, range, wrote_sep; 173 ebitmap_node_t *cnode; 174 175 if (!policydb->mls) 176 return; 177 178 scontextp = *scontext; 179 180 *scontextp = ':'; 181 scontextp++; 182 183 for (l = 0; l < 2; l++) { 184 range = 0; 185 wrote_sep = 0; 186 strcpy(scontextp, 187 policydb->p_sens_val_to_name[context->range.level[l]. 188 sens - 1]); 189 scontextp += 190 strlen(policydb-> 191 p_sens_val_to_name[context->range.level[l].sens - 192 1]); 193 /* categories */ 194 ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) { 195 if (ebitmap_node_get_bit(cnode, i)) { 196 if (range) { 197 range++; 198 continue; 199 } 200 201 if (!wrote_sep) { 202 *scontextp++ = ':'; 203 wrote_sep = 1; 204 } else 205 *scontextp++ = ','; 206 strcpy(scontextp, 207 policydb->p_cat_val_to_name[i]); 208 scontextp += 209 strlen(policydb->p_cat_val_to_name[i]); 210 range++; 211 } else { 212 if (range > 1) { 213 if (range > 2) 214 *scontextp++ = '.'; 215 else 216 *scontextp++ = ','; 217 218 strcpy(scontextp, 219 policydb->p_cat_val_to_name[i - 220 1]); 221 scontextp += 222 strlen(policydb-> 223 p_cat_val_to_name[i - 1]); 224 } 225 range = 0; 226 } 227 } 228 /* Handle case where last category is the end of range */ 229 if (range > 1) { 230 if (range > 2) 231 *scontextp++ = '.'; 232 else 233 *scontextp++ = ','; 234 235 strcpy(scontextp, policydb->p_cat_val_to_name[i - 1]); 236 scontextp += strlen(policydb->p_cat_val_to_name[i - 1]); 237 } 238 239 if (l == 0) { 240 if (mls_level_eq(&context->range.level[0], 241 &context->range.level[1])) 242 break; 243 else { 244 *scontextp = '-'; 245 scontextp++; 246 } 247 } 248 } 249 250 *scontext = scontextp; 251 return; 252 } 253 254 /* 255 * Return 1 if the MLS fields in the security context 256 * structure `c' are valid. Return 0 otherwise. 257 */ 258 int mls_context_isvalid(const policydb_t * p, const context_struct_t * c) 259 { 260 261 level_datum_t *levdatum; 262 user_datum_t *usrdatum; 263 unsigned int i, l; 264 ebitmap_node_t *cnode; 265 hashtab_key_t key; 266 267 if (!p->mls) 268 return 1; 269 270 /* 271 * MLS range validity checks: high must dominate low, low level must 272 * be valid (category set <-> sensitivity check), and high level must 273 * be valid (category set <-> sensitivity check) 274 */ 275 if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) 276 /* High does not dominate low. */ 277 return 0; 278 279 for (l = 0; l < 2; l++) { 280 if (!c->range.level[l].sens 281 || c->range.level[l].sens > p->p_levels.nprim) 282 return 0; 283 284 key = p->p_sens_val_to_name[c->range.level[l].sens - 1]; 285 if (!key) 286 return 0; 287 288 levdatum = (level_datum_t *) hashtab_search(p->p_levels.table, key); 289 if (!levdatum) 290 return 0; 291 292 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) { 293 if (ebitmap_node_get_bit(cnode, i)) { 294 if (i > p->p_cats.nprim) 295 return 0; 296 if (!ebitmap_get_bit(&levdatum->level->cat, i)) 297 /* 298 * Category may not be associated with 299 * sensitivity in low level. 300 */ 301 return 0; 302 } 303 } 304 } 305 306 if (c->role == OBJECT_R_VAL) 307 return 1; 308 309 /* 310 * User must be authorized for the MLS range. 311 */ 312 if (!c->user || c->user > p->p_users.nprim) 313 return 0; 314 usrdatum = p->user_val_to_struct[c->user - 1]; 315 if (!usrdatum || !mls_range_contains(usrdatum->exp_range, c->range)) 316 return 0; /* user may not be associated with range */ 317 318 return 1; 319 } 320 321 /* 322 * Set the MLS fields in the security context structure 323 * `context' based on the string representation in 324 * the string `*scontext'. Update `*scontext' to 325 * point to the end of the string representation of 326 * the MLS fields. 327 * 328 * This function modifies the string in place, inserting 329 * NULL characters to terminate the MLS fields. 330 */ 331 int mls_context_to_sid(const policydb_t * policydb, 332 char oldc, char **scontext, context_struct_t * context) 333 { 334 335 char delim; 336 char *scontextp, *p, *rngptr; 337 level_datum_t *levdatum; 338 cat_datum_t *catdatum, *rngdatum; 339 unsigned int l; 340 341 if (!policydb->mls) 342 return 0; 343 344 /* No MLS component to the security context */ 345 if (!oldc) 346 goto err; 347 348 /* Extract low sensitivity. */ 349 scontextp = p = *scontext; 350 while (*p && *p != ':' && *p != '-') 351 p++; 352 353 delim = *p; 354 if (delim != 0) 355 *p++ = 0; 356 357 for (l = 0; l < 2; l++) { 358 levdatum = 359 (level_datum_t *) hashtab_search(policydb->p_levels.table, 360 (hashtab_key_t) scontextp); 361 362 if (!levdatum) 363 goto err; 364 365 context->range.level[l].sens = levdatum->level->sens; 366 367 if (delim == ':') { 368 /* Extract category set. */ 369 while (1) { 370 scontextp = p; 371 while (*p && *p != ',' && *p != '-') 372 p++; 373 delim = *p; 374 if (delim != 0) 375 *p++ = 0; 376 377 /* Separate into range if exists */ 378 if ((rngptr = strchr(scontextp, '.')) != NULL) { 379 /* Remove '.' */ 380 *rngptr++ = 0; 381 } 382 383 catdatum = 384 (cat_datum_t *) hashtab_search(policydb-> 385 p_cats.table, 386 (hashtab_key_t) 387 scontextp); 388 if (!catdatum) 389 goto err; 390 391 if (ebitmap_set_bit 392 (&context->range.level[l].cat, 393 catdatum->s.value - 1, 1)) 394 goto err; 395 396 /* If range, set all categories in range */ 397 if (rngptr) { 398 unsigned int i; 399 400 rngdatum = (cat_datum_t *) 401 hashtab_search(policydb->p_cats. 402 table, 403 (hashtab_key_t) 404 rngptr); 405 if (!rngdatum) 406 goto err; 407 408 if (catdatum->s.value >= 409 rngdatum->s.value) 410 goto err; 411 412 for (i = catdatum->s.value; 413 i < rngdatum->s.value; i++) { 414 if (ebitmap_set_bit 415 (&context->range.level[l]. 416 cat, i, 1)) 417 goto err; 418 } 419 } 420 421 if (delim != ',') 422 break; 423 } 424 } 425 if (delim == '-') { 426 /* Extract high sensitivity. */ 427 scontextp = p; 428 while (*p && *p != ':') 429 p++; 430 431 delim = *p; 432 if (delim != 0) 433 *p++ = 0; 434 } else 435 break; 436 } 437 438 /* High level is missing, copy low level */ 439 if (l == 0) { 440 if (mls_level_cpy(&context->range.level[1], 441 &context->range.level[0]) < 0) 442 goto err; 443 } 444 *scontext = ++p; 445 446 return STATUS_SUCCESS; 447 448 err: 449 return STATUS_ERR; 450 } 451 452 /* 453 * Copies the MLS range from `src' into `dst'. 454 */ 455 static inline int mls_copy_context(context_struct_t * dst, 456 context_struct_t * src) 457 { 458 int l, rc = 0; 459 460 /* Copy the MLS range from the source context */ 461 for (l = 0; l < 2; l++) { 462 dst->range.level[l].sens = src->range.level[l].sens; 463 rc = ebitmap_cpy(&dst->range.level[l].cat, 464 &src->range.level[l].cat); 465 if (rc) 466 break; 467 } 468 469 return rc; 470 } 471 472 /* 473 * Copies the effective MLS range from `src' into `dst'. 474 */ 475 static inline int mls_scopy_context(context_struct_t * dst, 476 context_struct_t * src) 477 { 478 int l, rc = 0; 479 480 /* Copy the MLS range from the source context */ 481 for (l = 0; l < 2; l++) { 482 dst->range.level[l].sens = src->range.level[0].sens; 483 rc = ebitmap_cpy(&dst->range.level[l].cat, 484 &src->range.level[0].cat); 485 if (rc) 486 break; 487 } 488 489 return rc; 490 } 491 492 /* 493 * Copies the MLS range `range' into `context'. 494 */ 495 static inline int mls_range_set(context_struct_t * context, mls_range_t * range) 496 { 497 int l, rc = 0; 498 499 /* Copy the MLS range into the context */ 500 for (l = 0; l < 2; l++) { 501 context->range.level[l].sens = range->level[l].sens; 502 rc = ebitmap_cpy(&context->range.level[l].cat, 503 &range->level[l].cat); 504 if (rc) 505 break; 506 } 507 508 return rc; 509 } 510 511 int mls_setup_user_range(context_struct_t * fromcon, user_datum_t * user, 512 context_struct_t * usercon, int mls) 513 { 514 if (mls) { 515 mls_level_t *fromcon_sen = &(fromcon->range.level[0]); 516 mls_level_t *fromcon_clr = &(fromcon->range.level[1]); 517 mls_level_t *user_low = &(user->exp_range.level[0]); 518 mls_level_t *user_clr = &(user->exp_range.level[1]); 519 mls_level_t *user_def = &(user->exp_dfltlevel); 520 mls_level_t *usercon_sen = &(usercon->range.level[0]); 521 mls_level_t *usercon_clr = &(usercon->range.level[1]); 522 523 /* Honor the user's default level if we can */ 524 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { 525 *usercon_sen = *user_def; 526 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { 527 *usercon_sen = *fromcon_sen; 528 } else if (mls_level_between(fromcon_clr, user_low, user_def)) { 529 *usercon_sen = *user_low; 530 } else 531 return -EINVAL; 532 533 /* Lower the clearance of available contexts 534 if the clearance of "fromcon" is lower than 535 that of the user's default clearance (but 536 only if the "fromcon" clearance dominates 537 the user's computed sensitivity level) */ 538 if (mls_level_dom(user_clr, fromcon_clr)) { 539 *usercon_clr = *fromcon_clr; 540 } else if (mls_level_dom(fromcon_clr, user_clr)) { 541 *usercon_clr = *user_clr; 542 } else 543 return -EINVAL; 544 } 545 546 return 0; 547 } 548 549 /* 550 * Convert the MLS fields in the security context 551 * structure `c' from the values specified in the 552 * policy `oldp' to the values specified in the policy `newp'. 553 */ 554 int mls_convert_context(policydb_t * oldp, 555 policydb_t * newp, context_struct_t * c) 556 { 557 level_datum_t *levdatum; 558 cat_datum_t *catdatum; 559 ebitmap_t bitmap; 560 unsigned int l, i; 561 ebitmap_node_t *cnode; 562 563 if (!oldp->mls) 564 return 0; 565 566 for (l = 0; l < 2; l++) { 567 levdatum = 568 (level_datum_t *) hashtab_search(newp->p_levels.table, 569 oldp-> 570 p_sens_val_to_name[c-> 571 range. 572 level 573 [l]. 574 sens - 575 1]); 576 577 if (!levdatum) 578 return -EINVAL; 579 c->range.level[l].sens = levdatum->level->sens; 580 581 ebitmap_init(&bitmap); 582 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) { 583 if (ebitmap_node_get_bit(cnode, i)) { 584 int rc; 585 586 catdatum = 587 (cat_datum_t *) hashtab_search(newp->p_cats. 588 table, 589 oldp-> 590 p_cat_val_to_name 591 [i]); 592 if (!catdatum) 593 return -EINVAL; 594 rc = ebitmap_set_bit(&bitmap, 595 catdatum->s.value - 1, 1); 596 if (rc) 597 return rc; 598 } 599 } 600 ebitmap_destroy(&c->range.level[l].cat); 601 c->range.level[l].cat = bitmap; 602 } 603 604 return 0; 605 } 606 607 int mls_compute_sid(policydb_t * policydb, 608 context_struct_t * scontext, 609 context_struct_t * tcontext, 610 sepol_security_class_t tclass, 611 uint32_t specified, context_struct_t * newcontext) 612 { 613 range_trans_t rtr; 614 struct mls_range *r; 615 struct class_datum *cladatum; 616 int default_range = 0; 617 618 if (!policydb->mls) 619 return 0; 620 621 switch (specified) { 622 case AVTAB_TRANSITION: 623 /* Look for a range transition rule. */ 624 rtr.source_type = scontext->type; 625 rtr.target_type = tcontext->type; 626 rtr.target_class = tclass; 627 r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr); 628 if (r) 629 return mls_range_set(newcontext, r); 630 631 if (tclass && tclass <= policydb->p_classes.nprim) { 632 cladatum = policydb->class_val_to_struct[tclass - 1]; 633 if (cladatum) 634 default_range = cladatum->default_range; 635 } 636 637 switch (default_range) { 638 case DEFAULT_SOURCE_LOW: 639 return mls_context_cpy_low(newcontext, scontext); 640 case DEFAULT_SOURCE_HIGH: 641 return mls_context_cpy_high(newcontext, scontext); 642 case DEFAULT_SOURCE_LOW_HIGH: 643 return mls_context_cpy(newcontext, scontext); 644 case DEFAULT_TARGET_LOW: 645 return mls_context_cpy_low(newcontext, tcontext); 646 case DEFAULT_TARGET_HIGH: 647 return mls_context_cpy_high(newcontext, tcontext); 648 case DEFAULT_TARGET_LOW_HIGH: 649 return mls_context_cpy(newcontext, tcontext); 650 } 651 652 /* Fallthrough */ 653 case AVTAB_CHANGE: 654 if (tclass == SECCLASS_PROCESS) 655 /* Use the process MLS attributes. */ 656 return mls_copy_context(newcontext, scontext); 657 else 658 /* Use the process effective MLS attributes. */ 659 return mls_scopy_context(newcontext, scontext); 660 case AVTAB_MEMBER: 661 /* Use the process effective MLS attributes. */ 662 return mls_context_cpy_low(newcontext, scontext); 663 default: 664 return -EINVAL; 665 } 666 return -EINVAL; 667 } 668 669 int sepol_mls_contains(sepol_handle_t * handle, 670 sepol_policydb_t * policydb, 671 const char *mls1, const char *mls2, int *response) 672 { 673 674 context_struct_t *ctx1 = NULL, *ctx2 = NULL; 675 ctx1 = malloc(sizeof(context_struct_t)); 676 ctx2 = malloc(sizeof(context_struct_t)); 677 if (ctx1 == NULL || ctx2 == NULL) 678 goto omem; 679 context_init(ctx1); 680 context_init(ctx2); 681 682 if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0) 683 goto err; 684 685 if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0) 686 goto err; 687 688 *response = mls_range_contains(ctx1->range, ctx2->range); 689 context_destroy(ctx1); 690 context_destroy(ctx2); 691 free(ctx1); 692 free(ctx2); 693 return STATUS_SUCCESS; 694 695 omem: 696 ERR(handle, "out of memory"); 697 698 err: 699 ERR(handle, "could not check if mls context %s contains %s", 700 mls1, mls2); 701 context_destroy(ctx1); 702 context_destroy(ctx2); 703 free(ctx1); 704 free(ctx2); 705 return STATUS_ERR; 706 } 707 708 int sepol_mls_check(sepol_handle_t * handle, 709 sepol_policydb_t * policydb, const char *mls) 710 { 711 712 int ret; 713 context_struct_t *con = malloc(sizeof(context_struct_t)); 714 if (!con) { 715 ERR(handle, "out of memory, could not check if " 716 "mls context %s is valid", mls); 717 return STATUS_ERR; 718 } 719 context_init(con); 720 721 ret = mls_from_string(handle, &policydb->p, mls, con); 722 context_destroy(con); 723 free(con); 724 return ret; 725 } 726 727 void mls_semantic_cat_init(mls_semantic_cat_t * c) 728 { 729 memset(c, 0, sizeof(mls_semantic_cat_t)); 730 } 731 732 void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused))) 733 { 734 /* it's currently a simple struct - really nothing to destroy */ 735 return; 736 } 737 738 void mls_semantic_level_init(mls_semantic_level_t * l) 739 { 740 memset(l, 0, sizeof(mls_semantic_level_t)); 741 } 742 743 void mls_semantic_level_destroy(mls_semantic_level_t * l) 744 { 745 mls_semantic_cat_t *cur, *next; 746 747 if (l == NULL) 748 return; 749 750 next = l->cat; 751 while (next) { 752 cur = next; 753 next = cur->next; 754 mls_semantic_cat_destroy(cur); 755 free(cur); 756 } 757 } 758 759 int mls_semantic_level_cpy(mls_semantic_level_t * dst, 760 mls_semantic_level_t * src) 761 { 762 mls_semantic_cat_t *cat, *newcat, *lnewcat = NULL; 763 764 mls_semantic_level_init(dst); 765 dst->sens = src->sens; 766 cat = src->cat; 767 while (cat) { 768 newcat = 769 (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t)); 770 if (!newcat) 771 goto err; 772 773 mls_semantic_cat_init(newcat); 774 if (lnewcat) 775 lnewcat->next = newcat; 776 else 777 dst->cat = newcat; 778 779 newcat->low = cat->low; 780 newcat->high = cat->high; 781 782 lnewcat = newcat; 783 cat = cat->next; 784 } 785 return 0; 786 787 err: 788 mls_semantic_level_destroy(dst); 789 return -1; 790 } 791 792 void mls_semantic_range_init(mls_semantic_range_t * r) 793 { 794 mls_semantic_level_init(&r->level[0]); 795 mls_semantic_level_init(&r->level[1]); 796 } 797 798 void mls_semantic_range_destroy(mls_semantic_range_t * r) 799 { 800 mls_semantic_level_destroy(&r->level[0]); 801 mls_semantic_level_destroy(&r->level[1]); 802 } 803 804 int mls_semantic_range_cpy(mls_semantic_range_t * dst, 805 mls_semantic_range_t * src) 806 { 807 if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0) 808 return -1; 809 810 if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) { 811 mls_semantic_level_destroy(&dst->level[0]); 812 return -1; 813 } 814 815 return 0; 816 } 817