162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AppArmor security module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file contains AppArmor task related definitions and mediation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2017 Canonical Ltd. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO 1062306a36Sopenharmony_ci * If a task uses change_hat it currently does not return to the old 1162306a36Sopenharmony_ci * cred or task context but instead creates a new one. Ideally the task 1262306a36Sopenharmony_ci * should return to the previous cred if it has not been modified. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/gfp.h> 1662306a36Sopenharmony_ci#include <linux/ptrace.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "include/audit.h" 1962306a36Sopenharmony_ci#include "include/cred.h" 2062306a36Sopenharmony_ci#include "include/policy.h" 2162306a36Sopenharmony_ci#include "include/task.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * aa_get_task_label - Get another task's label 2562306a36Sopenharmony_ci * @task: task to query (NOT NULL) 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Returns: counted reference to @task's label 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistruct aa_label *aa_get_task_label(struct task_struct *task) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct aa_label *p; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci rcu_read_lock(); 3462306a36Sopenharmony_ci p = aa_get_newest_cred_label(__task_cred(task)); 3562306a36Sopenharmony_ci rcu_read_unlock(); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return p; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * aa_replace_current_label - replace the current tasks label 4262306a36Sopenharmony_ci * @label: new label (NOT NULL) 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Returns: 0 or error on failure 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ciint aa_replace_current_label(struct aa_label *label) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct aa_label *old = aa_current_raw_label(); 4962306a36Sopenharmony_ci struct aa_task_ctx *ctx = task_ctx(current); 5062306a36Sopenharmony_ci struct cred *new; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci AA_BUG(!label); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (old == label) 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (current_cred() != current_real_cred()) 5862306a36Sopenharmony_ci return -EBUSY; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci new = prepare_creds(); 6162306a36Sopenharmony_ci if (!new) 6262306a36Sopenharmony_ci return -ENOMEM; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (ctx->nnp && label_is_stale(ctx->nnp)) { 6562306a36Sopenharmony_ci struct aa_label *tmp = ctx->nnp; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ctx->nnp = aa_get_newest_label(tmp); 6862306a36Sopenharmony_ci aa_put_label(tmp); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci if (unconfined(label) || (labels_ns(old) != labels_ns(label))) 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * if switching to unconfined or a different label namespace 7362306a36Sopenharmony_ci * clear out context state 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci aa_clear_task_ctx_trans(task_ctx(current)); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * be careful switching cred label, when racing replacement it 7962306a36Sopenharmony_ci * is possible that the cred labels's->proxy->label is the reference 8062306a36Sopenharmony_ci * keeping @label valid, so make sure to get its reference before 8162306a36Sopenharmony_ci * dropping the reference on the cred's label 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci aa_get_label(label); 8462306a36Sopenharmony_ci aa_put_label(cred_label(new)); 8562306a36Sopenharmony_ci set_cred_label(new, label); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci commit_creds(new); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/** 9362306a36Sopenharmony_ci * aa_set_current_onexec - set the tasks change_profile to happen onexec 9462306a36Sopenharmony_ci * @label: system label to set at exec (MAYBE NULL to clear value) 9562306a36Sopenharmony_ci * @stack: whether stacking should be done 9662306a36Sopenharmony_ci * Returns: 0 or error on failure 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ciint aa_set_current_onexec(struct aa_label *label, bool stack) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct aa_task_ctx *ctx = task_ctx(current); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci aa_get_label(label); 10362306a36Sopenharmony_ci aa_put_label(ctx->onexec); 10462306a36Sopenharmony_ci ctx->onexec = label; 10562306a36Sopenharmony_ci ctx->token = stack; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * aa_set_current_hat - set the current tasks hat 11262306a36Sopenharmony_ci * @label: label to set as the current hat (NOT NULL) 11362306a36Sopenharmony_ci * @token: token value that must be specified to change from the hat 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Do switch of tasks hat. If the task is currently in a hat 11662306a36Sopenharmony_ci * validate the token to match. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * Returns: 0 or error on failure 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ciint aa_set_current_hat(struct aa_label *label, u64 token) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct aa_task_ctx *ctx = task_ctx(current); 12362306a36Sopenharmony_ci struct cred *new; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci new = prepare_creds(); 12662306a36Sopenharmony_ci if (!new) 12762306a36Sopenharmony_ci return -ENOMEM; 12862306a36Sopenharmony_ci AA_BUG(!label); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!ctx->previous) { 13162306a36Sopenharmony_ci /* transfer refcount */ 13262306a36Sopenharmony_ci ctx->previous = cred_label(new); 13362306a36Sopenharmony_ci ctx->token = token; 13462306a36Sopenharmony_ci } else if (ctx->token == token) { 13562306a36Sopenharmony_ci aa_put_label(cred_label(new)); 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci /* previous_profile && ctx->token != token */ 13862306a36Sopenharmony_ci abort_creds(new); 13962306a36Sopenharmony_ci return -EACCES; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci set_cred_label(new, aa_get_newest_label(label)); 14362306a36Sopenharmony_ci /* clear exec on switching context */ 14462306a36Sopenharmony_ci aa_put_label(ctx->onexec); 14562306a36Sopenharmony_ci ctx->onexec = NULL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci commit_creds(new); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * aa_restore_previous_label - exit from hat context restoring previous label 15362306a36Sopenharmony_ci * @token: the token that must be matched to exit hat context 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * Attempt to return out of a hat to the previous label. The token 15662306a36Sopenharmony_ci * must match the stored token value. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Returns: 0 or error of failure 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ciint aa_restore_previous_label(u64 token) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct aa_task_ctx *ctx = task_ctx(current); 16362306a36Sopenharmony_ci struct cred *new; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (ctx->token != token) 16662306a36Sopenharmony_ci return -EACCES; 16762306a36Sopenharmony_ci /* ignore restores when there is no saved label */ 16862306a36Sopenharmony_ci if (!ctx->previous) 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci new = prepare_creds(); 17262306a36Sopenharmony_ci if (!new) 17362306a36Sopenharmony_ci return -ENOMEM; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci aa_put_label(cred_label(new)); 17662306a36Sopenharmony_ci set_cred_label(new, aa_get_newest_label(ctx->previous)); 17762306a36Sopenharmony_ci AA_BUG(!cred_label(new)); 17862306a36Sopenharmony_ci /* clear exec && prev information when restoring to previous context */ 17962306a36Sopenharmony_ci aa_clear_task_ctx_trans(ctx); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci commit_creds(new); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/** 18762306a36Sopenharmony_ci * audit_ptrace_mask - convert mask to permission string 18862306a36Sopenharmony_ci * @mask: permission mask to convert 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * Returns: pointer to static string 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic const char *audit_ptrace_mask(u32 mask) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci switch (mask) { 19562306a36Sopenharmony_ci case MAY_READ: 19662306a36Sopenharmony_ci return "read"; 19762306a36Sopenharmony_ci case MAY_WRITE: 19862306a36Sopenharmony_ci return "trace"; 19962306a36Sopenharmony_ci case AA_MAY_BE_READ: 20062306a36Sopenharmony_ci return "readby"; 20162306a36Sopenharmony_ci case AA_MAY_BE_TRACED: 20262306a36Sopenharmony_ci return "tracedby"; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return ""; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* call back to audit ptrace fields */ 20862306a36Sopenharmony_cistatic void audit_ptrace_cb(struct audit_buffer *ab, void *va) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct common_audit_data *sa = va; 21162306a36Sopenharmony_ci struct apparmor_audit_data *ad = aad(sa); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (ad->request & AA_PTRACE_PERM_MASK) { 21462306a36Sopenharmony_ci audit_log_format(ab, " requested_mask=\"%s\"", 21562306a36Sopenharmony_ci audit_ptrace_mask(ad->request)); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ad->denied & AA_PTRACE_PERM_MASK) { 21862306a36Sopenharmony_ci audit_log_format(ab, " denied_mask=\"%s\"", 21962306a36Sopenharmony_ci audit_ptrace_mask(ad->denied)); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci audit_log_format(ab, " peer="); 22362306a36Sopenharmony_ci aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer, 22462306a36Sopenharmony_ci FLAGS_NONE, GFP_ATOMIC); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* assumes check for RULE_MEDIATES is already done */ 22862306a36Sopenharmony_ci/* TODO: conditionals */ 22962306a36Sopenharmony_cistatic int profile_ptrace_perm(const struct cred *cred, 23062306a36Sopenharmony_ci struct aa_profile *profile, 23162306a36Sopenharmony_ci struct aa_label *peer, u32 request, 23262306a36Sopenharmony_ci struct apparmor_audit_data *ad) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct aa_ruleset *rules = list_first_entry(&profile->rules, 23562306a36Sopenharmony_ci typeof(*rules), list); 23662306a36Sopenharmony_ci struct aa_perms perms = { }; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ad->subj_cred = cred; 23962306a36Sopenharmony_ci ad->peer = peer; 24062306a36Sopenharmony_ci aa_profile_match_label(profile, rules, peer, AA_CLASS_PTRACE, request, 24162306a36Sopenharmony_ci &perms); 24262306a36Sopenharmony_ci aa_apply_modes_to_perms(profile, &perms); 24362306a36Sopenharmony_ci return aa_check_perms(profile, &perms, request, ad, audit_ptrace_cb); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int profile_tracee_perm(const struct cred *cred, 24762306a36Sopenharmony_ci struct aa_profile *tracee, 24862306a36Sopenharmony_ci struct aa_label *tracer, u32 request, 24962306a36Sopenharmony_ci struct apparmor_audit_data *ad) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (profile_unconfined(tracee) || unconfined(tracer) || 25262306a36Sopenharmony_ci !ANY_RULE_MEDIATES(&tracee->rules, AA_CLASS_PTRACE)) 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return profile_ptrace_perm(cred, tracee, tracer, request, ad); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int profile_tracer_perm(const struct cred *cred, 25962306a36Sopenharmony_ci struct aa_profile *tracer, 26062306a36Sopenharmony_ci struct aa_label *tracee, u32 request, 26162306a36Sopenharmony_ci struct apparmor_audit_data *ad) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci if (profile_unconfined(tracer)) 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (ANY_RULE_MEDIATES(&tracer->rules, AA_CLASS_PTRACE)) 26762306a36Sopenharmony_ci return profile_ptrace_perm(cred, tracer, tracee, request, ad); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* profile uses the old style capability check for ptrace */ 27062306a36Sopenharmony_ci if (&tracer->label == tracee) 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ad->subj_label = &tracer->label; 27462306a36Sopenharmony_ci ad->peer = tracee; 27562306a36Sopenharmony_ci ad->request = 0; 27662306a36Sopenharmony_ci ad->error = aa_capable(cred, &tracer->label, CAP_SYS_PTRACE, 27762306a36Sopenharmony_ci CAP_OPT_NONE); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return aa_audit(AUDIT_APPARMOR_AUTO, tracer, ad, audit_ptrace_cb); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/** 28362306a36Sopenharmony_ci * aa_may_ptrace - test if tracer task can trace the tracee 28462306a36Sopenharmony_ci * @tracer: label of the task doing the tracing (NOT NULL) 28562306a36Sopenharmony_ci * @tracee: task label to be traced 28662306a36Sopenharmony_ci * @request: permission request 28762306a36Sopenharmony_ci * 28862306a36Sopenharmony_ci * Returns: %0 else error code if permission denied or error 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ciint aa_may_ptrace(const struct cred *tracer_cred, struct aa_label *tracer, 29162306a36Sopenharmony_ci const struct cred *tracee_cred, struct aa_label *tracee, 29262306a36Sopenharmony_ci u32 request) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct aa_profile *profile; 29562306a36Sopenharmony_ci u32 xrequest = request << PTRACE_PERM_SHIFT; 29662306a36Sopenharmony_ci DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_PTRACE, OP_PTRACE); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return xcheck_labels(tracer, tracee, profile, 29962306a36Sopenharmony_ci profile_tracer_perm(tracer_cred, profile, tracee, 30062306a36Sopenharmony_ci request, &sa), 30162306a36Sopenharmony_ci profile_tracee_perm(tracee_cred, profile, tracer, 30262306a36Sopenharmony_ci xrequest, &sa)); 30362306a36Sopenharmony_ci} 304