1 /* 2 * Implementation of the multi-level security (MLS) policy. 3 * 4 * Author : Stephen Smalley, <sds@epoch.ncsc.mil> 5 */ 6 /* 7 * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> 8 * 9 * Support for enhanced MLS infrastructure. 10 * 11 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. 12 */ 13 14 #include <linux/kernel.h> 15 #include <linux/slab.h> 16 #include <linux/string.h> 17 #include <linux/errno.h> 18 #include "mls.h" 19 #include "policydb.h" 20 #include "services.h" 21 22 /* 23 * Return the length in bytes for the MLS fields of the 24 * security context string representation of `context'. 25 */ 26 int mls_compute_context_len(struct context * context) 27 { 28 int i, l, len, range; 29 30 if (!selinux_mls_enabled) 31 return 0; 32 33 len = 1; /* for the beginning ":" */ 34 for (l = 0; l < 2; l++) { 35 range = 0; 36 len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); 37 38 for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { 39 if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { 40 if (range) { 41 range++; 42 continue; 43 } 44 45 len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; 46 range++; 47 } else { 48 if (range > 1) 49 len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; 50 range = 0; 51 } 52 } 53 /* Handle case where last category is the end of range */ 54 if (range > 1) 55 len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; 56 57 if (l == 0) { 58 if (mls_level_eq(&context->range.level[0], 59 &context->range.level[1])) 60 break; 61 else 62 len++; 63 } 64 } 65 66 return len; 67 } 68 69 /* 70 * Write the security context string representation of 71 * the MLS fields of `context' into the string `*scontext'. 72 * Update `*scontext' to point to the end of the MLS fields. 73 */ 74 void mls_sid_to_context(struct context *context, 75 char **scontext) 76 { 77 char *scontextp; 78 int i, l, range, wrote_sep; 79 80 if (!selinux_mls_enabled) 81 return; 82 83 scontextp = *scontext; 84 85 *scontextp = ':'; 86 scontextp++; 87 88 for (l = 0; l < 2; l++) { 89 range = 0; 90 wrote_sep = 0; 91 strcpy(scontextp, 92 policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); 93 scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); 94 95 /* categories */ 96 for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { 97 if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { 98 if (range) { 99 range++; 100 continue; 101 } 102 103 if (!wrote_sep) { 104 *scontextp++ = ':'; 105 wrote_sep = 1; 106 } else 107 *scontextp++ = ','; 108 strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); 109 scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); 110 range++; 111 } else { 112 if (range > 1) { 113 if (range > 2) 114 *scontextp++ = '.'; 115 else 116 *scontextp++ = ','; 117 118 strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); 119 scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); 120 } 121 range = 0; 122 } 123 } 124 125 /* Handle case where last category is the end of range */ 126 if (range > 1) { 127 if (range > 2) 128 *scontextp++ = '.'; 129 else 130 *scontextp++ = ','; 131 132 strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); 133 scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); 134 } 135 136 if (l == 0) { 137 if (mls_level_eq(&context->range.level[0], 138 &context->range.level[1])) 139 break; 140 else { 141 *scontextp = '-'; 142 scontextp++; 143 } 144 } 145 } 146 147 *scontext = scontextp; 148 return; 149 } 150 151 /* 152 * Return 1 if the MLS fields in the security context 153 * structure `c' are valid. Return 0 otherwise. 154 */ 155 int mls_context_isvalid(struct policydb *p, struct context *c) 156 { 157 struct level_datum *levdatum; 158 struct user_datum *usrdatum; 159 int i, l; 160 161 if (!selinux_mls_enabled) 162 return 1; 163 164 /* 165 * MLS range validity checks: high must dominate low, low level must 166 * be valid (category set <-> sensitivity check), and high level must 167 * be valid (category set <-> sensitivity check) 168 */ 169 if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) 170 /* High does not dominate low. */ 171 return 0; 172 173 for (l = 0; l < 2; l++) { 174 if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) 175 return 0; 176 levdatum = hashtab_search(p->p_levels.table, 177 p->p_sens_val_to_name[c->range.level[l].sens - 1]); 178 if (!levdatum) 179 return 0; 180 181 for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { 182 if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { 183 if (i > p->p_cats.nprim) 184 return 0; 185 if (!ebitmap_get_bit(&levdatum->level->cat, i - 1)) 186 /* 187 * Category may not be associated with 188 * sensitivity in low level. 189 */ 190 return 0; 191 } 192 } 193 } 194 195 if (c->role == OBJECT_R_VAL) 196 return 1; 197 198 /* 199 * User must be authorized for the MLS range. 200 */ 201 if (!c->user || c->user > p->p_users.nprim) 202 return 0; 203 usrdatum = p->user_val_to_struct[c->user - 1]; 204 if (!mls_range_contains(usrdatum->range, c->range)) 205 return 0; /* user may not be associated with range */ 206 207 return 1; 208 } 209 210 /* 211 * Set the MLS fields in the security context structure 212 * `context' based on the string representation in 213 * the string `*scontext'. Update `*scontext' to 214 * point to the end of the string representation of 215 * the MLS fields. 216 * 217 * This function modifies the string in place, inserting 218 * NULL characters to terminate the MLS fields. 219 */ 220 int mls_context_to_sid(char oldc, 221 char **scontext, 222 struct context *context) 223 { 224 225 char delim; 226 char *scontextp, *p, *rngptr; 227 struct level_datum *levdatum; 228 struct cat_datum *catdatum, *rngdatum; 229 int l, rc = -EINVAL; 230 231 if (!selinux_mls_enabled) 232 return 0; 233 234 /* No MLS component to the security context. */ 235 if (!oldc) 236 goto out; 237 238 /* Extract low sensitivity. */ 239 scontextp = p = *scontext; 240 while (*p && *p != ':' && *p != '-') 241 p++; 242 243 delim = *p; 244 if (delim != 0) 245 *p++ = 0; 246 247 for (l = 0; l < 2; l++) { 248 levdatum = hashtab_search(policydb.p_levels.table, scontextp); 249 if (!levdatum) { 250 rc = -EINVAL; 251 goto out; 252 } 253 254 context->range.level[l].sens = levdatum->level->sens; 255 256 if (delim == ':') { 257 /* Extract category set. */ 258 while (1) { 259 scontextp = p; 260 while (*p && *p != ',' && *p != '-') 261 p++; 262 delim = *p; 263 if (delim != 0) 264 *p++ = 0; 265 266 /* Separate into range if exists */ 267 if ((rngptr = strchr(scontextp, '.')) != NULL) { 268 /* Remove '.' */ 269 *rngptr++ = 0; 270 } 271 272 catdatum = hashtab_search(policydb.p_cats.table, 273 scontextp); 274 if (!catdatum) { 275 rc = -EINVAL; 276 goto out; 277 } 278 279 rc = ebitmap_set_bit(&context->range.level[l].cat, 280 catdatum->value - 1, 1); 281 if (rc) 282 goto out; 283 284 /* If range, set all categories in range */ 285 if (rngptr) { 286 int i; 287 288 rngdatum = hashtab_search(policydb.p_cats.table, rngptr); 289 if (!rngdatum) { 290 rc = -EINVAL; 291 goto out; 292 } 293 294 if (catdatum->value >= rngdatum->value) { 295 rc = -EINVAL; 296 goto out; 297 } 298 299 for (i = catdatum->value; i < rngdatum->value; i++) { 300 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1); 301 if (rc) 302 goto out; 303 } 304 } 305 306 if (delim != ',') 307 break; 308 } 309 } 310 if (delim == '-') { 311 /* Extract high sensitivity. */ 312 scontextp = p; 313 while (*p && *p != ':') 314 p++; 315 316 delim = *p; 317 if (delim != 0) 318 *p++ = 0; 319 } else 320 break; 321 } 322 323 if (l == 0) { 324 context->range.level[1].sens = context->range.level[0].sens; 325 rc = ebitmap_cpy(&context->range.level[1].cat, 326 &context->range.level[0].cat); 327 if (rc) 328 goto out; 329 } 330 *scontext = ++p; 331 rc = 0; 332 out: 333 return rc; 334 } 335 336 /* 337 * Copies the MLS range from `src' into `dst'. 338 */ 339 static inline int mls_copy_context(struct context *dst, 340 struct context *src) 341 { 342 int l, rc = 0; 343 344 /* Copy the MLS range from the source context */ 345 for (l = 0; l < 2; l++) { 346 dst->range.level[l].sens = src->range.level[l].sens; 347 rc = ebitmap_cpy(&dst->range.level[l].cat, 348 &src->range.level[l].cat); 349 if (rc) 350 break; 351 } 352 353 return rc; 354 } 355 356 /* 357 * Copies the effective MLS range from `src' into `dst'. 358 */ 359 static inline int mls_scopy_context(struct context *dst, 360 struct context *src) 361 { 362 int l, rc = 0; 363 364 /* Copy the MLS range from the source context */ 365 for (l = 0; l < 2; l++) { 366 dst->range.level[l].sens = src->range.level[0].sens; 367 rc = ebitmap_cpy(&dst->range.level[l].cat, 368 &src->range.level[0].cat); 369 if (rc) 370 break; 371 } 372 373 return rc; 374 } 375 376 /* 377 * Copies the MLS range `range' into `context'. 378 */ 379 static inline int mls_range_set(struct context *context, 380 struct mls_range *range) 381 { 382 int l, rc = 0; 383 384 /* Copy the MLS range into the context */ 385 for (l = 0; l < 2; l++) { 386 context->range.level[l].sens = range->level[l].sens; 387 rc = ebitmap_cpy(&context->range.level[l].cat, 388 &range->level[l].cat); 389 if (rc) 390 break; 391 } 392 393 return rc; 394 } 395 396 int mls_setup_user_range(struct context *fromcon, struct user_datum *user, 397 struct context *usercon) 398 { 399 if (selinux_mls_enabled) { 400 struct mls_level *fromcon_sen = &(fromcon->range.level[0]); 401 struct mls_level *fromcon_clr = &(fromcon->range.level[1]); 402 struct mls_level *user_low = &(user->range.level[0]); 403 struct mls_level *user_clr = &(user->range.level[1]); 404 struct mls_level *user_def = &(user->dfltlevel); 405 struct mls_level *usercon_sen = &(usercon->range.level[0]); 406 struct mls_level *usercon_clr = &(usercon->range.level[1]); 407 408 /* Honor the user's default level if we can */ 409 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { 410 *usercon_sen = *user_def; 411 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { 412 *usercon_sen = *fromcon_sen; 413 } else if (mls_level_between(fromcon_clr, user_low, user_def)) { 414 *usercon_sen = *user_low; 415 } else 416 return -EINVAL; 417 418 /* Lower the clearance of available contexts 419 if the clearance of "fromcon" is lower than 420 that of the user's default clearance (but 421 only if the "fromcon" clearance dominates 422 the user's computed sensitivity level) */ 423 if (mls_level_dom(user_clr, fromcon_clr)) { 424 *usercon_clr = *fromcon_clr; 425 } else if (mls_level_dom(fromcon_clr, user_clr)) { 426 *usercon_clr = *user_clr; 427 } else 428 return -EINVAL; 429 } 430 431 return 0; 432 } 433 434 /* 435 * Convert the MLS fields in the security context 436 * structure `c' from the values specified in the 437 * policy `oldp' to the values specified in the policy `newp'. 438 */ 439 int mls_convert_context(struct policydb *oldp, 440 struct policydb *newp, 441 struct context *c) 442 { 443 struct level_datum *levdatum; 444 struct cat_datum *catdatum; 445 struct ebitmap bitmap; 446 int l, i; 447 448 if (!selinux_mls_enabled) 449 return 0; 450 451 for (l = 0; l < 2; l++) { 452 levdatum = hashtab_search(newp->p_levels.table, 453 oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); 454 455 if (!levdatum) 456 return -EINVAL; 457 c->range.level[l].sens = levdatum->level->sens; 458 459 ebitmap_init(&bitmap); 460 for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { 461 if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { 462 int rc; 463 464 catdatum = hashtab_search(newp->p_cats.table, 465 oldp->p_cat_val_to_name[i - 1]); 466 if (!catdatum) 467 return -EINVAL; 468 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); 469 if (rc) 470 return rc; 471 } 472 } 473 ebitmap_destroy(&c->range.level[l].cat); 474 c->range.level[l].cat = bitmap; 475 } 476 477 return 0; 478 } 479 480 int mls_compute_sid(struct context *scontext, 481 struct context *tcontext, 482 u16 tclass, 483 u32 specified, 484 struct context *newcontext) 485 { 486 if (!selinux_mls_enabled) 487 return 0; 488 489 switch (specified) { 490 case AVTAB_TRANSITION: 491 if (tclass == SECCLASS_PROCESS) { 492 struct range_trans *rangetr; 493 /* Look for a range transition rule. */ 494 for (rangetr = policydb.range_tr; rangetr; 495 rangetr = rangetr->next) { 496 if (rangetr->dom == scontext->type && 497 rangetr->type == tcontext->type) { 498 /* Set the range from the rule */ 499 return mls_range_set(newcontext, 500 &rangetr->range); 501 } 502 } 503 } 504 /* Fallthrough */ 505 case AVTAB_CHANGE: 506 if (tclass == SECCLASS_PROCESS) 507 /* Use the process MLS attributes. */ 508 return mls_copy_context(newcontext, scontext); 509 else 510 /* Use the process effective MLS attributes. */ 511 return mls_scopy_context(newcontext, scontext); 512 case AVTAB_MEMBER: 513 /* Only polyinstantiate the MLS attributes if 514 the type is being polyinstantiated */ 515 if (newcontext->type != tcontext->type) { 516 /* Use the process effective MLS attributes. */ 517 return mls_scopy_context(newcontext, scontext); 518 } else { 519 /* Use the related object MLS attributes. */ 520 return mls_copy_context(newcontext, tcontext); 521 } 522 default: 523 return -EINVAL; 524 } 525 return -EINVAL; 526 } 527 528