xref: /openbmc/linux/security/apparmor/ipc.c (revision b886d83c5b621abc84ff9616f14c529be3f6b147)
1*b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20ed3b28aSJohn Johansen /*
30ed3b28aSJohn Johansen  * AppArmor security module
40ed3b28aSJohn Johansen  *
50ed3b28aSJohn Johansen  * This file contains AppArmor ipc mediation
60ed3b28aSJohn Johansen  *
70ed3b28aSJohn Johansen  * Copyright (C) 1998-2008 Novell/SUSE
8b2d09ae4SJohn Johansen  * Copyright 2009-2017 Canonical Ltd.
90ed3b28aSJohn Johansen  */
100ed3b28aSJohn Johansen 
110ed3b28aSJohn Johansen #include <linux/gfp.h>
120ed3b28aSJohn Johansen #include <linux/ptrace.h>
130ed3b28aSJohn Johansen 
140ed3b28aSJohn Johansen #include "include/audit.h"
150ed3b28aSJohn Johansen #include "include/capability.h"
16d8889d49SJohn Johansen #include "include/cred.h"
170ed3b28aSJohn Johansen #include "include/policy.h"
1833f8bf58SJames Morris #include "include/ipc.h"
19cd1dbf76SJohn Johansen #include "include/sig_names.h"
200ed3b28aSJohn Johansen 
21290f458aSJohn Johansen /**
22290f458aSJohn Johansen  * audit_ptrace_mask - convert mask to permission string
23290f458aSJohn Johansen  * @buffer: buffer to write string to (NOT NULL)
24290f458aSJohn Johansen  * @mask: permission mask to convert
25290f458aSJohn Johansen  */
26290f458aSJohn Johansen static void audit_ptrace_mask(struct audit_buffer *ab, u32 mask)
27290f458aSJohn Johansen {
28290f458aSJohn Johansen 	switch (mask) {
29290f458aSJohn Johansen 	case MAY_READ:
30290f458aSJohn Johansen 		audit_log_string(ab, "read");
31290f458aSJohn Johansen 		break;
32290f458aSJohn Johansen 	case MAY_WRITE:
33290f458aSJohn Johansen 		audit_log_string(ab, "trace");
34290f458aSJohn Johansen 		break;
35290f458aSJohn Johansen 	case AA_MAY_BE_READ:
36290f458aSJohn Johansen 		audit_log_string(ab, "readby");
37290f458aSJohn Johansen 		break;
38290f458aSJohn Johansen 	case AA_MAY_BE_TRACED:
39290f458aSJohn Johansen 		audit_log_string(ab, "tracedby");
40290f458aSJohn Johansen 		break;
41290f458aSJohn Johansen 	}
42290f458aSJohn Johansen }
43290f458aSJohn Johansen 
440ed3b28aSJohn Johansen /* call back to audit ptrace fields */
45637f688dSJohn Johansen static void audit_ptrace_cb(struct audit_buffer *ab, void *va)
460ed3b28aSJohn Johansen {
470ed3b28aSJohn Johansen 	struct common_audit_data *sa = va;
48b2d09ae4SJohn Johansen 
49290f458aSJohn Johansen 	if (aad(sa)->request & AA_PTRACE_PERM_MASK) {
50290f458aSJohn Johansen 		audit_log_format(ab, " requested_mask=");
51290f458aSJohn Johansen 		audit_ptrace_mask(ab, aad(sa)->request);
52290f458aSJohn Johansen 
53290f458aSJohn Johansen 		if (aad(sa)->denied & AA_PTRACE_PERM_MASK) {
54290f458aSJohn Johansen 			audit_log_format(ab, " denied_mask=");
55290f458aSJohn Johansen 			audit_ptrace_mask(ab, aad(sa)->denied);
56290f458aSJohn Johansen 		}
57290f458aSJohn Johansen 	}
58ef88a7acSJohn Johansen 	audit_log_format(ab, " peer=");
59637f688dSJohn Johansen 	aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
60637f688dSJohn Johansen 			FLAGS_NONE, GFP_ATOMIC);
610ed3b28aSJohn Johansen }
620ed3b28aSJohn Johansen 
630dda0b3fSJohn Johansen /* assumes check for PROFILE_MEDIATES is already done */
64290f458aSJohn Johansen /* TODO: conditionals */
65290f458aSJohn Johansen static int profile_ptrace_perm(struct aa_profile *profile,
660dda0b3fSJohn Johansen 			     struct aa_label *peer, u32 request,
67290f458aSJohn Johansen 			     struct common_audit_data *sa)
68290f458aSJohn Johansen {
69290f458aSJohn Johansen 	struct aa_perms perms = { };
70290f458aSJohn Johansen 
710dda0b3fSJohn Johansen 	aad(sa)->peer = peer;
720dda0b3fSJohn Johansen 	aa_profile_match_label(profile, peer, AA_CLASS_PTRACE, request,
73290f458aSJohn Johansen 			       &perms);
74290f458aSJohn Johansen 	aa_apply_modes_to_perms(profile, &perms);
75290f458aSJohn Johansen 	return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb);
76290f458aSJohn Johansen }
77290f458aSJohn Johansen 
780dda0b3fSJohn Johansen static int profile_tracee_perm(struct aa_profile *tracee,
790dda0b3fSJohn Johansen 			       struct aa_label *tracer, u32 request,
80b2d09ae4SJohn Johansen 			       struct common_audit_data *sa)
810ed3b28aSJohn Johansen {
820dda0b3fSJohn Johansen 	if (profile_unconfined(tracee) || unconfined(tracer) ||
830dda0b3fSJohn Johansen 	    !PROFILE_MEDIATES(tracee, AA_CLASS_PTRACE))
840dda0b3fSJohn Johansen 		return 0;
850dda0b3fSJohn Johansen 
860dda0b3fSJohn Johansen 	return profile_ptrace_perm(tracee, tracer, request, sa);
870dda0b3fSJohn Johansen }
880dda0b3fSJohn Johansen 
890dda0b3fSJohn Johansen static int profile_tracer_perm(struct aa_profile *tracer,
900dda0b3fSJohn Johansen 			       struct aa_label *tracee, u32 request,
910dda0b3fSJohn Johansen 			       struct common_audit_data *sa)
920dda0b3fSJohn Johansen {
930dda0b3fSJohn Johansen 	if (profile_unconfined(tracer))
940dda0b3fSJohn Johansen 		return 0;
950dda0b3fSJohn Johansen 
96290f458aSJohn Johansen 	if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE))
970dda0b3fSJohn Johansen 		return profile_ptrace_perm(tracer, tracee, request, sa);
980dda0b3fSJohn Johansen 
990dda0b3fSJohn Johansen 	/* profile uses the old style capability check for ptrace */
1000dda0b3fSJohn Johansen 	if (&tracer->label == tracee)
101b2d09ae4SJohn Johansen 		return 0;
1020ed3b28aSJohn Johansen 
103b2d09ae4SJohn Johansen 	aad(sa)->label = &tracer->label;
1040dda0b3fSJohn Johansen 	aad(sa)->peer = tracee;
105b2d09ae4SJohn Johansen 	aad(sa)->request = 0;
106c1a85a00SMicah Morton 	aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
107c1a85a00SMicah Morton 				    CAP_OPT_NONE);
108ef88a7acSJohn Johansen 
109b2d09ae4SJohn Johansen 	return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
1100ed3b28aSJohn Johansen }
1110ed3b28aSJohn Johansen 
1120ed3b28aSJohn Johansen /**
1130ed3b28aSJohn Johansen  * aa_may_ptrace - test if tracer task can trace the tracee
114b2d09ae4SJohn Johansen  * @tracer: label of the task doing the tracing  (NOT NULL)
115b2d09ae4SJohn Johansen  * @tracee: task label to be traced
116b2d09ae4SJohn Johansen  * @request: permission request
1170ed3b28aSJohn Johansen  *
1180ed3b28aSJohn Johansen  * Returns: %0 else error code if permission denied or error
1190ed3b28aSJohn Johansen  */
120b2d09ae4SJohn Johansen int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
121b2d09ae4SJohn Johansen 		  u32 request)
1220ed3b28aSJohn Johansen {
1230dda0b3fSJohn Johansen 	struct aa_profile *profile;
1240dda0b3fSJohn Johansen 	u32 xrequest = request << PTRACE_PERM_SHIFT;
125b2d09ae4SJohn Johansen 	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_PTRACE);
1260ed3b28aSJohn Johansen 
1270dda0b3fSJohn Johansen 	return xcheck_labels(tracer, tracee, profile,
1280dda0b3fSJohn Johansen 			profile_tracer_perm(profile, tracee, request, &sa),
1290dda0b3fSJohn Johansen 			profile_tracee_perm(profile, tracer, xrequest, &sa));
1300ed3b28aSJohn Johansen }
1310ed3b28aSJohn Johansen 
1320ed3b28aSJohn Johansen 
133cd1dbf76SJohn Johansen static inline int map_signal_num(int sig)
134cd1dbf76SJohn Johansen {
135cd1dbf76SJohn Johansen 	if (sig > SIGRTMAX)
136cd1dbf76SJohn Johansen 		return SIGUNKNOWN;
137cd1dbf76SJohn Johansen 	else if (sig >= SIGRTMIN)
1383acfd5f5SJohn Johansen 		return sig - SIGRTMIN + SIGRT_BASE;
139f7dc4c9aSJohn Johansen 	else if (sig < MAXMAPPED_SIG)
140cd1dbf76SJohn Johansen 		return sig_map[sig];
141cd1dbf76SJohn Johansen 	return SIGUNKNOWN;
142cd1dbf76SJohn Johansen }
143cd1dbf76SJohn Johansen 
144cd1dbf76SJohn Johansen /**
145cd1dbf76SJohn Johansen  * audit_file_mask - convert mask to permission string
146cd1dbf76SJohn Johansen  * @buffer: buffer to write string to (NOT NULL)
147cd1dbf76SJohn Johansen  * @mask: permission mask to convert
148cd1dbf76SJohn Johansen  */
149cd1dbf76SJohn Johansen static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
150cd1dbf76SJohn Johansen {
151cd1dbf76SJohn Johansen 	if (mask & MAY_READ)
152cd1dbf76SJohn Johansen 		audit_log_string(ab, "receive");
153cd1dbf76SJohn Johansen 	if (mask & MAY_WRITE)
154cd1dbf76SJohn Johansen 		audit_log_string(ab, "send");
155cd1dbf76SJohn Johansen }
156cd1dbf76SJohn Johansen 
157cd1dbf76SJohn Johansen /**
158cd1dbf76SJohn Johansen  * audit_cb - call back for signal specific audit fields
159cd1dbf76SJohn Johansen  * @ab: audit_buffer  (NOT NULL)
160cd1dbf76SJohn Johansen  * @va: audit struct to audit values of  (NOT NULL)
161cd1dbf76SJohn Johansen  */
162cd1dbf76SJohn Johansen static void audit_signal_cb(struct audit_buffer *ab, void *va)
163cd1dbf76SJohn Johansen {
164cd1dbf76SJohn Johansen 	struct common_audit_data *sa = va;
165cd1dbf76SJohn Johansen 
166cd1dbf76SJohn Johansen 	if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
167cd1dbf76SJohn Johansen 		audit_log_format(ab, " requested_mask=");
168cd1dbf76SJohn Johansen 		audit_signal_mask(ab, aad(sa)->request);
169cd1dbf76SJohn Johansen 		if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
170cd1dbf76SJohn Johansen 			audit_log_format(ab, " denied_mask=");
171cd1dbf76SJohn Johansen 			audit_signal_mask(ab, aad(sa)->denied);
172cd1dbf76SJohn Johansen 		}
173cd1dbf76SJohn Johansen 	}
1743acfd5f5SJohn Johansen 	if (aad(sa)->signal == SIGUNKNOWN)
1753acfd5f5SJohn Johansen 		audit_log_format(ab, "signal=unknown(%d)",
1763acfd5f5SJohn Johansen 				 aad(sa)->unmappedsig);
1773acfd5f5SJohn Johansen 	else if (aad(sa)->signal < MAXMAPPED_SIGNAME)
178cd1dbf76SJohn Johansen 		audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
179cd1dbf76SJohn Johansen 	else
180cd1dbf76SJohn Johansen 		audit_log_format(ab, " signal=rtmin+%d",
1813acfd5f5SJohn Johansen 				 aad(sa)->signal - SIGRT_BASE);
182cd1dbf76SJohn Johansen 	audit_log_format(ab, " peer=");
183cd1dbf76SJohn Johansen 	aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
184cd1dbf76SJohn Johansen 			FLAGS_NONE, GFP_ATOMIC);
185cd1dbf76SJohn Johansen }
186cd1dbf76SJohn Johansen 
187cd1dbf76SJohn Johansen static int profile_signal_perm(struct aa_profile *profile,
1883dc6b1ceSJohn Johansen 			       struct aa_label *peer, u32 request,
189cd1dbf76SJohn Johansen 			       struct common_audit_data *sa)
190cd1dbf76SJohn Johansen {
191cd1dbf76SJohn Johansen 	struct aa_perms perms;
1923dc6b1ceSJohn Johansen 	unsigned int state;
193cd1dbf76SJohn Johansen 
194cd1dbf76SJohn Johansen 	if (profile_unconfined(profile) ||
195cd1dbf76SJohn Johansen 	    !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
196cd1dbf76SJohn Johansen 		return 0;
197cd1dbf76SJohn Johansen 
1983dc6b1ceSJohn Johansen 	aad(sa)->peer = peer;
1993dc6b1ceSJohn Johansen 	/* TODO: secondary cache check <profile, profile, perm> */
2003dc6b1ceSJohn Johansen 	state = aa_dfa_next(profile->policy.dfa,
2013dc6b1ceSJohn Johansen 			    profile->policy.start[AA_CLASS_SIGNAL],
2023dc6b1ceSJohn Johansen 			    aad(sa)->signal);
2033dc6b1ceSJohn Johansen 	aa_label_match(profile, peer, state, false, request, &perms);
204cd1dbf76SJohn Johansen 	aa_apply_modes_to_perms(profile, &perms);
205cd1dbf76SJohn Johansen 	return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
206cd1dbf76SJohn Johansen }
207cd1dbf76SJohn Johansen 
208cd1dbf76SJohn Johansen int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
209cd1dbf76SJohn Johansen {
2103dc6b1ceSJohn Johansen 	struct aa_profile *profile;
211cd1dbf76SJohn Johansen 	DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
212cd1dbf76SJohn Johansen 
213cd1dbf76SJohn Johansen 	aad(&sa)->signal = map_signal_num(sig);
2143acfd5f5SJohn Johansen 	aad(&sa)->unmappedsig = sig;
2153dc6b1ceSJohn Johansen 	return xcheck_labels(sender, target, profile,
2163dc6b1ceSJohn Johansen 			profile_signal_perm(profile, target, MAY_WRITE, &sa),
2173dc6b1ceSJohn Johansen 			profile_signal_perm(profile, sender, MAY_READ, &sa));
218cd1dbf76SJohn Johansen }
219