18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AppArmor security module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file contains AppArmor ipc mediation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 1998-2008 Novell/SUSE
88c2ecf20Sopenharmony_ci * Copyright 2009-2017 Canonical Ltd.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/gfp.h>
128c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "include/audit.h"
158c2ecf20Sopenharmony_ci#include "include/capability.h"
168c2ecf20Sopenharmony_ci#include "include/cred.h"
178c2ecf20Sopenharmony_ci#include "include/policy.h"
188c2ecf20Sopenharmony_ci#include "include/ipc.h"
198c2ecf20Sopenharmony_ci#include "include/sig_names.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/**
228c2ecf20Sopenharmony_ci * audit_ptrace_mask - convert mask to permission string
238c2ecf20Sopenharmony_ci * @mask: permission mask to convert
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Returns: pointer to static string
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistatic const char *audit_ptrace_mask(u32 mask)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	switch (mask) {
308c2ecf20Sopenharmony_ci	case MAY_READ:
318c2ecf20Sopenharmony_ci		return "read";
328c2ecf20Sopenharmony_ci	case MAY_WRITE:
338c2ecf20Sopenharmony_ci		return "trace";
348c2ecf20Sopenharmony_ci	case AA_MAY_BE_READ:
358c2ecf20Sopenharmony_ci		return "readby";
368c2ecf20Sopenharmony_ci	case AA_MAY_BE_TRACED:
378c2ecf20Sopenharmony_ci		return "tracedby";
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci	return "";
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* call back to audit ptrace fields */
438c2ecf20Sopenharmony_cistatic void audit_ptrace_cb(struct audit_buffer *ab, void *va)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct common_audit_data *sa = va;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (aad(sa)->request & AA_PTRACE_PERM_MASK) {
488c2ecf20Sopenharmony_ci		audit_log_format(ab, " requested_mask=\"%s\"",
498c2ecf20Sopenharmony_ci				 audit_ptrace_mask(aad(sa)->request));
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		if (aad(sa)->denied & AA_PTRACE_PERM_MASK) {
528c2ecf20Sopenharmony_ci			audit_log_format(ab, " denied_mask=\"%s\"",
538c2ecf20Sopenharmony_ci					 audit_ptrace_mask(aad(sa)->denied));
548c2ecf20Sopenharmony_ci		}
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	audit_log_format(ab, " peer=");
578c2ecf20Sopenharmony_ci	aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
588c2ecf20Sopenharmony_ci			FLAGS_NONE, GFP_ATOMIC);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* assumes check for PROFILE_MEDIATES is already done */
628c2ecf20Sopenharmony_ci/* TODO: conditionals */
638c2ecf20Sopenharmony_cistatic int profile_ptrace_perm(struct aa_profile *profile,
648c2ecf20Sopenharmony_ci			     struct aa_label *peer, u32 request,
658c2ecf20Sopenharmony_ci			     struct common_audit_data *sa)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct aa_perms perms = { };
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	aad(sa)->peer = peer;
708c2ecf20Sopenharmony_ci	aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request,
718c2ecf20Sopenharmony_ci			       &perms);
728c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, &perms);
738c2ecf20Sopenharmony_ci	return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int profile_tracee_perm(struct aa_profile *tracee,
778c2ecf20Sopenharmony_ci			       struct aa_label *tracer, u32 request,
788c2ecf20Sopenharmony_ci			       struct common_audit_data *sa)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (profile_unconfined(tracee) || unconfined(tracer) ||
818c2ecf20Sopenharmony_ci	    !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
828c2ecf20Sopenharmony_ci		return 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return profile_ptrace_perm(tracee, tracer, request, sa);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int profile_tracer_perm(struct aa_profile *tracer,
888c2ecf20Sopenharmony_ci			       struct aa_label *tracee, u32 request,
898c2ecf20Sopenharmony_ci			       struct common_audit_data *sa)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	if (profile_unconfined(tracer))
928c2ecf20Sopenharmony_ci		return 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
958c2ecf20Sopenharmony_ci		return profile_ptrace_perm(tracer, tracee, request, sa);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* profile uses the old style capability check for ptrace */
988c2ecf20Sopenharmony_ci	if (&tracer->label == tracee)
998c2ecf20Sopenharmony_ci		return 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	aad(sa)->label = &tracer->label;
1028c2ecf20Sopenharmony_ci	aad(sa)->peer = tracee;
1038c2ecf20Sopenharmony_ci	aad(sa)->request = 0;
1048c2ecf20Sopenharmony_ci	aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
1058c2ecf20Sopenharmony_ci				    CAP_OPT_NONE);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/**
1118c2ecf20Sopenharmony_ci * aa_may_ptrace - test if tracer task can trace the tracee
1128c2ecf20Sopenharmony_ci * @tracer: label of the task doing the tracing  (NOT NULL)
1138c2ecf20Sopenharmony_ci * @tracee: task label to be traced
1148c2ecf20Sopenharmony_ci * @request: permission request
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci * Returns: %0 else error code if permission denied or error
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_ciint aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
1198c2ecf20Sopenharmony_ci		  u32 request)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct aa_profile *profile;
1228c2ecf20Sopenharmony_ci	u32 xrequest = request << PTRACE_PERM_SHIFT;
1238c2ecf20Sopenharmony_ci	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return xcheck_labels(tracer, tracee, profile,
1268c2ecf20Sopenharmony_ci			profile_tracer_perm(profile, tracee, request, &sa),
1278c2ecf20Sopenharmony_ci			profile_tracee_perm(profile, tracer, xrequest, &sa));
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline int map_signal_num(int sig)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (sig > SIGRTMAX)
1348c2ecf20Sopenharmony_ci		return SIGUNKNOWN;
1358c2ecf20Sopenharmony_ci	else if (sig >= SIGRTMIN)
1368c2ecf20Sopenharmony_ci		return sig - SIGRTMIN + SIGRT_BASE;
1378c2ecf20Sopenharmony_ci	else if (sig < MAXMAPPED_SIG)
1388c2ecf20Sopenharmony_ci		return sig_map[sig];
1398c2ecf20Sopenharmony_ci	return SIGUNKNOWN;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * audit_signal_mask - convert mask to permission string
1448c2ecf20Sopenharmony_ci * @mask: permission mask to convert
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Returns: pointer to static string
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistatic const char *audit_signal_mask(u32 mask)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	if (mask & MAY_READ)
1518c2ecf20Sopenharmony_ci		return "receive";
1528c2ecf20Sopenharmony_ci	if (mask & MAY_WRITE)
1538c2ecf20Sopenharmony_ci		return "send";
1548c2ecf20Sopenharmony_ci	return "";
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/**
1588c2ecf20Sopenharmony_ci * audit_cb - call back for signal specific audit fields
1598c2ecf20Sopenharmony_ci * @ab: audit_buffer  (NOT NULL)
1608c2ecf20Sopenharmony_ci * @va: audit struct to audit values of  (NOT NULL)
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_cistatic void audit_signal_cb(struct audit_buffer *ab, void *va)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct common_audit_data *sa = va;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
1678c2ecf20Sopenharmony_ci		audit_log_format(ab, " requested_mask=\"%s\"",
1688c2ecf20Sopenharmony_ci				 audit_signal_mask(aad(sa)->request));
1698c2ecf20Sopenharmony_ci		if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
1708c2ecf20Sopenharmony_ci			audit_log_format(ab, " denied_mask=\"%s\"",
1718c2ecf20Sopenharmony_ci					 audit_signal_mask(aad(sa)->denied));
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	if (aad(sa)->signal == SIGUNKNOWN)
1758c2ecf20Sopenharmony_ci		audit_log_format(ab, "signal=unknown(%d)",
1768c2ecf20Sopenharmony_ci				 aad(sa)->unmappedsig);
1778c2ecf20Sopenharmony_ci	else if (aad(sa)->signal < MAXMAPPED_SIGNAME)
1788c2ecf20Sopenharmony_ci		audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
1798c2ecf20Sopenharmony_ci	else
1808c2ecf20Sopenharmony_ci		audit_log_format(ab, " signal=rtmin+%d",
1818c2ecf20Sopenharmony_ci				 aad(sa)->signal - SIGRT_BASE);
1828c2ecf20Sopenharmony_ci	audit_log_format(ab, " peer=");
1838c2ecf20Sopenharmony_ci	aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
1848c2ecf20Sopenharmony_ci			FLAGS_NONE, GFP_ATOMIC);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int profile_signal_perm(struct aa_profile *profile,
1888c2ecf20Sopenharmony_ci			       struct aa_label *peer, u32 request,
1898c2ecf20Sopenharmony_ci			       struct common_audit_data *sa)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct aa_perms perms;
1928c2ecf20Sopenharmony_ci	unsigned int state;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (profile_unconfined(profile) ||
1958c2ecf20Sopenharmony_ci	    !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
1968c2ecf20Sopenharmony_ci		return 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	aad(sa)->peer = peer;
1998c2ecf20Sopenharmony_ci	/* TODO: secondary cache check <profile, profile, perm> */
2008c2ecf20Sopenharmony_ci	state = aa_dfa_next(profile->policy.dfa,
2018c2ecf20Sopenharmony_ci			    profile->policy.start[AA_CLASS_SIGNAL],
2028c2ecf20Sopenharmony_ci			    aad(sa)->signal);
2038c2ecf20Sopenharmony_ci	aa_label_match(profile, peer, state, false, request, &perms);
2048c2ecf20Sopenharmony_ci	aa_apply_modes_to_perms(profile, &perms);
2058c2ecf20Sopenharmony_ci	return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciint aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct aa_profile *profile;
2118c2ecf20Sopenharmony_ci	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	aad(&sa)->signal = map_signal_num(sig);
2148c2ecf20Sopenharmony_ci	aad(&sa)->unmappedsig = sig;
2158c2ecf20Sopenharmony_ci	return xcheck_labels(sender, target, profile,
2168c2ecf20Sopenharmony_ci			profile_signal_perm(profile, target, MAY_WRITE, &sa),
2178c2ecf20Sopenharmony_ci			profile_signal_perm(profile, sender, MAY_READ, &sa));
2188c2ecf20Sopenharmony_ci}
219