Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373// SPDX-License-Identifier: GPL-2.0-only/* * AppArmor security module * * This file contains AppArmor task related definitions and mediation * * Copyright 2017 Canonical Ltd. * * TODO * If a task uses change_hat it currently does not return to the old * cred or task context but instead creates a new one. Ideally the task * should return to the previous cred if it has not been modified. */
#include <linux/gfp.h>#include <linux/ptrace.h>
#include "include/path.h"#include "include/audit.h"#include "include/cred.h"#include "include/policy.h"#include "include/task.h"
/** * aa_get_task_label - Get another task's label * @task: task to query (NOT NULL) * * Returns: counted reference to @task's label */struct aa_label *aa_get_task_label(struct task_struct *task){ struct aa_label *p;
rcu_read_lock(); p = aa_get_newest_cred_label(__task_cred(task)); rcu_read_unlock();
return p;}
/** * aa_replace_current_label - replace the current tasks label * @label: new label (NOT NULL) * * Returns: 0 or error on failure */int aa_replace_current_label(struct aa_label *label){ struct aa_label *old = aa_current_raw_label(); struct aa_task_ctx *ctx = task_ctx(current); struct cred *new;
AA_BUG(!label);
if (old == label) return 0;
if (current_cred() != current_real_cred()) return -EBUSY;
new = prepare_creds(); if (!new) return -ENOMEM;
if (ctx->nnp && label_is_stale(ctx->nnp)) { struct aa_label *tmp = ctx->nnp;
ctx->nnp = aa_get_newest_label(tmp); aa_put_label(tmp); } if (unconfined(label) || (labels_ns(old) != labels_ns(label))) /* * if switching to unconfined or a different label namespace * clear out context state */ aa_clear_task_ctx_trans(task_ctx(current));
/* * be careful switching cred label, when racing replacement it * is possible that the cred labels's->proxy->label is the reference * keeping @label valid, so make sure to get its reference before * dropping the reference on the cred's label */ aa_get_label(label); aa_put_label(cred_label(new)); set_cred_label(new, label);
commit_creds(new); return 0;}
/** * aa_set_current_onexec - set the tasks change_profile to happen onexec * @label: system label to set at exec (MAYBE NULL to clear value) * @stack: whether stacking should be done */void aa_set_current_onexec(struct aa_label *label, bool stack){ struct aa_task_ctx *ctx = task_ctx(current);
aa_get_label(label); aa_put_label(ctx->onexec); ctx->onexec = label; ctx->token = stack;}
/** * aa_set_current_hat - set the current tasks hat * @label: label to set as the current hat (NOT NULL) * @token: token value that must be specified to change from the hat * * Do switch of tasks hat. If the task is currently in a hat * validate the token to match. * * Returns: 0 or error on failure */int aa_set_current_hat(struct aa_label *label, u64 token){ struct aa_task_ctx *ctx = task_ctx(current); struct cred *new;
new = prepare_creds(); if (!new) return -ENOMEM; AA_BUG(!label);
if (!ctx->previous) { /* transfer refcount */ ctx->previous = cred_label(new); ctx->token = token; } else if (ctx->token == token) { aa_put_label(cred_label(new)); } else { /* previous_profile && ctx->token != token */ abort_creds(new); return -EACCES; }
set_cred_label(new, aa_get_newest_label(label)); /* clear exec on switching context */ aa_put_label(ctx->onexec); ctx->onexec = NULL;
commit_creds(new); return 0;}
/** * aa_restore_previous_label - exit from hat context restoring previous label * @token: the token that must be matched to exit hat context * * Attempt to return out of a hat to the previous label. The token * must match the stored token value. * * Returns: 0 or error of failure */int aa_restore_previous_label(u64 token){ struct aa_task_ctx *ctx = task_ctx(current); struct cred *new;
if (ctx->token != token) return -EACCES; /* ignore restores when there is no saved label */ if (!ctx->previous) return 0;
new = prepare_creds(); if (!new) return -ENOMEM;
aa_put_label(cred_label(new)); set_cred_label(new, aa_get_newest_label(ctx->previous)); AA_BUG(!cred_label(new)); /* clear exec && prev information when restoring to previous context */ aa_clear_task_ctx_trans(ctx);
commit_creds(new);
return 0;}
/** * audit_ptrace_mask - convert mask to permission string * @mask: permission mask to convert * * Returns: pointer to static string */static const char *audit_ptrace_mask(u32 mask){ switch (mask) { case MAY_READ: return "read"; case MAY_WRITE: return "trace"; case AA_MAY_BE_READ: return "readby"; case AA_MAY_BE_TRACED: return "tracedby"; } return "";}
/* call back to audit ptrace fields */static void audit_ptrace_cb(struct audit_buffer *ab, void *va){ struct common_audit_data *sa = va; struct apparmor_audit_data *ad = aad(sa);
if (ad->request & AA_PTRACE_PERM_MASK) { audit_log_format(ab, " requested_mask=\"%s\"", audit_ptrace_mask(ad->request));
if (ad->denied & AA_PTRACE_PERM_MASK) { audit_log_format(ab, " denied_mask=\"%s\"", audit_ptrace_mask(ad->denied)); } } audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, FLAGS_NONE, GFP_ATOMIC);}
/* assumes check for RULE_MEDIATES is already done *//* TODO: conditionals */static int profile_ptrace_perm(const struct cred *cred, struct aa_profile *profile, struct aa_label *peer, u32 request, struct apparmor_audit_data *ad){ struct aa_ruleset *rules = profile->label.rules[0]; struct aa_perms perms = { };
ad->subj_cred = cred; ad->peer = peer; aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request, &perms); aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, ad, audit_ptrace_cb);}
static int profile_tracee_perm(const struct cred *cred, struct aa_profile *tracee, struct aa_label *tracer, u32 request, struct apparmor_audit_data *ad){ if (profile_unconfined(tracee) || unconfined(tracer) || !label_mediates(&tracee->label, AA_CLASS_PTRACE)) return 0;
return profile_ptrace_perm(cred, tracee, tracer, request, ad);}
static int profile_tracer_perm(const struct cred *cred, struct aa_profile *tracer, struct aa_label *tracee, u32 request, struct apparmor_audit_data *ad){ if (profile_unconfined(tracer)) return 0;
if (label_mediates(&tracer->label, AA_CLASS_PTRACE)) return profile_ptrace_perm(cred, tracer, tracee, request, ad);
/* profile uses the old style capability check for ptrace */ if (&tracer->label == tracee) return 0;
ad->subj_label = &tracer->label; ad->peer = tracee; ad->request = 0; ad->error = aa_capable(cred, &tracer->label, CAP_SYS_PTRACE, CAP_OPT_NONE);
return aa_audit(AUDIT_APPARMOR_AUTO, tracer, ad, audit_ptrace_cb);}
/** * aa_may_ptrace - test if tracer task can trace the tracee * @tracer_cred: cred of task doing the tracing (NOT NULL) * @tracer: label of the task doing the tracing (NOT NULL) * @tracee_cred: cred of task to be traced * @tracee: task label to be traced * @request: permission request * * Returns: %0 else error code if permission denied or error */int aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer, const struct cred *tracee_cred, struct aa_label *tracee, u32 request){ struct aa_profile *profile; u32 xrequest = request << PTRACE_PERM_SHIFT; DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE);
return xcheck_labels(tracer, tracee, profile, profile_tracer_perm(tracer_cred, profile, tracee, request, &sa), profile_tracee_perm(tracee_cred, profile, tracer, xrequest, &sa));}
static const char *get_current_exe_path(char *buffer, int buffer_size){ struct file *exe_file; struct path p; const char *path_str;
exe_file = get_task_exe_file(current); if (!exe_file) return ERR_PTR(-ENOENT); p = exe_file->f_path; path_get(&p);
if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL)) path_str = ERR_PTR(-ENOMEM);
fput(exe_file); path_put(&p);
return path_str;}
/* call back to audit ptrace fields */static void audit_ns_cb(struct audit_buffer *ab, void *va){ struct apparmor_audit_data *ad = aad_of_va(va); char *buffer; const char *path;
if (ad->request & AA_USERNS_CREATE) audit_log_format(ab, " requested=\"userns_create\"");
if (ad->denied & AA_USERNS_CREATE) audit_log_format(ab, " denied=\"userns_create\"");
buffer = aa_get_buffer(false); if (!buffer) return; // OOM path = get_current_exe_path(buffer, aa_g_path_max); if (!IS_ERR(path)) audit_log_format(ab, " execpath=\"%s\"", path); aa_put_buffer(buffer);}
int aa_profile_ns_perm(struct aa_profile *profile, struct apparmor_audit_data *ad, u32 request){ struct aa_perms perms = { }; int error = 0;
ad->subj_label = &profile->label; ad->request = request;
if (!profile_unconfined(profile)) { struct aa_ruleset *rules = profile->label.rules[0]; aa_state_t state;
state = RULE_MEDIATES(rules, ad->class); if (!state) /* TODO: add flag to complain about unmediated */ return 0; perms = *aa_lookup_perms(rules->policy, state); aa_apply_modes_to_perms(profile, &perms); error = aa_check_perms(profile, &perms, request, ad, audit_ns_cb); }
return error;}