Commit e00b02bb authored by John Johansen's avatar John Johansen

apparmor: move change_profile mediation to using labels

Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 89dbf196
...@@ -301,26 +301,6 @@ static int change_profile_perms(struct aa_profile *profile, ...@@ -301,26 +301,6 @@ static int change_profile_perms(struct aa_profile *profile,
return label_match(profile, target, stack, start, true, request, perms); 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 * __attach_match_ - find an attachment match
* @name - to match against (NOT NULL) * @name - to match against (NOT NULL)
...@@ -1140,6 +1120,39 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) ...@@ -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 * aa_change_profile - perform a one-way profile transition
...@@ -1157,12 +1170,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, int flags) ...@@ -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) int aa_change_profile(const char *fqname, int flags)
{ {
const struct cred *cred; struct aa_label *label, *new = NULL, *target = NULL;
struct aa_label *label; struct aa_profile *profile;
struct aa_profile *profile, *target = NULL;
struct aa_perms perms = {}; 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; int error = 0;
char *op;
u32 request; u32 request;
if (!fqname || !*fqname) { if (!fqname || !*fqname) {
...@@ -1172,76 +1187,116 @@ int aa_change_profile(const char *fqname, int flags) ...@@ -1172,76 +1187,116 @@ int aa_change_profile(const char *fqname, int flags)
if (flags & AA_CHANGE_ONEXEC) { if (flags & AA_CHANGE_ONEXEC) {
request = AA_MAY_ONEXEC; request = AA_MAY_ONEXEC;
op = OP_CHANGE_ONEXEC; if (stack)
op = OP_STACK_ONEXEC;
else
op = OP_CHANGE_ONEXEC;
} else { } else {
request = AA_MAY_CHANGE_PROFILE; request = AA_MAY_CHANGE_PROFILE;
op = OP_CHANGE_PROFILE; if (stack)
op = OP_STACK;
else
op = OP_CHANGE_PROFILE;
} }
cred = get_current_cred(); label = aa_get_current_label();
label = aa_get_newest_cred_label(cred);
profile = labels_profile(label);
/* if (*fqname == '&') {
* Fail explicitly requested domain transitions if no_new_privs stack = true;
* and not unconfined. /* don't have label_parse() do stacking */
* Domain transitions from unconfined are allowed even when fqname++;
* 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;
} }
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)); info = "label not found";
if (!target) { error = PTR_ERR(target);
info = "profile not found"; target = NULL;
error = -ENOENT; /*
* TODO: fixme using labels_profile is not right - do profile
* per complain profile
*/
if ((flags & AA_CHANGE_TEST) || if ((flags & AA_CHANGE_TEST) ||
!COMPLAIN_MODE(profile)) !COMPLAIN_MODE(labels_profile(label)))
goto audit; goto audit;
/* released below */ /* released below */
target = aa_new_null_profile(profile, false, fqname, tprofile = aa_new_null_profile(labels_profile(label), false,
GFP_KERNEL); fqname, GFP_KERNEL);
if (!target) { if (!tprofile) {
info = "failed null profile create"; info = "failed null profile create";
error = -ENOMEM; error = -ENOMEM;
goto audit; goto audit;
} }
target = &tprofile->label;
goto check;
} }
perms = change_profile_perms_wrapper(profile, target, request, /*
profile->file.start); * self directed transitions only apply to current policy ns
if (!(perms.allow & request)) { * TODO: currently requiring perms for stacking and straight change
error = -EACCES; * stacking doesn't strictly need this. Determine how much
goto audit; * 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 */ /* check if tracing task is allowed to trace target domain */
error = may_change_ptraced_domain(&target->label, &info); error = may_change_ptraced_domain(target, &info);
if (error) { if (error && !fn_for_each_in_ns(label, profile,
info = "ptrace prevents transition"; COMPLAIN_MODE(profile)))
goto audit; 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) if (flags & AA_CHANGE_TEST)
goto audit; goto out;
if (flags & AA_CHANGE_ONEXEC) if (!(flags & AA_CHANGE_ONEXEC)) {
error = aa_set_current_onexec(&target->label, 0); /* only transition profiles in the current ns */
else if (stack)
error = aa_replace_current_label(&target->label); new = aa_label_merge(label, target, GFP_KERNEL);
else
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: audit:
if (!(flags & AA_CHANGE_TEST)) error = fn_for_each_in_ns(label, profile,
error = aa_audit_file(profile, &perms, op, request, NULL, aa_audit_file(profile, &perms, op, request, auditname,
fqname, NULL, GLOBAL_ROOT_UID, info, NULL, new ? new : target,
error); GLOBAL_ROOT_UID, info, error));
aa_put_profile(target); out:
aa_put_label(new);
aa_put_label(target);
aa_put_label(label); aa_put_label(label);
put_cred(cred);
return error; return error;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment