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