apparmor: move change_profile mediation to using labels

Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen 2017-06-09 17:07:58 -07:00
parent 89dbf1962a
commit e00b02bb6a

View file

@ -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)
@ -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
@ -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) {
@ -1172,76 +1187,116 @@ int aa_change_profile(const char *fqname, int flags)
if (flags & AA_CHANGE_ONEXEC) {
request = AA_MAY_ONEXEC;
op = OP_CHANGE_ONEXEC;
if (stack)
op = OP_STACK_ONEXEC;
else
op = OP_CHANGE_ONEXEC;
} else {
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_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);
else
error = aa_replace_current_label(&target->label);
if (!(flags & AA_CHANGE_ONEXEC)) {
/* only transition profiles in the current ns */
if (stack)
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:
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;
}