Loading security/apparmor/domain.c +123 −68 Original line number Diff line number Diff line Loading @@ -301,26 +301,6 @@ static int change_profile_perms(struct aa_profile *profile, return label_match(profile, target, stack, start, true, request, perms); } static struct aa_perms change_profile_perms_wrapper(struct aa_profile *profile, struct aa_profile *target, u32 request, unsigned int start) { struct aa_perms perms; if (profile_unconfined(profile)) { perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; perms.audit = perms.quiet = perms.kill = 0; return perms; } if (change_profile_perms(profile, &target->label, false, request, start, &perms)) return nullperms; return perms; } /** * __attach_match_ - find an attachment match * @name - to match against (NOT NULL) Loading Loading @@ -1140,6 +1120,39 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) } static int change_profile_perms_wrapper(const char *op, const char *name, struct aa_profile *profile, struct aa_label *target, bool stack, u32 request, struct aa_perms *perms) { const char *info = NULL; int error = 0; /* * Fail explicitly requested domain transitions when no_new_privs * and not unconfined OR the transition results in a stack on * the current label. * Stacking domain transitions and transitions from unconfined are * allowed even when no_new_privs is set because this aways results * in a reduction of permissions. */ if (task_no_new_privs(current) && !stack && !profile_unconfined(profile) && !aa_label_is_subset(target, &profile->label)) { info = "no new privs"; error = -EPERM; } if (!error) error = change_profile_perms(profile, target, stack, request, profile->file.start, perms); if (error) error = aa_audit_file(profile, perms, op, request, name, NULL, target, GLOBAL_ROOT_UID, info, error); return error; } /** * aa_change_profile - perform a one-way profile transition Loading @@ -1157,12 +1170,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) */ int aa_change_profile(const char *fqname, int flags) { const struct cred *cred; struct aa_label *label; struct aa_profile *profile, *target = NULL; struct aa_label *label, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; const char *info = NULL, *op; const char *info = NULL; const char *auditname = fqname; /* retain leading & if stack */ bool stack = flags & AA_CHANGE_STACK; int error = 0; char *op; u32 request; if (!fqname || !*fqname) { Loading @@ -1172,76 +1187,116 @@ int aa_change_profile(const char *fqname, int flags) if (flags & AA_CHANGE_ONEXEC) { request = AA_MAY_ONEXEC; if (stack) op = OP_STACK_ONEXEC; else op = OP_CHANGE_ONEXEC; } else { request = AA_MAY_CHANGE_PROFILE; if (stack) op = OP_STACK; else op = OP_CHANGE_PROFILE; } cred = get_current_cred(); label = aa_get_newest_cred_label(cred); profile = labels_profile(label); label = aa_get_current_label(); /* * Fail explicitly requested domain transitions if no_new_privs * and not unconfined. * Domain transitions from unconfined are allowed even when * no_new_privs is set because this aways results in a reduction * of permissions. */ if (task_no_new_privs(current) && !profile_unconfined(profile)) { put_cred(cred); return -EPERM; if (*fqname == '&') { stack = true; /* don't have label_parse() do stacking */ fqname++; } target = aa_label_parse(label, fqname, GFP_KERNEL, true, false); if (IS_ERR(target)) { struct aa_profile *tprofile; target = aa_fqlookupn_profile(label, fqname, strlen(fqname)); if (!target) { info = "profile not found"; error = -ENOENT; info = "label not found"; error = PTR_ERR(target); target = NULL; /* * TODO: fixme using labels_profile is not right - do profile * per complain profile */ if ((flags & AA_CHANGE_TEST) || !COMPLAIN_MODE(profile)) !COMPLAIN_MODE(labels_profile(label))) goto audit; /* released below */ target = aa_new_null_profile(profile, false, fqname, GFP_KERNEL); if (!target) { tprofile = aa_new_null_profile(labels_profile(label), false, fqname, GFP_KERNEL); if (!tprofile) { info = "failed null profile create"; error = -ENOMEM; goto audit; } target = &tprofile->label; goto check; } perms = change_profile_perms_wrapper(profile, target, request, profile->file.start); if (!(perms.allow & request)) { error = -EACCES; goto audit; } /* * self directed transitions only apply to current policy ns * TODO: currently requiring perms for stacking and straight change * stacking doesn't strictly need this. Determine how much * we want to loosen this restriction for stacking * * if (!stack) { */ error = fn_for_each_in_ns(label, profile, change_profile_perms_wrapper(op, auditname, profile, target, stack, request, &perms)); if (error) /* auditing done in change_profile_perms_wrapper */ goto out; /* } */ check: /* check if tracing task is allowed to trace target domain */ error = may_change_ptraced_domain(&target->label, &info); if (error) { info = "ptrace prevents transition"; error = may_change_ptraced_domain(target, &info); if (error && !fn_for_each_in_ns(label, profile, COMPLAIN_MODE(profile))) goto audit; } /* TODO: add permission check to allow this * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) { * info = "not a single threaded task"; * error = -EACCES; * goto audit; * } */ if (flags & AA_CHANGE_TEST) goto audit; goto out; if (flags & AA_CHANGE_ONEXEC) error = aa_set_current_onexec(&target->label, 0); if (!(flags & AA_CHANGE_ONEXEC)) { /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); else error = aa_replace_current_label(&target->label); new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); if (IS_ERR_OR_NULL(new)) { info = "failed to build target label"; error = PTR_ERR(new); new = NULL; perms.allow = 0; goto audit; } error = aa_replace_current_label(new); } else /* full transition will be built in exec path */ error = aa_set_current_onexec(target, stack); audit: if (!(flags & AA_CHANGE_TEST)) error = aa_audit_file(profile, &perms, op, request, NULL, fqname, NULL, GLOBAL_ROOT_UID, info, error); error = fn_for_each_in_ns(label, profile, aa_audit_file(profile, &perms, op, request, auditname, NULL, new ? new : target, GLOBAL_ROOT_UID, info, error)); aa_put_profile(target); out: aa_put_label(new); aa_put_label(target); aa_put_label(label); put_cred(cred); return error; } Loading
security/apparmor/domain.c +123 −68 Original line number Diff line number Diff line Loading @@ -301,26 +301,6 @@ static int change_profile_perms(struct aa_profile *profile, return label_match(profile, target, stack, start, true, request, perms); } static struct aa_perms change_profile_perms_wrapper(struct aa_profile *profile, struct aa_profile *target, u32 request, unsigned int start) { struct aa_perms perms; if (profile_unconfined(profile)) { perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; perms.audit = perms.quiet = perms.kill = 0; return perms; } if (change_profile_perms(profile, &target->label, false, request, start, &perms)) return nullperms; return perms; } /** * __attach_match_ - find an attachment match * @name - to match against (NOT NULL) Loading Loading @@ -1140,6 +1120,39 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) } static int change_profile_perms_wrapper(const char *op, const char *name, struct aa_profile *profile, struct aa_label *target, bool stack, u32 request, struct aa_perms *perms) { const char *info = NULL; int error = 0; /* * Fail explicitly requested domain transitions when no_new_privs * and not unconfined OR the transition results in a stack on * the current label. * Stacking domain transitions and transitions from unconfined are * allowed even when no_new_privs is set because this aways results * in a reduction of permissions. */ if (task_no_new_privs(current) && !stack && !profile_unconfined(profile) && !aa_label_is_subset(target, &profile->label)) { info = "no new privs"; error = -EPERM; } if (!error) error = change_profile_perms(profile, target, stack, request, profile->file.start, perms); if (error) error = aa_audit_file(profile, perms, op, request, name, NULL, target, GLOBAL_ROOT_UID, info, error); return error; } /** * aa_change_profile - perform a one-way profile transition Loading @@ -1157,12 +1170,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) */ int aa_change_profile(const char *fqname, int flags) { const struct cred *cred; struct aa_label *label; struct aa_profile *profile, *target = NULL; struct aa_label *label, *new = NULL, *target = NULL; struct aa_profile *profile; struct aa_perms perms = {}; const char *info = NULL, *op; const char *info = NULL; const char *auditname = fqname; /* retain leading & if stack */ bool stack = flags & AA_CHANGE_STACK; int error = 0; char *op; u32 request; if (!fqname || !*fqname) { Loading @@ -1172,76 +1187,116 @@ int aa_change_profile(const char *fqname, int flags) if (flags & AA_CHANGE_ONEXEC) { request = AA_MAY_ONEXEC; if (stack) op = OP_STACK_ONEXEC; else op = OP_CHANGE_ONEXEC; } else { request = AA_MAY_CHANGE_PROFILE; if (stack) op = OP_STACK; else op = OP_CHANGE_PROFILE; } cred = get_current_cred(); label = aa_get_newest_cred_label(cred); profile = labels_profile(label); label = aa_get_current_label(); /* * Fail explicitly requested domain transitions if no_new_privs * and not unconfined. * Domain transitions from unconfined are allowed even when * no_new_privs is set because this aways results in a reduction * of permissions. */ if (task_no_new_privs(current) && !profile_unconfined(profile)) { put_cred(cred); return -EPERM; if (*fqname == '&') { stack = true; /* don't have label_parse() do stacking */ fqname++; } target = aa_label_parse(label, fqname, GFP_KERNEL, true, false); if (IS_ERR(target)) { struct aa_profile *tprofile; target = aa_fqlookupn_profile(label, fqname, strlen(fqname)); if (!target) { info = "profile not found"; error = -ENOENT; info = "label not found"; error = PTR_ERR(target); target = NULL; /* * TODO: fixme using labels_profile is not right - do profile * per complain profile */ if ((flags & AA_CHANGE_TEST) || !COMPLAIN_MODE(profile)) !COMPLAIN_MODE(labels_profile(label))) goto audit; /* released below */ target = aa_new_null_profile(profile, false, fqname, GFP_KERNEL); if (!target) { tprofile = aa_new_null_profile(labels_profile(label), false, fqname, GFP_KERNEL); if (!tprofile) { info = "failed null profile create"; error = -ENOMEM; goto audit; } target = &tprofile->label; goto check; } perms = change_profile_perms_wrapper(profile, target, request, profile->file.start); if (!(perms.allow & request)) { error = -EACCES; goto audit; } /* * self directed transitions only apply to current policy ns * TODO: currently requiring perms for stacking and straight change * stacking doesn't strictly need this. Determine how much * we want to loosen this restriction for stacking * * if (!stack) { */ error = fn_for_each_in_ns(label, profile, change_profile_perms_wrapper(op, auditname, profile, target, stack, request, &perms)); if (error) /* auditing done in change_profile_perms_wrapper */ goto out; /* } */ check: /* check if tracing task is allowed to trace target domain */ error = may_change_ptraced_domain(&target->label, &info); if (error) { info = "ptrace prevents transition"; error = may_change_ptraced_domain(target, &info); if (error && !fn_for_each_in_ns(label, profile, COMPLAIN_MODE(profile))) goto audit; } /* TODO: add permission check to allow this * if ((flags & AA_CHANGE_ONEXEC) && !current_is_single_threaded()) { * info = "not a single threaded task"; * error = -EACCES; * goto audit; * } */ if (flags & AA_CHANGE_TEST) goto audit; goto out; if (flags & AA_CHANGE_ONEXEC) error = aa_set_current_onexec(&target->label, 0); if (!(flags & AA_CHANGE_ONEXEC)) { /* only transition profiles in the current ns */ if (stack) new = aa_label_merge(label, target, GFP_KERNEL); else error = aa_replace_current_label(&target->label); new = fn_label_build_in_ns(label, profile, GFP_KERNEL, aa_get_label(target), aa_get_label(&profile->label)); if (IS_ERR_OR_NULL(new)) { info = "failed to build target label"; error = PTR_ERR(new); new = NULL; perms.allow = 0; goto audit; } error = aa_replace_current_label(new); } else /* full transition will be built in exec path */ error = aa_set_current_onexec(target, stack); audit: if (!(flags & AA_CHANGE_TEST)) error = aa_audit_file(profile, &perms, op, request, NULL, fqname, NULL, GLOBAL_ROOT_UID, info, error); error = fn_for_each_in_ns(label, profile, aa_audit_file(profile, &perms, op, request, auditname, NULL, new ? new : target, GLOBAL_ROOT_UID, info, error)); aa_put_profile(target); out: aa_put_label(new); aa_put_label(target); aa_put_label(label); put_cred(cred); return error; }