diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
| commit | f4cdf7ca9a1fdcca413157df19753f388a5a224e (patch) | |
| tree | 60a31895b2446cd7fa27514e8c2843534a0de0da /security/apparmor/task.c | |
| download | linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip | |
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab:
- v4l2-core: added ISP statistics support and per-block validation
- v4l2-core: Allow unknown HDR10 white point and luminance
- New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor
- New codec: Milos: VPU v2.0 codec support
- isp driver: gained support for Dreamchip RPPX1 ISP framework
- vsp1 driver: gained support for RZ/T2H and RZ/N2H
- Novalake driver: gained CVS support for new NVL hardware
- dvb-core: fix feed leak on failed DMX_ADD_PID
- several driver fixes, cleanups and minor improvements
* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: ipu-bridge: check all DMI entries when overriding sensor rotation
media: v4l2-async: avoid deleting unlinked ASC entry on link error
media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
media: intel/ipu6: fix async notifier cleanup leak on parse error
media: staging/ipu7: fix async notifier UAF on probe error path
media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
media: rcar-isp: Fix VSPX reference leaks
media: rcar-isp: Release ISPCORE resources
media: i2c: imx415: Release runtime PM reference on VBLANK error
media: i2c: imx415: Return test pattern write errors
media: renesas: vsp1: Declare index variables in for loop statement
media: renesas: vsp1: Make reset control optional to support platforms without a reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
media: nxp: imx8-isi: Add additional 32-bit RGB format support
media: nxp: imx8-isi: Add 16-bit raw Bayer format support
media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
...
Diffstat (limited to 'security/apparmor/task.c')
| -rw-r--r-- | security/apparmor/task.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/security/apparmor/task.c b/security/apparmor/task.c new file mode 100644 index 000000000..b9fb37381 --- /dev/null +++ b/security/apparmor/task.c @@ -0,0 +1,372 @@ +// 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; +} |
