diff options
Diffstat (limited to 'security/selinux/ss/services.c')
| -rw-r--r-- | security/selinux/ss/services.c | 197 |
1 files changed, 182 insertions, 15 deletions
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b52f923ce680..ab0cc0c7b944 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
| @@ -88,6 +88,11 @@ static u32 latest_granting; | |||
| 88 | static int context_struct_to_string(struct context *context, char **scontext, | 88 | static int context_struct_to_string(struct context *context, char **scontext, |
| 89 | u32 *scontext_len); | 89 | u32 *scontext_len); |
| 90 | 90 | ||
| 91 | static int context_struct_compute_av(struct context *scontext, | ||
| 92 | struct context *tcontext, | ||
| 93 | u16 tclass, | ||
| 94 | u32 requested, | ||
| 95 | struct av_decision *avd); | ||
| 91 | /* | 96 | /* |
| 92 | * Return the boolean value of a constraint expression | 97 | * Return the boolean value of a constraint expression |
| 93 | * when it is applied to the specified source and target | 98 | * when it is applied to the specified source and target |
| @@ -274,6 +279,100 @@ mls_ops: | |||
| 274 | } | 279 | } |
| 275 | 280 | ||
| 276 | /* | 281 | /* |
| 282 | * security_boundary_permission - drops violated permissions | ||
| 283 | * on boundary constraint. | ||
| 284 | */ | ||
| 285 | static void type_attribute_bounds_av(struct context *scontext, | ||
| 286 | struct context *tcontext, | ||
| 287 | u16 tclass, | ||
| 288 | u32 requested, | ||
| 289 | struct av_decision *avd) | ||
| 290 | { | ||
| 291 | struct context lo_scontext; | ||
| 292 | struct context lo_tcontext; | ||
| 293 | struct av_decision lo_avd; | ||
| 294 | struct type_datum *source | ||
| 295 | = policydb.type_val_to_struct[scontext->type - 1]; | ||
| 296 | struct type_datum *target | ||
| 297 | = policydb.type_val_to_struct[tcontext->type - 1]; | ||
| 298 | u32 masked = 0; | ||
| 299 | |||
| 300 | if (source->bounds) { | ||
| 301 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
| 302 | |||
| 303 | memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); | ||
| 304 | lo_scontext.type = source->bounds; | ||
| 305 | |||
| 306 | context_struct_compute_av(&lo_scontext, | ||
| 307 | tcontext, | ||
| 308 | tclass, | ||
| 309 | requested, | ||
| 310 | &lo_avd); | ||
| 311 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
| 312 | return; /* no masked permission */ | ||
| 313 | masked = ~lo_avd.allowed & avd->allowed; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (target->bounds) { | ||
| 317 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
| 318 | |||
| 319 | memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); | ||
| 320 | lo_tcontext.type = target->bounds; | ||
| 321 | |||
| 322 | context_struct_compute_av(scontext, | ||
| 323 | &lo_tcontext, | ||
| 324 | tclass, | ||
| 325 | requested, | ||
| 326 | &lo_avd); | ||
| 327 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
| 328 | return; /* no masked permission */ | ||
| 329 | masked = ~lo_avd.allowed & avd->allowed; | ||
| 330 | } | ||
| 331 | |||
| 332 | if (source->bounds && target->bounds) { | ||
| 333 | memset(&lo_avd, 0, sizeof(lo_avd)); | ||
| 334 | /* | ||
| 335 | * lo_scontext and lo_tcontext are already | ||
| 336 | * set up. | ||
| 337 | */ | ||
| 338 | |||
| 339 | context_struct_compute_av(&lo_scontext, | ||
| 340 | &lo_tcontext, | ||
| 341 | tclass, | ||
| 342 | requested, | ||
| 343 | &lo_avd); | ||
| 344 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | ||
| 345 | return; /* no masked permission */ | ||
| 346 | masked = ~lo_avd.allowed & avd->allowed; | ||
| 347 | } | ||
| 348 | |||
| 349 | if (masked) { | ||
| 350 | struct audit_buffer *ab; | ||
| 351 | char *stype_name | ||
| 352 | = policydb.p_type_val_to_name[source->value - 1]; | ||
| 353 | char *ttype_name | ||
| 354 | = policydb.p_type_val_to_name[target->value - 1]; | ||
| 355 | char *tclass_name | ||
| 356 | = policydb.p_class_val_to_name[tclass - 1]; | ||
| 357 | |||
| 358 | /* mask violated permissions */ | ||
| 359 | avd->allowed &= ~masked; | ||
| 360 | |||
| 361 | /* notice to userspace via audit message */ | ||
| 362 | ab = audit_log_start(current->audit_context, | ||
| 363 | GFP_ATOMIC, AUDIT_SELINUX_ERR); | ||
| 364 | if (!ab) | ||
| 365 | return; | ||
| 366 | |||
| 367 | audit_log_format(ab, "av boundary violation: " | ||
| 368 | "source=%s target=%s tclass=%s", | ||
| 369 | stype_name, ttype_name, tclass_name); | ||
| 370 | avc_dump_av(ab, tclass, masked); | ||
| 371 | audit_log_end(ab); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | /* | ||
| 277 | * Compute access vectors based on a context structure pair for | 376 | * Compute access vectors based on a context structure pair for |
| 278 | * the permissions in a particular class. | 377 | * the permissions in a particular class. |
| 279 | */ | 378 | */ |
| @@ -356,7 +455,7 @@ static int context_struct_compute_av(struct context *scontext, | |||
| 356 | avkey.source_type = i + 1; | 455 | avkey.source_type = i + 1; |
| 357 | avkey.target_type = j + 1; | 456 | avkey.target_type = j + 1; |
| 358 | for (node = avtab_search_node(&policydb.te_avtab, &avkey); | 457 | for (node = avtab_search_node(&policydb.te_avtab, &avkey); |
| 359 | node != NULL; | 458 | node; |
| 360 | node = avtab_search_node_next(node, avkey.specified)) { | 459 | node = avtab_search_node_next(node, avkey.specified)) { |
| 361 | if (node->key.specified == AVTAB_ALLOWED) | 460 | if (node->key.specified == AVTAB_ALLOWED) |
| 362 | avd->allowed |= node->datum.data; | 461 | avd->allowed |= node->datum.data; |
| @@ -404,6 +503,14 @@ static int context_struct_compute_av(struct context *scontext, | |||
| 404 | PROCESS__DYNTRANSITION); | 503 | PROCESS__DYNTRANSITION); |
| 405 | } | 504 | } |
| 406 | 505 | ||
| 506 | /* | ||
| 507 | * If the given source and target types have boundary | ||
| 508 | * constraint, lazy checks have to mask any violated | ||
| 509 | * permission and notice it to userspace via audit. | ||
| 510 | */ | ||
| 511 | type_attribute_bounds_av(scontext, tcontext, | ||
| 512 | tclass, requested, avd); | ||
| 513 | |||
| 407 | return 0; | 514 | return 0; |
| 408 | 515 | ||
| 409 | inval_class: | 516 | inval_class: |
| @@ -549,6 +656,69 @@ out: | |||
| 549 | return rc; | 656 | return rc; |
| 550 | } | 657 | } |
| 551 | 658 | ||
| 659 | /* | ||
| 660 | * security_bounded_transition - check whether the given | ||
| 661 | * transition is directed to bounded, or not. | ||
| 662 | * It returns 0, if @newsid is bounded by @oldsid. | ||
| 663 | * Otherwise, it returns error code. | ||
| 664 | * | ||
| 665 | * @oldsid : current security identifier | ||
| 666 | * @newsid : destinated security identifier | ||
| 667 | */ | ||
| 668 | int security_bounded_transition(u32 old_sid, u32 new_sid) | ||
| 669 | { | ||
| 670 | struct context *old_context, *new_context; | ||
| 671 | struct type_datum *type; | ||
| 672 | int index; | ||
| 673 | int rc = -EINVAL; | ||
| 674 | |||
| 675 | read_lock(&policy_rwlock); | ||
| 676 | |||
| 677 | old_context = sidtab_search(&sidtab, old_sid); | ||
| 678 | if (!old_context) { | ||
| 679 | printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", | ||
| 680 | __func__, old_sid); | ||
| 681 | goto out; | ||
| 682 | } | ||
| 683 | |||
| 684 | new_context = sidtab_search(&sidtab, new_sid); | ||
| 685 | if (!new_context) { | ||
| 686 | printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n", | ||
| 687 | __func__, new_sid); | ||
| 688 | goto out; | ||
| 689 | } | ||
| 690 | |||
| 691 | /* type/domain unchaned */ | ||
| 692 | if (old_context->type == new_context->type) { | ||
| 693 | rc = 0; | ||
| 694 | goto out; | ||
| 695 | } | ||
| 696 | |||
| 697 | index = new_context->type; | ||
| 698 | while (true) { | ||
| 699 | type = policydb.type_val_to_struct[index - 1]; | ||
| 700 | BUG_ON(!type); | ||
| 701 | |||
| 702 | /* not bounded anymore */ | ||
| 703 | if (!type->bounds) { | ||
| 704 | rc = -EPERM; | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | |||
| 708 | /* @newsid is bounded by @oldsid */ | ||
| 709 | if (type->bounds == old_context->type) { | ||
| 710 | rc = 0; | ||
| 711 | break; | ||
| 712 | } | ||
| 713 | index = type->bounds; | ||
| 714 | } | ||
| 715 | out: | ||
| 716 | read_unlock(&policy_rwlock); | ||
| 717 | |||
| 718 | return rc; | ||
| 719 | } | ||
| 720 | |||
| 721 | |||
| 552 | /** | 722 | /** |
| 553 | * security_compute_av - Compute access vector decisions. | 723 | * security_compute_av - Compute access vector decisions. |
| 554 | * @ssid: source security identifier | 724 | * @ssid: source security identifier |
| @@ -794,7 +964,7 @@ static int string_to_context_struct(struct policydb *pol, | |||
| 794 | *p++ = 0; | 964 | *p++ = 0; |
| 795 | 965 | ||
| 796 | typdatum = hashtab_search(pol->p_types.table, scontextp); | 966 | typdatum = hashtab_search(pol->p_types.table, scontextp); |
| 797 | if (!typdatum) | 967 | if (!typdatum || typdatum->attribute) |
| 798 | goto out; | 968 | goto out; |
| 799 | 969 | ||
| 800 | ctx->type = typdatum->value; | 970 | ctx->type = typdatum->value; |
| @@ -811,11 +981,12 @@ static int string_to_context_struct(struct policydb *pol, | |||
| 811 | /* Check the validity of the new context. */ | 981 | /* Check the validity of the new context. */ |
| 812 | if (!policydb_context_isvalid(pol, ctx)) { | 982 | if (!policydb_context_isvalid(pol, ctx)) { |
| 813 | rc = -EINVAL; | 983 | rc = -EINVAL; |
| 814 | context_destroy(ctx); | ||
| 815 | goto out; | 984 | goto out; |
| 816 | } | 985 | } |
| 817 | rc = 0; | 986 | rc = 0; |
| 818 | out: | 987 | out: |
| 988 | if (rc) | ||
| 989 | context_destroy(ctx); | ||
| 819 | return rc; | 990 | return rc; |
| 820 | } | 991 | } |
| 821 | 992 | ||
| @@ -868,8 +1039,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, | |||
| 868 | } else if (rc) | 1039 | } else if (rc) |
| 869 | goto out; | 1040 | goto out; |
| 870 | rc = sidtab_context_to_sid(&sidtab, &context, sid); | 1041 | rc = sidtab_context_to_sid(&sidtab, &context, sid); |
| 871 | if (rc) | 1042 | context_destroy(&context); |
| 872 | context_destroy(&context); | ||
| 873 | out: | 1043 | out: |
| 874 | read_unlock(&policy_rwlock); | 1044 | read_unlock(&policy_rwlock); |
| 875 | kfree(scontext2); | 1045 | kfree(scontext2); |
| @@ -1037,7 +1207,7 @@ static int security_compute_sid(u32 ssid, | |||
| 1037 | /* If no permanent rule, also check for enabled conditional rules */ | 1207 | /* If no permanent rule, also check for enabled conditional rules */ |
| 1038 | if (!avdatum) { | 1208 | if (!avdatum) { |
| 1039 | node = avtab_search_node(&policydb.te_cond_avtab, &avkey); | 1209 | node = avtab_search_node(&policydb.te_cond_avtab, &avkey); |
| 1040 | for (; node != NULL; node = avtab_search_node_next(node, specified)) { | 1210 | for (; node; node = avtab_search_node_next(node, specified)) { |
| 1041 | if (node->key.specified & AVTAB_ENABLED) { | 1211 | if (node->key.specified & AVTAB_ENABLED) { |
| 1042 | avdatum = &node->datum; | 1212 | avdatum = &node->datum; |
| 1043 | break; | 1213 | break; |
| @@ -2050,7 +2220,7 @@ int security_set_bools(int len, int *values) | |||
| 2050 | policydb.bool_val_to_struct[i]->state = 0; | 2220 | policydb.bool_val_to_struct[i]->state = 0; |
| 2051 | } | 2221 | } |
| 2052 | 2222 | ||
| 2053 | for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { | 2223 | for (cur = policydb.cond_list; cur; cur = cur->next) { |
| 2054 | rc = evaluate_cond_node(&policydb, cur); | 2224 | rc = evaluate_cond_node(&policydb, cur); |
| 2055 | if (rc) | 2225 | if (rc) |
| 2056 | goto out; | 2226 | goto out; |
| @@ -2102,7 +2272,7 @@ static int security_preserve_bools(struct policydb *p) | |||
| 2102 | if (booldatum) | 2272 | if (booldatum) |
| 2103 | booldatum->state = bvalues[i]; | 2273 | booldatum->state = bvalues[i]; |
| 2104 | } | 2274 | } |
| 2105 | for (cur = p->cond_list; cur != NULL; cur = cur->next) { | 2275 | for (cur = p->cond_list; cur; cur = cur->next) { |
| 2106 | rc = evaluate_cond_node(p, cur); | 2276 | rc = evaluate_cond_node(p, cur); |
| 2107 | if (rc) | 2277 | if (rc) |
| 2108 | goto out; | 2278 | goto out; |
| @@ -2737,6 +2907,7 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, | |||
| 2737 | if (ctx == NULL) | 2907 | if (ctx == NULL) |
| 2738 | goto netlbl_secattr_to_sid_return; | 2908 | goto netlbl_secattr_to_sid_return; |
| 2739 | 2909 | ||
| 2910 | context_init(&ctx_new); | ||
| 2740 | ctx_new.user = ctx->user; | 2911 | ctx_new.user = ctx->user; |
| 2741 | ctx_new.role = ctx->role; | 2912 | ctx_new.role = ctx->role; |
| 2742 | ctx_new.type = ctx->type; | 2913 | ctx_new.type = ctx->type; |
| @@ -2745,13 +2916,9 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, | |||
| 2745 | if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, | 2916 | if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat, |
| 2746 | secattr->attr.mls.cat) != 0) | 2917 | secattr->attr.mls.cat) != 0) |
| 2747 | goto netlbl_secattr_to_sid_return; | 2918 | goto netlbl_secattr_to_sid_return; |
| 2748 | ctx_new.range.level[1].cat.highbit = | 2919 | memcpy(&ctx_new.range.level[1].cat, |
| 2749 | ctx_new.range.level[0].cat.highbit; | 2920 | &ctx_new.range.level[0].cat, |
| 2750 | ctx_new.range.level[1].cat.node = | 2921 | sizeof(ctx_new.range.level[0].cat)); |
| 2751 | ctx_new.range.level[0].cat.node; | ||
| 2752 | } else { | ||
| 2753 | ebitmap_init(&ctx_new.range.level[0].cat); | ||
| 2754 | ebitmap_init(&ctx_new.range.level[1].cat); | ||
| 2755 | } | 2922 | } |
| 2756 | if (mls_context_isvalid(&policydb, &ctx_new) != 1) | 2923 | if (mls_context_isvalid(&policydb, &ctx_new) != 1) |
| 2757 | goto netlbl_secattr_to_sid_return_cleanup; | 2924 | goto netlbl_secattr_to_sid_return_cleanup; |
