1 /* Authors: Joshua Brindle <jbrindle (at) tresys.com> 2 * Jason Tang <jtang (at) tresys.com> 3 * 4 * Updates: KaiGai Kohei <kaigai (at) 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 typedef struct hierarchy_args { 41 policydb_t *p; 42 avtab_t *expa; /* expanded avtab */ 43 /* This tells check_avtab_hierarchy to check this list in addition to the unconditional avtab */ 44 cond_av_list_t *opt_cond_list; 45 sepol_handle_t *handle; 46 int numerr; 47 } hierarchy_args_t; 48 49 /* 50 * find_parent_(type|role|user) 51 * 52 * This function returns the parent datum of given XXX_datum_t 53 * object or NULL, if it doesn't exist. 54 * 55 * If the given datum has a valid bounds, this function merely 56 * returns the indicated object. Otherwise, it looks up the 57 * parent based on the based hierarchy. 58 */ 59 #define find_parent_template(prefix) \ 60 int find_parent_##prefix(hierarchy_args_t *a, \ 61 prefix##_datum_t *datum, \ 62 prefix##_datum_t **parent) \ 63 { \ 64 char *parent_name, *datum_name, *tmp; \ 65 \ 66 if (datum->bounds) \ 67 *parent = a->p->prefix##_val_to_struct[datum->bounds - 1]; \ 68 else { \ 69 datum_name = a->p->p_##prefix##_val_to_name[datum->s.value - 1]; \ 70 \ 71 tmp = strrchr(datum_name, '.'); \ 72 /* no '.' means it has no parent */ \ 73 if (!tmp) { \ 74 *parent = NULL; \ 75 return 0; \ 76 } \ 77 \ 78 parent_name = strdup(datum_name); \ 79 if (!parent_name) \ 80 return -1; \ 81 parent_name[tmp - datum_name] = '\0'; \ 82 \ 83 *parent = hashtab_search(a->p->p_##prefix##s.table, parent_name); \ 84 if (!*parent) { \ 85 /* Orphan type/role/user */ \ 86 ERR(a->handle, \ 87 "%s doesn't exist, %s is an orphan", \ 88 parent_name, \ 89 a->p->p_##prefix##_val_to_name[datum->s.value - 1]); \ 90 free(parent_name); \ 91 return -1; \ 92 } \ 93 free(parent_name); \ 94 } \ 95 \ 96 return 0; \ 97 } 98 99 static find_parent_template(type) 100 static find_parent_template(role) 101 static find_parent_template(user) 102 103 static void compute_avtab_datum(hierarchy_args_t *args, 104 avtab_key_t *key, 105 avtab_datum_t *result) 106 { 107 avtab_datum_t *avdatp; 108 uint32_t av = 0; 109 110 avdatp = avtab_search(args->expa, key); 111 if (avdatp) 112 av = avdatp->data; 113 if (args->opt_cond_list) { 114 avdatp = cond_av_list_search(key, args->opt_cond_list); 115 if (avdatp) 116 av |= avdatp->data; 117 } 118 119 result->data = av; 120 } 121 122 /* This function verifies that the type passed in either has a parent or is in the 123 * root of the namespace, 0 on success, 1 on orphan and -1 on error 124 */ 125 static int check_type_hierarchy_callback(hashtab_key_t k, hashtab_datum_t d, 126 void *args) 127 { 128 hierarchy_args_t *a; 129 type_datum_t *t, *tp; 130 131 a = (hierarchy_args_t *) args; 132 t = (type_datum_t *) d; 133 134 if (t->flavor == TYPE_ATTRIB) { 135 /* It's an attribute, we don't care */ 136 return 0; 137 } 138 if (find_parent_type(a, t, &tp) < 0) 139 return -1; 140 141 if (tp && tp->flavor == TYPE_ATTRIB) { 142 /* The parent is an attribute but the child isn't, not legal */ 143 ERR(a->handle, "type %s is a child of an attribute %s", 144 (char *) k, a->p->p_type_val_to_name[tp->s.value - 1]); 145 a->numerr++; 146 return -1; 147 } 148 return 0; 149 } 150 151 /* This function only verifies that the avtab node passed in does not violate any 152 * hiearchy constraint via any relationship with other types in the avtab. 153 * it should be called using avtab_map, returns 0 on success, 1 on violation and 154 * -1 on error. opt_cond_list is an optional argument that tells this to check 155 * a conditional list for the relationship as well as the unconditional avtab 156 */ 157 static int check_avtab_hierarchy_callback(avtab_key_t * k, avtab_datum_t * d, 158 void *args) 159 { 160 avtab_key_t key; 161 hierarchy_args_t *a = (hierarchy_args_t *) args; 162 type_datum_t *s, *t1 = NULL, *t2 = NULL; 163 avtab_datum_t av; 164 165 if (!(k->specified & AVTAB_ALLOWED)) { 166 /* This is not an allow rule, no checking done */ 167 return 0; 168 } 169 170 /* search for parent first */ 171 s = a->p->type_val_to_struct[k->source_type - 1]; 172 if (find_parent_type(a, s, &t1) < 0) 173 return -1; 174 if (t1) { 175 /* 176 * search for access allowed between type 1's 177 * parent and type 2. 178 */ 179 key.source_type = t1->s.value; 180 key.target_type = k->target_type; 181 key.target_class = k->target_class; 182 key.specified = AVTAB_ALLOWED; 183 compute_avtab_datum(a, &key, &av); 184 185 if ((av.data & d->data) == d->data) 186 return 0; 187 } 188 189 /* next we try type 1 and type 2's parent */ 190 s = a->p->type_val_to_struct[k->target_type - 1]; 191 if (find_parent_type(a, s, &t2) < 0) 192 return -1; 193 if (t2) { 194 /* 195 * search for access allowed between type 1 and 196 * type 2's parent. 197 */ 198 key.source_type = k->source_type; 199 key.target_type = t2->s.value; 200 key.target_class = k->target_class; 201 key.specified = AVTAB_ALLOWED; 202 compute_avtab_datum(a, &key, &av); 203 204 if ((av.data & d->data) == d->data) 205 return 0; 206 } 207 208 if (t1 && t2) { 209 /* 210 * search for access allowed between type 1's parent 211 * and type 2's parent. 212 */ 213 key.source_type = t1->s.value; 214 key.target_type = t2->s.value; 215 key.target_class = k->target_class; 216 key.specified = AVTAB_ALLOWED; 217 compute_avtab_datum(a, &key, &av); 218 219 if ((av.data & d->data) == d->data) 220 return 0; 221 } 222 223 /* 224 * Neither one of these types have parents and 225 * therefore the hierarchical constraint does not apply 226 */ 227 if (!t1 && !t2) 228 return 0; 229 230 /* 231 * At this point there is a violation of the hierarchal 232 * constraint, send error condition back 233 */ 234 ERR(a->handle, 235 "hierarchy violation between types %s and %s : %s { %s }", 236 a->p->p_type_val_to_name[k->source_type - 1], 237 a->p->p_type_val_to_name[k->target_type - 1], 238 a->p->p_class_val_to_name[k->target_class - 1], 239 sepol_av_to_string(a->p, k->target_class, d->data & ~av.data)); 240 a->numerr++; 241 return 0; 242 } 243 244 /* 245 * If same permissions are allowed for same combination of 246 * source and target, we can evaluate them as unconditional 247 * one. 248 * See the following example. A_t type is bounds of B_t type, 249 * so B_t can never have wider permissions then A_t. 250 * A_t has conditional permission on X_t, however, a part of 251 * them (getattr and read) are unconditionaly allowed to A_t. 252 * 253 * Example) 254 * typebounds A_t B_t; 255 * 256 * allow B_t X_t : file { getattr }; 257 * if (foo_bool) { 258 * allow A_t X_t : file { getattr read }; 259 * } else { 260 * allow A_t X_t : file { getattr read write }; 261 * } 262 * 263 * We have to pull up them as unconditional ones in this case, 264 * because it seems to us B_t is violated to bounds constraints 265 * during unconditional policy checking. 266 */ 267 static int pullup_unconditional_perms(cond_list_t * cond_list, 268 hierarchy_args_t * args) 269 { 270 cond_list_t *cur_node; 271 cond_av_list_t *cur_av, *expl_true = NULL, *expl_false = NULL; 272 avtab_t expa_true, expa_false; 273 avtab_datum_t *avdatp; 274 avtab_datum_t avdat; 275 avtab_ptr_t avnode; 276 277 for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { 278 if (avtab_init(&expa_true)) 279 goto oom0; 280 if (avtab_init(&expa_false)) 281 goto oom1; 282 if (expand_cond_av_list(args->p, cur_node->true_list, 283 &expl_true, &expa_true)) 284 goto oom2; 285 if (expand_cond_av_list(args->p, cur_node->false_list, 286 &expl_false, &expa_false)) 287 goto oom3; 288 for (cur_av = expl_true; cur_av; cur_av = cur_av->next) { 289 avdatp = avtab_search(&expa_false, 290 &cur_av->node->key); 291 if (!avdatp) 292 continue; 293 294 avdat.data = (cur_av->node->datum.data 295 & avdatp->data); 296 if (!avdat.data) 297 continue; 298 299 avnode = avtab_search_node(args->expa, 300 &cur_av->node->key); 301 if (avnode) { 302 avnode->datum.data |= avdat.data; 303 } else { 304 if (avtab_insert(args->expa, 305 &cur_av->node->key, 306 &avdat)) 307 goto oom4; 308 } 309 } 310 cond_av_list_destroy(expl_false); 311 cond_av_list_destroy(expl_true); 312 avtab_destroy(&expa_false); 313 avtab_destroy(&expa_true); 314 } 315 return 0; 316 317 oom4: 318 cond_av_list_destroy(expl_false); 319 oom3: 320 cond_av_list_destroy(expl_true); 321 oom2: 322 avtab_destroy(&expa_false); 323 oom1: 324 avtab_destroy(&expa_true); 325 oom0: 326 ERR(args->handle, "out of memory on conditional av list expansion"); 327 return 1; 328 } 329 330 static int check_cond_avtab_hierarchy(cond_list_t * cond_list, 331 hierarchy_args_t * args) 332 { 333 int rc; 334 cond_list_t *cur_node; 335 cond_av_list_t *cur_av, *expl = NULL; 336 avtab_t expa; 337 hierarchy_args_t *a = (hierarchy_args_t *) args; 338 avtab_datum_t avdat, *uncond; 339 340 for (cur_node = cond_list; cur_node; cur_node = cur_node->next) { 341 /* 342 * Check true condition 343 */ 344 if (avtab_init(&expa)) 345 goto oom; 346 if (expand_cond_av_list(args->p, cur_node->true_list, 347 &expl, &expa)) { 348 avtab_destroy(&expa); 349 goto oom; 350 } 351 args->opt_cond_list = expl; 352 for (cur_av = expl; cur_av; cur_av = cur_av->next) { 353 avdat.data = cur_av->node->datum.data; 354 uncond = avtab_search(a->expa, &cur_av->node->key); 355 if (uncond) 356 avdat.data |= uncond->data; 357 rc = check_avtab_hierarchy_callback(&cur_av->node->key, 358 &avdat, args); 359 if (rc) 360 args->numerr++; 361 } 362 cond_av_list_destroy(expl); 363 avtab_destroy(&expa); 364 365 /* 366 * Check false condition 367 */ 368 if (avtab_init(&expa)) 369 goto oom; 370 if (expand_cond_av_list(args->p, cur_node->false_list, 371 &expl, &expa)) { 372 avtab_destroy(&expa); 373 goto oom; 374 } 375 args->opt_cond_list = expl; 376 for (cur_av = expl; cur_av; cur_av = cur_av->next) { 377 avdat.data = cur_av->node->datum.data; 378 uncond = avtab_search(a->expa, &cur_av->node->key); 379 if (uncond) 380 avdat.data |= uncond->data; 381 382 rc = check_avtab_hierarchy_callback(&cur_av->node->key, 383 &avdat, args); 384 if (rc) 385 a->numerr++; 386 } 387 cond_av_list_destroy(expl); 388 avtab_destroy(&expa); 389 } 390 391 return 0; 392 393 oom: 394 ERR(args->handle, "out of memory on conditional av list expansion"); 395 return 1; 396 } 397 398 /* The role hierarchy is defined as: a child role cannot have more types than it's parent. 399 * This function should be called with hashtab_map, it will return 0 on success, 1 on 400 * constraint violation and -1 on error 401 */ 402 static int check_role_hierarchy_callback(hashtab_key_t k 403 __attribute__ ((unused)), 404 hashtab_datum_t d, void *args) 405 { 406 hierarchy_args_t *a; 407 role_datum_t *r, *rp; 408 409 a = (hierarchy_args_t *) args; 410 r = (role_datum_t *) d; 411 412 if (find_parent_role(a, r, &rp) < 0) 413 return -1; 414 415 if (rp && !ebitmap_contains(&rp->types.types, &r->types.types)) { 416 /* hierarchical constraint violation, return error */ 417 ERR(a->handle, "Role hierarchy violation, %s exceeds %s", 418 (char *) k, a->p->p_role_val_to_name[rp->s.value - 1]); 419 a->numerr++; 420 } 421 return 0; 422 } 423 424 /* The user hierarchy is defined as: a child user cannot have a role that 425 * its parent doesn't have. This function should be called with hashtab_map, 426 * it will return 0 on success, 1 on constraint violation and -1 on error. 427 */ 428 static int check_user_hierarchy_callback(hashtab_key_t k 429 __attribute__ ((unused)), 430 hashtab_datum_t d, void *args) 431 { 432 hierarchy_args_t *a; 433 user_datum_t *u, *up; 434 435 a = (hierarchy_args_t *) args; 436 u = (user_datum_t *) d; 437 438 if (find_parent_user(a, u, &up) < 0) 439 return -1; 440 441 if (up && !ebitmap_contains(&up->roles.roles, &u->roles.roles)) { 442 /* hierarchical constraint violation, return error */ 443 ERR(a->handle, "User hierarchy violation, %s exceeds %s", 444 (char *) k, a->p->p_user_val_to_name[up->s.value - 1]); 445 a->numerr++; 446 } 447 return 0; 448 } 449 450 int hierarchy_check_constraints(sepol_handle_t * handle, policydb_t * p) 451 { 452 hierarchy_args_t args; 453 avtab_t expa; 454 455 if (avtab_init(&expa)) 456 goto oom; 457 if (expand_avtab(p, &p->te_avtab, &expa)) { 458 avtab_destroy(&expa); 459 goto oom; 460 } 461 462 args.p = p; 463 args.expa = &expa; 464 args.opt_cond_list = NULL; 465 args.handle = handle; 466 args.numerr = 0; 467 468 if (hashtab_map(p->p_types.table, check_type_hierarchy_callback, &args)) 469 goto bad; 470 471 if (pullup_unconditional_perms(p->cond_list, &args)) 472 return -1; 473 474 if (avtab_map(&expa, check_avtab_hierarchy_callback, &args)) 475 goto bad; 476 477 if (check_cond_avtab_hierarchy(p->cond_list, &args)) 478 goto bad; 479 480 if (hashtab_map(p->p_roles.table, check_role_hierarchy_callback, &args)) 481 goto bad; 482 483 if (hashtab_map(p->p_users.table, check_user_hierarchy_callback, &args)) 484 goto bad; 485 486 if (args.numerr) { 487 ERR(handle, "%d total errors found during hierarchy check", 488 args.numerr); 489 goto bad; 490 } 491 492 avtab_destroy(&expa); 493 return 0; 494 495 bad: 496 avtab_destroy(&expa); 497 return -1; 498 499 oom: 500 ERR(handle, "Out of memory"); 501 return -1; 502 } 503