Updated AppArmor with a newer backported AppArmor release by UBports
This commit is contained in:
@@ -50,41 +50,39 @@ void aa_free_domain_entries(struct aa_domain *domain)
|
||||
|
||||
/**
|
||||
* may_change_ptraced_domain - check if can change profile on ptraced task
|
||||
* @task: task we want to change profile of (NOT NULL)
|
||||
* @to_profile: profile to change to (NOT NULL)
|
||||
* @info: message if there is an error
|
||||
*
|
||||
* Check if the task is ptraced and if so if the tracing task is allowed
|
||||
* Check if current is ptraced and if so if the tracing task is allowed
|
||||
* to trace the new domain
|
||||
*
|
||||
* Returns: %0 or error if change not allowed
|
||||
*/
|
||||
static int may_change_ptraced_domain(struct task_struct *task,
|
||||
struct aa_profile *to_profile)
|
||||
static int may_change_ptraced_domain(struct aa_profile *to_profile,
|
||||
const char **info)
|
||||
{
|
||||
struct task_struct *tracer;
|
||||
const struct cred *cred = NULL;
|
||||
struct aa_profile *tracerp = NULL;
|
||||
struct aa_label *tracerl = NULL;
|
||||
int error = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
tracer = ptrace_parent(task);
|
||||
if (tracer) {
|
||||
tracer = ptrace_parent(current);
|
||||
if (tracer)
|
||||
/* released below */
|
||||
cred = get_task_cred(tracer);
|
||||
tracerp = aa_cred_profile(cred);
|
||||
}
|
||||
tracerl = aa_get_task_label(tracer);
|
||||
|
||||
/* not ptraced */
|
||||
if (!tracer || unconfined(tracerp))
|
||||
if (!tracer || unconfined(tracerl))
|
||||
goto out;
|
||||
|
||||
error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
|
||||
error = aa_may_ptrace(tracerl, &to_profile->label, PTRACE_MODE_ATTACH);
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
if (cred)
|
||||
put_cred(cred);
|
||||
aa_put_label(tracerl);
|
||||
|
||||
if (error)
|
||||
*info = "ptrace prevents transition";
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -107,7 +105,7 @@ static struct file_perms change_profile_perms(struct aa_profile *profile,
|
||||
struct path_cond cond = { };
|
||||
unsigned int state;
|
||||
|
||||
if (unconfined(profile)) {
|
||||
if (profile_unconfined(profile)) {
|
||||
perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC;
|
||||
perms.audit = perms.quiet = perms.kill = 0;
|
||||
return perms;
|
||||
@@ -148,8 +146,8 @@ static struct aa_profile *__attach_match(const char *name,
|
||||
int len = 0;
|
||||
struct aa_profile *profile, *candidate = NULL;
|
||||
|
||||
list_for_each_entry(profile, head, base.list) {
|
||||
if (profile->flags & PFLAG_NULL)
|
||||
list_for_each_entry_rcu(profile, head, base.list) {
|
||||
if (profile->label.flags & FLAG_NULL)
|
||||
continue;
|
||||
if (profile->xmatch && profile->xmatch_len > len) {
|
||||
unsigned int state = aa_dfa_match(profile->xmatch,
|
||||
@@ -181,9 +179,9 @@ static struct aa_profile *find_attach(struct aa_namespace *ns,
|
||||
{
|
||||
struct aa_profile *profile;
|
||||
|
||||
read_lock(&ns->lock);
|
||||
rcu_read_lock();
|
||||
profile = aa_get_profile(__attach_match(name, list));
|
||||
read_unlock(&ns->lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
return profile;
|
||||
}
|
||||
@@ -242,7 +240,7 @@ static const char *next_name(int xtype, const char *name)
|
||||
*
|
||||
* Returns: refcounted profile, or NULL on failure (MAYBE NULL)
|
||||
*/
|
||||
static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex)
|
||||
{
|
||||
struct aa_profile *new_profile = NULL;
|
||||
struct aa_namespace *ns = profile->ns;
|
||||
@@ -343,6 +341,7 @@ static struct aa_profile *x_to_profile(struct aa_profile *profile,
|
||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
struct aa_task_cxt *cxt;
|
||||
struct aa_label *label;
|
||||
struct aa_profile *profile, *new_profile = NULL;
|
||||
struct aa_namespace *ns;
|
||||
char *buffer = NULL;
|
||||
@@ -360,10 +359,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
if (bprm->cred_prepared)
|
||||
return 0;
|
||||
|
||||
cxt = bprm->cred->security;
|
||||
cxt = cred_cxt(bprm->cred);
|
||||
BUG_ON(!cxt);
|
||||
|
||||
profile = aa_get_profile(aa_newest_version(cxt->profile));
|
||||
label = aa_get_newest_label(cxt->label);
|
||||
profile = labels_profile(label);
|
||||
/*
|
||||
* get the namespace from the replacement profile as replacement
|
||||
* can change the namespace
|
||||
@@ -372,11 +372,12 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
state = profile->file.start;
|
||||
|
||||
/* buffer freed below, name is pointer into buffer */
|
||||
error = aa_path_name(&bprm->file->f_path, profile->path_flags, &buffer,
|
||||
&name, &info);
|
||||
get_buffers(buffer);
|
||||
error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer,
|
||||
&name, &info, profile->disconnected);
|
||||
if (error) {
|
||||
if (profile->flags &
|
||||
(PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED))
|
||||
if (profile_unconfined(profile) ||
|
||||
(profile->label.flags & FLAG_IX_ON_NAME_ERROR))
|
||||
error = 0;
|
||||
name = bprm->filename;
|
||||
goto audit;
|
||||
@@ -385,11 +386,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
/* Test for onexec first as onexec directives override other
|
||||
* x transitions.
|
||||
*/
|
||||
if (unconfined(profile)) {
|
||||
if (profile_unconfined(profile)) {
|
||||
/* unconfined task */
|
||||
if (cxt->onexec)
|
||||
/* change_profile on exec already been granted */
|
||||
new_profile = aa_get_profile(cxt->onexec);
|
||||
new_profile = labels_profile(aa_get_label(cxt->onexec));
|
||||
else
|
||||
new_profile = find_attach(ns, &ns->base.profiles, name);
|
||||
if (!new_profile)
|
||||
@@ -415,13 +416,13 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
* exec\0change_profile
|
||||
*/
|
||||
state = aa_dfa_null_transition(profile->file.dfa, state);
|
||||
cp = change_profile_perms(profile, cxt->onexec->ns,
|
||||
cxt->onexec->base.name,
|
||||
cp = change_profile_perms(profile, labels_profile(cxt->onexec)->ns,
|
||||
labels_profile(cxt->onexec)->base.name,
|
||||
AA_MAY_ONEXEC, state);
|
||||
|
||||
if (!(cp.allow & AA_MAY_ONEXEC))
|
||||
goto audit;
|
||||
new_profile = aa_get_profile(aa_newest_version(cxt->onexec));
|
||||
new_profile = labels_profile(aa_get_newest_label(cxt->onexec));
|
||||
goto apply;
|
||||
}
|
||||
|
||||
@@ -438,15 +439,19 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
new_profile = aa_get_profile(profile);
|
||||
goto x_clear;
|
||||
} else if (perms.xindex & AA_X_UNCONFINED) {
|
||||
new_profile = aa_get_profile(ns->unconfined);
|
||||
new_profile = labels_profile(aa_get_newest_label(&ns->unconfined->label));
|
||||
info = "ux fallback";
|
||||
} else {
|
||||
error = -ENOENT;
|
||||
error = -EACCES;
|
||||
info = "profile not found";
|
||||
/* remove MAY_EXEC to audit as failure */
|
||||
perms.allow &= ~MAY_EXEC;
|
||||
}
|
||||
}
|
||||
} else if (COMPLAIN_MODE(profile)) {
|
||||
/* no exec permission - are we in learning mode */
|
||||
/* no exec permission - learning mode. break rcu lock */
|
||||
put_buffers(buffer);
|
||||
name = NULL;
|
||||
new_profile = aa_new_null_profile(profile, 0);
|
||||
if (!new_profile) {
|
||||
error = -ENOMEM;
|
||||
@@ -456,6 +461,12 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
target = new_profile->base.hname;
|
||||
}
|
||||
perms.xindex |= AA_X_UNSAFE;
|
||||
/* re-aquire buffer and rcu readlock and re-get name */
|
||||
get_buffers(buffer);
|
||||
if (!error)
|
||||
error = aa_path_name(&bprm->file->f_path,
|
||||
profile->path_flags, buffer,
|
||||
&name, &info, profile->disconnected);
|
||||
} else
|
||||
/* fail exec */
|
||||
error = -EACCES;
|
||||
@@ -479,7 +490,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
}
|
||||
|
||||
if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
|
||||
error = may_change_ptraced_domain(current, new_profile);
|
||||
error = may_change_ptraced_domain(new_profile, &info);
|
||||
if (error) {
|
||||
aa_put_profile(new_profile);
|
||||
goto audit;
|
||||
@@ -509,24 +520,20 @@ apply:
|
||||
bprm->per_clear |= PER_CLEAR_ON_SETID;
|
||||
|
||||
x_clear:
|
||||
aa_put_profile(cxt->profile);
|
||||
aa_put_label(cxt->label);
|
||||
/* transfer new profile reference will be released when cxt is freed */
|
||||
cxt->profile = new_profile;
|
||||
cxt->label = &new_profile->label;
|
||||
|
||||
/* clear out all temporary/transitional state from the context */
|
||||
aa_put_profile(cxt->previous);
|
||||
aa_put_profile(cxt->onexec);
|
||||
cxt->previous = NULL;
|
||||
cxt->onexec = NULL;
|
||||
cxt->token = 0;
|
||||
aa_clear_task_cxt_trans(cxt);
|
||||
|
||||
audit:
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC,
|
||||
name, target, cond.uid, info, error);
|
||||
error = aa_audit_file(profile, &perms, OP_EXEC, MAY_EXEC, name, target,
|
||||
cond.uid, info, error);
|
||||
|
||||
cleanup:
|
||||
aa_put_profile(profile);
|
||||
kfree(buffer);
|
||||
aa_put_label(label);
|
||||
put_buffers(buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -550,36 +557,6 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* apparmor_bprm_committing_creds - do task cleanup on committing new creds
|
||||
* @bprm: binprm for the exec (NOT NULL)
|
||||
*/
|
||||
void apparmor_bprm_committing_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
struct aa_profile *profile = __aa_current_profile();
|
||||
struct aa_task_cxt *new_cxt = bprm->cred->security;
|
||||
|
||||
/* bail out if unconfined or not changing profile */
|
||||
if ((new_cxt->profile == profile) ||
|
||||
(unconfined(new_cxt->profile)))
|
||||
return;
|
||||
|
||||
current->pdeath_signal = 0;
|
||||
|
||||
/* reset soft limits and set hard limits for the new profile */
|
||||
__aa_transition_rlimits(profile, new_cxt->profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* apparmor_bprm_commited_cred - do cleanup after new creds committed
|
||||
* @bprm: binprm for the exec (NOT NULL)
|
||||
*/
|
||||
void apparmor_bprm_committed_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
/* TODO: cleanup signals - ipc mediation */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for self directed profile change
|
||||
*/
|
||||
@@ -617,7 +594,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
{
|
||||
const struct cred *cred;
|
||||
struct aa_task_cxt *cxt;
|
||||
struct aa_profile *profile, *previous_profile, *hat = NULL;
|
||||
struct aa_label *label, *previous;
|
||||
struct aa_profile *profile, *hat = NULL;
|
||||
char *name = NULL;
|
||||
int i;
|
||||
struct file_perms perms = {};
|
||||
@@ -634,11 +612,13 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
|
||||
/* released below */
|
||||
cred = get_current_cred();
|
||||
cxt = cred->security;
|
||||
profile = aa_cred_profile(cred);
|
||||
previous_profile = cxt->previous;
|
||||
cxt = cred_cxt(cred);
|
||||
label = aa_get_newest_cred_label(cred);
|
||||
previous = cxt->previous;
|
||||
|
||||
if (unconfined(profile)) {
|
||||
profile = labels_profile(label);
|
||||
|
||||
if (unconfined(label)) {
|
||||
info = "unconfined";
|
||||
error = -EPERM;
|
||||
goto audit;
|
||||
@@ -647,7 +627,10 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
if (count) {
|
||||
/* attempting to change into a new hat or switch to a sibling */
|
||||
struct aa_profile *root;
|
||||
root = PROFILE_IS_HAT(profile) ? profile->parent : profile;
|
||||
if (PROFILE_IS_HAT(profile))
|
||||
root = aa_get_profile_rcu(&profile->parent);
|
||||
else
|
||||
root = aa_get_profile(labels_profile(label));
|
||||
|
||||
/* find first matching hat */
|
||||
for (i = 0; i < count && !hat; i++)
|
||||
@@ -659,6 +642,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
error = -ECHILD;
|
||||
else
|
||||
error = -ENOENT;
|
||||
aa_put_profile(root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -673,6 +657,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
|
||||
/* freed below */
|
||||
name = new_compound_name(root->base.hname, hats[0]);
|
||||
aa_put_profile(root);
|
||||
target = name;
|
||||
/* released below */
|
||||
hat = aa_new_null_profile(profile, 1);
|
||||
@@ -682,6 +667,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
goto audit;
|
||||
}
|
||||
} else {
|
||||
aa_put_profile(root);
|
||||
target = hat->base.hname;
|
||||
if (!PROFILE_IS_HAT(hat)) {
|
||||
info = "target not hat";
|
||||
@@ -690,15 +676,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
}
|
||||
}
|
||||
|
||||
error = may_change_ptraced_domain(current, hat);
|
||||
error = may_change_ptraced_domain(hat, &info);
|
||||
if (error) {
|
||||
info = "ptraced";
|
||||
error = -EPERM;
|
||||
goto audit;
|
||||
}
|
||||
|
||||
if (!permtest) {
|
||||
error = aa_set_current_hat(hat, token);
|
||||
error = aa_set_current_hat(&hat->label, token);
|
||||
if (error == -EACCES)
|
||||
/* kill task in case of brute force attacks */
|
||||
perms.kill = AA_MAY_CHANGEHAT;
|
||||
@@ -706,12 +691,12 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
/* reset error for learning of new hats */
|
||||
error = -ENOENT;
|
||||
}
|
||||
} else if (previous_profile) {
|
||||
/* Return to saved profile. Kill task if restore fails
|
||||
} else if (previous) {
|
||||
/* Return to saved label. Kill task if restore fails
|
||||
* to avoid brute force attacks
|
||||
*/
|
||||
target = previous_profile->base.hname;
|
||||
error = aa_restore_previous_profile(token);
|
||||
target = previous->hname;
|
||||
error = aa_restore_previous_label(token);
|
||||
perms.kill = AA_MAY_CHANGEHAT;
|
||||
} else
|
||||
/* ignore restores when there is no saved profile */
|
||||
@@ -719,12 +704,13 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
|
||||
audit:
|
||||
if (!permtest)
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL,
|
||||
OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL,
|
||||
target, GLOBAL_ROOT_UID, info, error);
|
||||
error = aa_audit_file(profile, &perms, OP_CHANGE_HAT,
|
||||
AA_MAY_CHANGEHAT, NULL, target,
|
||||
GLOBAL_ROOT_UID, info, error);
|
||||
|
||||
out:
|
||||
aa_put_profile(hat);
|
||||
aa_put_label(label);
|
||||
kfree(name);
|
||||
put_cred(cred);
|
||||
|
||||
@@ -750,7 +736,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
bool permtest)
|
||||
{
|
||||
const struct cred *cred;
|
||||
struct aa_task_cxt *cxt;
|
||||
struct aa_label *label;
|
||||
struct aa_profile *profile, *target = NULL;
|
||||
struct aa_namespace *ns = NULL;
|
||||
struct file_perms perms = {};
|
||||
@@ -770,8 +756,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
}
|
||||
|
||||
cred = get_current_cred();
|
||||
cxt = cred->security;
|
||||
profile = aa_cred_profile(cred);
|
||||
label = aa_get_newest_cred_label(cred);
|
||||
profile = labels_profile(label);
|
||||
|
||||
/*
|
||||
* Fail explicitly requested domain transitions if no_new_privs
|
||||
@@ -780,7 +766,8 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
* no_new_privs is set because this aways results in a reduction
|
||||
* of permissions.
|
||||
*/
|
||||
if (task_no_new_privs(current) && !unconfined(profile)) {
|
||||
if (task_no_new_privs(current) && !unconfined(label)) {
|
||||
aa_put_label(label);
|
||||
put_cred(cred);
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -801,7 +788,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
|
||||
/* if the name was not specified, use the name of the current profile */
|
||||
if (!hname) {
|
||||
if (unconfined(profile))
|
||||
if (profile_unconfined(profile))
|
||||
hname = ns->unconfined->base.hname;
|
||||
else
|
||||
hname = profile->base.hname;
|
||||
@@ -831,27 +818,26 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
}
|
||||
|
||||
/* check if tracing task is allowed to trace target domain */
|
||||
error = may_change_ptraced_domain(current, target);
|
||||
if (error) {
|
||||
info = "ptrace prevents transition";
|
||||
error = may_change_ptraced_domain(target, &info);
|
||||
if (error)
|
||||
goto audit;
|
||||
}
|
||||
|
||||
if (permtest)
|
||||
goto audit;
|
||||
|
||||
if (onexec)
|
||||
error = aa_set_current_onexec(target);
|
||||
error = aa_set_current_onexec(&target->label);
|
||||
else
|
||||
error = aa_replace_current_profile(target);
|
||||
error = aa_replace_current_label(&target->label);
|
||||
|
||||
audit:
|
||||
if (!permtest)
|
||||
error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request,
|
||||
name, hname, GLOBAL_ROOT_UID, info, error);
|
||||
error = aa_audit_file(profile, &perms, op, request, name,
|
||||
hname, GLOBAL_ROOT_UID, info, error);
|
||||
|
||||
aa_put_namespace(ns);
|
||||
aa_put_profile(target);
|
||||
aa_put_label(label);
|
||||
put_cred(cred);
|
||||
|
||||
return error;
|
||||
|
||||
Reference in New Issue
Block a user