1/* Authors: Joshua Brindle <jbrindle@tresys.com> 2 * Jason Tang <jtang@tresys.com> 3 * 4 * Updates: KaiGai Kohei <kaigai@ak.jp.nec.com> 5 * adds checks based on newer boundary facility. 6 * 7 * A set of utility functions that aid policy decision when dealing 8 * with hierarchal namespaces. 9 * 10 * Copyright (C) 2005 Tresys Technology, LLC 11 * 12 * Copyright (c) 2008 NEC Corporation 13 * 14 * This library is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU Lesser General Public 16 * License as published by the Free Software Foundation; either 17 * version 2.1 of the License, or (at your option) any later version. 18 * 19 * This library is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 * Lesser General Public License for more details. 23 * 24 * You should have received a copy of the GNU Lesser General Public 25 * License along with this library; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 */ 28 29#include <string.h> 30#include <stdlib.h> 31#include <assert.h> 32#include <sepol/policydb/policydb.h> 33#include <sepol/policydb/conditional.h> 34#include <sepol/policydb/hierarchy.h> 35#include <sepol/policydb/expand.h> 36#include <sepol/policydb/util.h> 37 38#include "debug.h" 39 40#define BOUNDS_AVTAB_SIZE 1024 41 42static int bounds_insert_helper(sepol_handle_t *handle, avtab_t *avtab, 43 avtab_key_t *avtab_key, avtab_datum_t *datum) 44{ 45 int rc = avtab_insert(avtab, avtab_key, datum); 46 if (rc) { 47 if (rc == SEPOL_ENOMEM) 48 ERR(handle, "Insufficient memory"); 49 else 50 ERR(handle, "Unexpected error (%d)", rc); 51 } 52 return rc; 53} 54 55 56static int bounds_insert_rule(sepol_handle_t *handle, avtab_t *avtab, 57 avtab_t *global, avtab_t *other, 58 avtab_key_t *avtab_key, avtab_datum_t *datum) 59{ 60 int rc = 0; 61 avtab_datum_t *dup = avtab_search(avtab, avtab_key); 62 63 if (!dup) { 64 rc = bounds_insert_helper(handle, avtab, avtab_key, datum); 65 if (rc) goto exit; 66 } else { 67 dup->data |= datum->data; 68 } 69 70 if (other) { 71 /* Search the other conditional avtab for the key and 72 * add any common permissions to the global avtab 73 */ 74 uint32_t data = 0; 75 dup = avtab_search(other, avtab_key); 76 if (dup) { 77 data = dup->data & datum->data; 78 if (data) { 79 dup = avtab_search(global, avtab_key); 80 if (!dup) { 81 avtab_datum_t d; 82 d.data = data; 83 rc = bounds_insert_helper(handle, global, 84 avtab_key, &d); 85 if (rc) goto exit; 86 } else { 87 dup->data |= data; 88 } 89 } 90 } 91 } 92 93exit: 94 return rc; 95} 96 97static int bounds_expand_rule(sepol_handle_t *handle, policydb_t *p, 98 avtab_t *avtab, avtab_t *global, avtab_t *other, 99 uint32_t parent, uint32_t src, uint32_t tgt, 100 uint32_t class, uint32_t data) 101{ 102 int rc = 0; 103 avtab_key_t avtab_key; 104 avtab_datum_t datum; 105 ebitmap_node_t *tnode; 106 unsigned int i; 107 108 avtab_key.specified = AVTAB_ALLOWED; 109 avtab_key.target_class = class; 110 datum.data = data; 111 112 if (ebitmap_get_bit(&p->attr_type_map[src - 1], parent - 1)) { 113 avtab_key.source_type = parent; 114 ebitmap_for_each_positive_bit(&p->attr_type_map[tgt - 1], tnode, i) { 115 avtab_key.target_type = i + 1; 116 rc = bounds_insert_rule(handle, avtab, global, other, 117 &avtab_key, &datum); 118 if (rc) goto exit; 119 } 120 } 121 122exit: 123 return rc; 124} 125 126static int bounds_expand_cond_rules(sepol_handle_t *handle, policydb_t *p, 127 cond_av_list_t *cur, avtab_t *avtab, 128 avtab_t *global, avtab_t *other, 129 uint32_t parent) 130{ 131 int rc = 0; 132 133 for (; cur; cur = cur->next) { 134 avtab_ptr_t n = cur->node; 135 rc = bounds_expand_rule(handle, p, avtab, global, other, parent, 136 n->key.source_type, n->key.target_type, 137 n->key.target_class, n->datum.data); 138 if (rc) goto exit; 139 } 140 141exit: 142 return rc; 143} 144 145struct bounds_expand_args { 146 sepol_handle_t *handle; 147 policydb_t *p; 148 avtab_t *avtab; 149 uint32_t parent; 150}; 151 152static int bounds_expand_rule_callback(avtab_key_t *k, avtab_datum_t *d, 153 void *args) 154{ 155 struct bounds_expand_args *a = (struct bounds_expand_args *)args; 156 157 if (!(k->specified & AVTAB_ALLOWED)) 158 return 0; 159 160 return bounds_expand_rule(a->handle, a->p, a->avtab, NULL, NULL, 161 a->parent, k->source_type, k->target_type, 162 k->target_class, d->data); 163} 164 165struct bounds_cond_info { 166 avtab_t true_avtab; 167 avtab_t false_avtab; 168 cond_list_t *cond_list; 169 struct bounds_cond_info *next; 170}; 171 172static void bounds_destroy_cond_info(struct bounds_cond_info *cur) 173{ 174 struct bounds_cond_info *next; 175 176 for (; cur; cur = next) { 177 next = cur->next; 178 avtab_destroy(&cur->true_avtab); 179 avtab_destroy(&cur->false_avtab); 180 cur->next = NULL; 181 free(cur); 182 } 183} 184 185static int bounds_expand_parent_rules(sepol_handle_t *handle, policydb_t *p, 186 avtab_t *global_avtab, 187 struct bounds_cond_info **cond_info, 188 uint32_t parent) 189{ 190 int rc = 0; 191 struct bounds_expand_args args; 192 cond_list_t *cur; 193 194 avtab_init(global_avtab); 195 rc = avtab_alloc(global_avtab, BOUNDS_AVTAB_SIZE); 196 if (rc) goto oom; 197 198 args.handle = handle; 199 args.p = p; 200 args.avtab = global_avtab; 201 args.parent = parent; 202 rc = avtab_map(&p->te_avtab, bounds_expand_rule_callback, &args); 203 if (rc) goto exit; 204 205 *cond_info = NULL; 206 for (cur = p->cond_list; cur; cur = cur->next) { 207 struct bounds_cond_info *ci; 208 ci = malloc(sizeof(struct bounds_cond_info)); 209 if (!ci) goto oom; 210 avtab_init(&ci->true_avtab); 211 avtab_init(&ci->false_avtab); 212 ci->cond_list = cur; 213 ci->next = *cond_info; 214 *cond_info = ci; 215 if (cur->true_list) { 216 rc = avtab_alloc(&ci->true_avtab, BOUNDS_AVTAB_SIZE); 217 if (rc) goto oom; 218 rc = bounds_expand_cond_rules(handle, p, cur->true_list, 219 &ci->true_avtab, NULL, 220 NULL, parent); 221 if (rc) goto exit; 222 } 223 if (cur->false_list) { 224 rc = avtab_alloc(&ci->false_avtab, BOUNDS_AVTAB_SIZE); 225 if (rc) goto oom; 226 rc = bounds_expand_cond_rules(handle, p, cur->false_list, 227 &ci->false_avtab, 228 global_avtab, 229 &ci->true_avtab, parent); 230 if (rc) goto exit; 231 } 232 } 233 234 return 0; 235 236oom: 237 ERR(handle, "Insufficient memory"); 238 239exit: 240 ERR(handle,"Failed to expand parent rules"); 241 avtab_destroy(global_avtab); 242 bounds_destroy_cond_info(*cond_info); 243 *cond_info = NULL; 244 return rc; 245} 246 247static int bounds_not_covered(avtab_t *global_avtab, avtab_t *cur_avtab, 248 avtab_key_t *avtab_key, uint32_t data) 249{ 250 avtab_datum_t *datum = avtab_search(cur_avtab, avtab_key); 251 if (datum) 252 data &= ~datum->data; 253 if (global_avtab && data) { 254 datum = avtab_search(global_avtab, avtab_key); 255 if (datum) 256 data &= ~datum->data; 257 } 258 259 return data; 260} 261 262static int bounds_add_bad(sepol_handle_t *handle, uint32_t src, uint32_t tgt, 263 uint32_t class, uint32_t data, avtab_ptr_t *bad) 264{ 265 struct avtab_node *new = malloc(sizeof(struct avtab_node)); 266 if (new == NULL) { 267 ERR(handle, "Insufficient memory"); 268 return SEPOL_ENOMEM; 269 } 270 memset(new, 0, sizeof(struct avtab_node)); 271 new->key.source_type = src; 272 new->key.target_type = tgt; 273 new->key.target_class = class; 274 new->datum.data = data; 275 new->next = *bad; 276 *bad = new; 277 278 return 0; 279} 280 281static int bounds_check_rule(sepol_handle_t *handle, policydb_t *p, 282 avtab_t *global_avtab, avtab_t *cur_avtab, 283 uint32_t child, uint32_t parent, uint32_t src, 284 uint32_t tgt, uint32_t class, uint32_t data, 285 avtab_ptr_t *bad, int *numbad) 286{ 287 int rc = 0; 288 avtab_key_t avtab_key; 289 type_datum_t *td; 290 ebitmap_node_t *tnode; 291 unsigned int i; 292 uint32_t d; 293 294 avtab_key.specified = AVTAB_ALLOWED; 295 avtab_key.target_class = class; 296 297 if (ebitmap_get_bit(&p->attr_type_map[src - 1], child - 1)) { 298 avtab_key.source_type = parent; 299 ebitmap_for_each_positive_bit(&p->attr_type_map[tgt - 1], tnode, i) { 300 td = p->type_val_to_struct[i]; 301 if (td && td->bounds) { 302 avtab_key.target_type = td->bounds; 303 d = bounds_not_covered(global_avtab, cur_avtab, 304 &avtab_key, data); 305 } else { 306 avtab_key.target_type = i + 1; 307 d = bounds_not_covered(global_avtab, cur_avtab, 308 &avtab_key, data); 309 } 310 if (d) { 311 (*numbad)++; 312 rc = bounds_add_bad(handle, child, i+1, class, d, bad); 313 if (rc) goto exit; 314 } 315 } 316 } 317 318exit: 319 return rc; 320} 321 322static int bounds_check_cond_rules(sepol_handle_t *handle, policydb_t *p, 323 avtab_t *global_avtab, avtab_t *cond_avtab, 324 cond_av_list_t *rules, uint32_t child, 325 uint32_t parent, avtab_ptr_t *bad, 326 int *numbad) 327{ 328 int rc = 0; 329 cond_av_list_t *cur; 330 331 for (cur = rules; cur; cur = cur->next) { 332 avtab_ptr_t ap = cur->node; 333 avtab_key_t *key = &ap->key; 334 avtab_datum_t *datum = &ap->datum; 335 if (!(key->specified & AVTAB_ALLOWED)) 336 continue; 337 rc = bounds_check_rule(handle, p, global_avtab, cond_avtab, 338 child, parent, key->source_type, 339 key->target_type, key->target_class, 340 datum->data, bad, numbad); 341 if (rc) goto exit; 342 } 343 344exit: 345 return rc; 346} 347 348struct bounds_check_args { 349 sepol_handle_t *handle; 350 policydb_t *p; 351 avtab_t *cur_avtab; 352 uint32_t child; 353 uint32_t parent; 354 avtab_ptr_t bad; 355 int numbad; 356}; 357 358static int bounds_check_rule_callback(avtab_key_t *k, avtab_datum_t *d, 359 void *args) 360{ 361 struct bounds_check_args *a = (struct bounds_check_args *)args; 362 363 if (!(k->specified & AVTAB_ALLOWED)) 364 return 0; 365 366 return bounds_check_rule(a->handle, a->p, NULL, a->cur_avtab, a->child, 367 a->parent, k->source_type, k->target_type, 368 k->target_class, d->data, &a->bad, &a->numbad); 369} 370 371static int bounds_check_child_rules(sepol_handle_t *handle, policydb_t *p, 372 avtab_t *global_avtab, 373 struct bounds_cond_info *cond_info, 374 uint32_t child, uint32_t parent, 375 avtab_ptr_t *bad, int *numbad) 376{ 377 int rc; 378 struct bounds_check_args args; 379 struct bounds_cond_info *cur; 380 381 args.handle = handle; 382 args.p = p; 383 args.cur_avtab = global_avtab; 384 args.child = child; 385 args.parent = parent; 386 args.bad = NULL; 387 args.numbad = 0; 388 rc = avtab_map(&p->te_avtab, bounds_check_rule_callback, &args); 389 if (rc) goto exit; 390 391 for (cur = cond_info; cur; cur = cur->next) { 392 cond_list_t *node = cur->cond_list; 393 rc = bounds_check_cond_rules(handle, p, global_avtab, 394 &cur->true_avtab, 395 node->true_list, child, parent, 396 &args.bad, &args.numbad); 397 if (rc) goto exit; 398 399 rc = bounds_check_cond_rules(handle, p, global_avtab, 400 &cur->false_avtab, 401 node->false_list, child, parent, 402 &args.bad, &args.numbad); 403 if (rc) goto exit; 404 } 405 406 *numbad += args.numbad; 407 *bad = args.bad; 408 409exit: 410 return rc; 411} 412 413int bounds_check_type(sepol_handle_t *handle, policydb_t *p, uint32_t child, 414 uint32_t parent, avtab_ptr_t *bad, int *numbad) 415{ 416 int rc = 0; 417 avtab_t global_avtab; 418 struct bounds_cond_info *cond_info = NULL; 419 420 rc = bounds_expand_parent_rules(handle, p, &global_avtab, &cond_info, parent); 421 if (rc) goto exit; 422 423 rc = bounds_check_child_rules(handle, p, &global_avtab, cond_info, 424 child, parent, bad, numbad); 425 426 bounds_destroy_cond_info(cond_info); 427 avtab_destroy(&global_avtab); 428 429exit: 430 return rc; 431} 432 433struct bounds_args { 434 sepol_handle_t *handle; 435 policydb_t *p; 436 int numbad; 437}; 438 439static void bounds_report(sepol_handle_t *handle, policydb_t *p, uint32_t child, 440 uint32_t parent, avtab_ptr_t cur) 441{ 442 ERR(handle, "Child type %s exceeds bounds of parent %s in the following rules:", 443 p->p_type_val_to_name[child - 1], 444 p->p_type_val_to_name[parent - 1]); 445 for (; cur; cur = cur->next) { 446 ERR(handle, " %s %s : %s { %s }", 447 p->p_type_val_to_name[cur->key.source_type - 1], 448 p->p_type_val_to_name[cur->key.target_type - 1], 449 p->p_class_val_to_name[cur->key.target_class - 1], 450 sepol_av_to_string(p, cur->key.target_class, 451 cur->datum.data)); 452 } 453} 454 455void bounds_destroy_bad(avtab_ptr_t cur) 456{ 457 avtab_ptr_t next; 458 459 for (; cur; cur = next) { 460 next = cur->next; 461 cur->next = NULL; 462 free(cur); 463 } 464} 465 466static int bounds_check_type_callback(hashtab_key_t k __attribute__ ((unused)), 467 hashtab_datum_t d, void *args) 468{ 469 int rc = 0; 470 struct bounds_args *a = (struct bounds_args *)args; 471 type_datum_t *t = (type_datum_t *)d; 472 avtab_ptr_t bad = NULL; 473 474 if (t->bounds) { 475 rc = bounds_check_type(a->handle, a->p, t->s.value, t->bounds, 476 &bad, &a->numbad); 477 if (bad) { 478 bounds_report(a->handle, a->p, t->s.value, t->bounds, 479 bad); 480 bounds_destroy_bad(bad); 481 } 482 } 483 484 return rc; 485} 486 487int bounds_check_types(sepol_handle_t *handle, policydb_t *p) 488{ 489 int rc; 490 struct bounds_args args; 491 492 args.handle = handle; 493 args.p = p; 494 args.numbad = 0; 495 496 rc = hashtab_map(p->p_types.table, bounds_check_type_callback, &args); 497 if (rc) goto exit; 498 499 if (args.numbad > 0) { 500 ERR(handle, "%d errors found during type bounds check", 501 args.numbad); 502 rc = SEPOL_ERR; 503 } 504 505exit: 506 return rc; 507} 508 509/* The role bounds is defined as: a child role cannot have a type that 510 * its parent doesn't have. 511 */ 512static int bounds_check_role_callback(hashtab_key_t k, 513 hashtab_datum_t d, void *args) 514{ 515 struct bounds_args *a = (struct bounds_args *)args; 516 role_datum_t *r = (role_datum_t *) d; 517 role_datum_t *rp = NULL; 518 519 if (!r->bounds) 520 return 0; 521 522 rp = a->p->role_val_to_struct[r->bounds - 1]; 523 524 if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) { 525 ERR(a->handle, "Role bounds violation, %s exceeds %s", 526 (char *)k, a->p->p_role_val_to_name[rp->s.value - 1]); 527 a->numbad++; 528 } 529 530 return 0; 531} 532 533int bounds_check_roles(sepol_handle_t *handle, policydb_t *p) 534{ 535 struct bounds_args args; 536 537 args.handle = handle; 538 args.p = p; 539 args.numbad = 0; 540 541 hashtab_map(p->p_roles.table, bounds_check_role_callback, &args); 542 543 if (args.numbad > 0) { 544 ERR(handle, "%d errors found during role bounds check", 545 args.numbad); 546 return SEPOL_ERR; 547 } 548 549 return 0; 550} 551 552/* The user bounds is defined as: a child user cannot have a role that 553 * its parent doesn't have. 554 */ 555static int bounds_check_user_callback(hashtab_key_t k, 556 hashtab_datum_t d, void *args) 557{ 558 struct bounds_args *a = (struct bounds_args *)args; 559 user_datum_t *u = (user_datum_t *) d; 560 user_datum_t *up = NULL; 561 562 if (!u->bounds) 563 return 0; 564 565 up = a->p->user_val_to_struct[u->bounds - 1]; 566 567 if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) { 568 ERR(a->handle, "User bounds violation, %s exceeds %s", 569 (char *) k, a->p->p_user_val_to_name[up->s.value - 1]); 570 a->numbad++; 571 } 572 573 return 0; 574} 575 576int bounds_check_users(sepol_handle_t *handle, policydb_t *p) 577{ 578 struct bounds_args args; 579 580 args.handle = handle; 581 args.p = p; 582 args.numbad = 0; 583 584 hashtab_map(p->p_users.table, bounds_check_user_callback, &args); 585 586 if (args.numbad > 0) { 587 ERR(handle, "%d errors found during user bounds check", 588 args.numbad); 589 return SEPOL_ERR; 590 } 591 592 return 0; 593} 594 595#define add_hierarchy_callback_template(prefix) \ 596 int hierarchy_add_##prefix##_callback(hashtab_key_t k __attribute__ ((unused)), \ 597 hashtab_datum_t d, void *args) \ 598{ \ 599 struct bounds_args *a = (struct bounds_args *)args; \ 600 sepol_handle_t *handle = a->handle; \ 601 policydb_t *p = a->p; \ 602 prefix##_datum_t *datum = (prefix##_datum_t *)d; \ 603 prefix##_datum_t *parent; \ 604 char *parent_name, *datum_name, *tmp; \ 605 \ 606 if (!datum->bounds) { \ 607 datum_name = p->p_##prefix##_val_to_name[datum->s.value - 1]; \ 608 \ 609 tmp = strrchr(datum_name, '.'); \ 610 /* no '.' means it has no parent */ \ 611 if (!tmp) return 0; \ 612 \ 613 parent_name = strdup(datum_name); \ 614 if (!parent_name) { \ 615 ERR(handle, "Insufficient memory"); \ 616 return SEPOL_ENOMEM; \ 617 } \ 618 parent_name[tmp - datum_name] = '\0'; \ 619 \ 620 parent = hashtab_search(p->p_##prefix##s.table, parent_name); \ 621 if (!parent) { \ 622 /* Orphan type/role/user */ \ 623 ERR(handle, "%s doesn't exist, %s is an orphan",\ 624 parent_name, \ 625 p->p_##prefix##_val_to_name[datum->s.value - 1]); \ 626 free(parent_name); \ 627 a->numbad++; \ 628 return 0; \ 629 } \ 630 datum->bounds = parent->s.value; \ 631 free(parent_name); \ 632 } \ 633 \ 634 return 0; \ 635} \ 636 637static add_hierarchy_callback_template(type) 638static add_hierarchy_callback_template(role) 639static add_hierarchy_callback_template(user) 640 641int hierarchy_add_bounds(sepol_handle_t *handle, policydb_t *p) 642{ 643 int rc = 0; 644 struct bounds_args args; 645 646 args.handle = handle; 647 args.p = p; 648 args.numbad = 0; 649 650 rc = hashtab_map(p->p_users.table, hierarchy_add_user_callback, &args); 651 if (rc) goto exit; 652 653 rc = hashtab_map(p->p_roles.table, hierarchy_add_role_callback, &args); 654 if (rc) goto exit; 655 656 rc = hashtab_map(p->p_types.table, hierarchy_add_type_callback, &args); 657 if (rc) goto exit; 658 659 if (args.numbad > 0) { 660 ERR(handle, "%d errors found while adding hierarchies", 661 args.numbad); 662 rc = SEPOL_ERR; 663 } 664 665exit: 666 return rc; 667} 668 669int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p) 670{ 671 int rc = 0; 672 int violation = 0; 673 674 rc = hierarchy_add_bounds(handle, p); 675 if (rc) goto exit; 676 677 rc = bounds_check_users(handle, p); 678 if (rc) 679 violation = 1; 680 681 rc = bounds_check_roles(handle, p); 682 if (rc) 683 violation = 1; 684 685 rc = bounds_check_types(handle, p); 686 if (rc) { 687 if (rc == SEPOL_ERR) 688 violation = 1; 689 else 690 goto exit; 691 } 692 693 if (violation) 694 rc = SEPOL_ERR; 695 696exit: 697 return rc; 698} 699