1caa9f579SJohn Johansen // SPDX-License-Identifier: GPL-2.0-only
2caa9f579SJohn Johansen /*
3caa9f579SJohn Johansen  * AppArmor security module
4caa9f579SJohn Johansen  *
5caa9f579SJohn Johansen  * This file contains AppArmor functions for unpacking policy loaded
6caa9f579SJohn Johansen  * from userspace.
7caa9f579SJohn Johansen  *
8caa9f579SJohn Johansen  * Copyright (C) 1998-2008 Novell/SUSE
9caa9f579SJohn Johansen  * Copyright 2009-2022 Canonical Ltd.
10caa9f579SJohn Johansen  *
11caa9f579SJohn Johansen  * Code to provide backwards compatibility with older policy versions,
12caa9f579SJohn Johansen  * by converting/mapping older policy formats into the newer internal
13caa9f579SJohn Johansen  * formats.
14caa9f579SJohn Johansen  */
15caa9f579SJohn Johansen 
16caa9f579SJohn Johansen #include <linux/ctype.h>
17caa9f579SJohn Johansen #include <linux/errno.h>
18caa9f579SJohn Johansen 
19caa9f579SJohn Johansen #include "include/lib.h"
20caa9f579SJohn Johansen #include "include/policy_unpack.h"
21caa9f579SJohn Johansen #include "include/policy_compat.h"
22caa9f579SJohn Johansen 
23caa9f579SJohn Johansen /* remap old accept table embedded permissions to separate permission table */
dfa_map_xindex(u16 mask)24caa9f579SJohn Johansen static u32 dfa_map_xindex(u16 mask)
25caa9f579SJohn Johansen {
26caa9f579SJohn Johansen 	u16 old_index = (mask >> 10) & 0xf;
27caa9f579SJohn Johansen 	u32 index = 0;
28caa9f579SJohn Johansen 
29caa9f579SJohn Johansen 	if (mask & 0x100)
30caa9f579SJohn Johansen 		index |= AA_X_UNSAFE;
31caa9f579SJohn Johansen 	if (mask & 0x200)
32caa9f579SJohn Johansen 		index |= AA_X_INHERIT;
33caa9f579SJohn Johansen 	if (mask & 0x80)
34caa9f579SJohn Johansen 		index |= AA_X_UNCONFINED;
35caa9f579SJohn Johansen 
36caa9f579SJohn Johansen 	if (old_index == 1) {
37caa9f579SJohn Johansen 		index |= AA_X_UNCONFINED;
38caa9f579SJohn Johansen 	} else if (old_index == 2) {
39caa9f579SJohn Johansen 		index |= AA_X_NAME;
40caa9f579SJohn Johansen 	} else if (old_index == 3) {
41caa9f579SJohn Johansen 		index |= AA_X_NAME | AA_X_CHILD;
42caa9f579SJohn Johansen 	} else if (old_index) {
43caa9f579SJohn Johansen 		index |= AA_X_TABLE;
44caa9f579SJohn Johansen 		index |= old_index - 4;
45caa9f579SJohn Johansen 	}
46caa9f579SJohn Johansen 
47caa9f579SJohn Johansen 	return index;
48caa9f579SJohn Johansen }
49caa9f579SJohn Johansen 
50caa9f579SJohn Johansen /*
51caa9f579SJohn Johansen  * map old dfa inline permissions to new format
52caa9f579SJohn Johansen  */
53caa9f579SJohn Johansen #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \
54caa9f579SJohn Johansen 				    ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
55caa9f579SJohn Johansen #define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f)
56caa9f579SJohn Johansen #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f)
57caa9f579SJohn Johansen #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f)
58caa9f579SJohn Johansen #define dfa_user_xindex(dfa, state) \
59caa9f579SJohn Johansen 	(dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
60caa9f579SJohn Johansen 
61caa9f579SJohn Johansen #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \
62caa9f579SJohn Johansen 				      0x7f) |				\
63caa9f579SJohn Johansen 				     ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
64caa9f579SJohn Johansen #define dfa_other_xbits(dfa, state) \
65caa9f579SJohn Johansen 	((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f)
66caa9f579SJohn Johansen #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f)
67caa9f579SJohn Johansen #define dfa_other_quiet(dfa, state) \
68caa9f579SJohn Johansen 	((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f)
69caa9f579SJohn Johansen #define dfa_other_xindex(dfa, state) \
70caa9f579SJohn Johansen 	dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
71caa9f579SJohn Johansen 
72caa9f579SJohn Johansen /**
73caa9f579SJohn Johansen  * map_old_perms - map old file perms layout to the new layout
74caa9f579SJohn Johansen  * @old: permission set in old mapping
75caa9f579SJohn Johansen  *
76caa9f579SJohn Johansen  * Returns: new permission mapping
77caa9f579SJohn Johansen  */
map_old_perms(u32 old)78caa9f579SJohn Johansen static u32 map_old_perms(u32 old)
79caa9f579SJohn Johansen {
80caa9f579SJohn Johansen 	u32 new = old & 0xf;
81caa9f579SJohn Johansen 
82caa9f579SJohn Johansen 	if (old & MAY_READ)
83caa9f579SJohn Johansen 		new |= AA_MAY_GETATTR | AA_MAY_OPEN;
84caa9f579SJohn Johansen 	if (old & MAY_WRITE)
85caa9f579SJohn Johansen 		new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
86caa9f579SJohn Johansen 		       AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
87caa9f579SJohn Johansen 	if (old & 0x10)
88caa9f579SJohn Johansen 		new |= AA_MAY_LINK;
89caa9f579SJohn Johansen 	/* the old mapping lock and link_subset flags where overlaid
90caa9f579SJohn Johansen 	 * and use was determined by part of a pair that they were in
91caa9f579SJohn Johansen 	 */
92caa9f579SJohn Johansen 	if (old & 0x20)
93caa9f579SJohn Johansen 		new |= AA_MAY_LOCK | AA_LINK_SUBSET;
94caa9f579SJohn Johansen 	if (old & 0x40)	/* AA_EXEC_MMAP */
95caa9f579SJohn Johansen 		new |= AA_EXEC_MMAP;
96caa9f579SJohn Johansen 
97caa9f579SJohn Johansen 	return new;
98caa9f579SJohn Johansen }
99caa9f579SJohn Johansen 
compute_fperms_allow(struct aa_perms * perms,struct aa_dfa * dfa,aa_state_t state)100caa9f579SJohn Johansen static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa,
101caa9f579SJohn Johansen 				 aa_state_t state)
102caa9f579SJohn Johansen {
103caa9f579SJohn Johansen 	perms->allow |= AA_MAY_GETATTR;
104caa9f579SJohn Johansen 
105caa9f579SJohn Johansen 	/* change_profile wasn't determined by ownership in old mapping */
106caa9f579SJohn Johansen 	if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
107caa9f579SJohn Johansen 		perms->allow |= AA_MAY_CHANGE_PROFILE;
108caa9f579SJohn Johansen 	if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
109caa9f579SJohn Johansen 		perms->allow |= AA_MAY_ONEXEC;
110caa9f579SJohn Johansen }
111caa9f579SJohn Johansen 
compute_fperms_user(struct aa_dfa * dfa,aa_state_t state)112caa9f579SJohn Johansen static struct aa_perms compute_fperms_user(struct aa_dfa *dfa,
113caa9f579SJohn Johansen 					   aa_state_t state)
114caa9f579SJohn Johansen {
115caa9f579SJohn Johansen 	struct aa_perms perms = { };
116caa9f579SJohn Johansen 
117caa9f579SJohn Johansen 	perms.allow = map_old_perms(dfa_user_allow(dfa, state));
118caa9f579SJohn Johansen 	perms.audit = map_old_perms(dfa_user_audit(dfa, state));
119caa9f579SJohn Johansen 	perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
120caa9f579SJohn Johansen 	perms.xindex = dfa_user_xindex(dfa, state);
121caa9f579SJohn Johansen 
122caa9f579SJohn Johansen 	compute_fperms_allow(&perms, dfa, state);
123caa9f579SJohn Johansen 
124caa9f579SJohn Johansen 	return perms;
125caa9f579SJohn Johansen }
126caa9f579SJohn Johansen 
compute_fperms_other(struct aa_dfa * dfa,aa_state_t state)127caa9f579SJohn Johansen static struct aa_perms compute_fperms_other(struct aa_dfa *dfa,
128caa9f579SJohn Johansen 					    aa_state_t state)
129caa9f579SJohn Johansen {
130caa9f579SJohn Johansen 	struct aa_perms perms = { };
131caa9f579SJohn Johansen 
132caa9f579SJohn Johansen 	perms.allow = map_old_perms(dfa_other_allow(dfa, state));
133caa9f579SJohn Johansen 	perms.audit = map_old_perms(dfa_other_audit(dfa, state));
134caa9f579SJohn Johansen 	perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
135caa9f579SJohn Johansen 	perms.xindex = dfa_other_xindex(dfa, state);
136caa9f579SJohn Johansen 
137caa9f579SJohn Johansen 	compute_fperms_allow(&perms, dfa, state);
138caa9f579SJohn Johansen 
139caa9f579SJohn Johansen 	return perms;
140caa9f579SJohn Johansen }
141caa9f579SJohn Johansen 
142caa9f579SJohn Johansen /**
1431ddece8cSJohn Johansen  * compute_fperms - convert dfa compressed perms to internal perms and store
144caa9f579SJohn Johansen  *		    them so they can be retrieved later.
145caa9f579SJohn Johansen  * @dfa: a dfa using fperms to remap to internal permissions
146caa9f579SJohn Johansen  *
147caa9f579SJohn Johansen  * Returns: remapped perm table
148caa9f579SJohn Johansen  */
compute_fperms(struct aa_dfa * dfa,u32 * size)149*6f442d42SJohn Johansen static struct aa_perms *compute_fperms(struct aa_dfa *dfa,
150*6f442d42SJohn Johansen 				       u32 *size)
151caa9f579SJohn Johansen {
152caa9f579SJohn Johansen 	aa_state_t state;
153caa9f579SJohn Johansen 	unsigned int state_count;
154caa9f579SJohn Johansen 	struct aa_perms *table;
155caa9f579SJohn Johansen 
156caa9f579SJohn Johansen 	AA_BUG(!dfa);
157caa9f579SJohn Johansen 
158caa9f579SJohn Johansen 	state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
159caa9f579SJohn Johansen 	/* DFAs are restricted from having a state_count of less than 2 */
160caa9f579SJohn Johansen 	table = kvcalloc(state_count * 2, sizeof(struct aa_perms), GFP_KERNEL);
161caa9f579SJohn Johansen 	if (!table)
162caa9f579SJohn Johansen 		return NULL;
163*6f442d42SJohn Johansen 	*size = state_count * 2;
164caa9f579SJohn Johansen 
165caa9f579SJohn Johansen 	for (state = 0; state < state_count; state++) {
166caa9f579SJohn Johansen 		table[state * 2] = compute_fperms_user(dfa, state);
167caa9f579SJohn Johansen 		table[state * 2 + 1] = compute_fperms_other(dfa, state);
168caa9f579SJohn Johansen 	}
169caa9f579SJohn Johansen 
170caa9f579SJohn Johansen 	return table;
171caa9f579SJohn Johansen }
172caa9f579SJohn Johansen 
compute_xmatch_perms(struct aa_dfa * xmatch,u32 * size)173caa9f579SJohn Johansen static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch,
174*6f442d42SJohn Johansen 				      u32 *size)
175*6f442d42SJohn Johansen {
176caa9f579SJohn Johansen 	struct aa_perms *perms;
177caa9f579SJohn Johansen 	int state;
178caa9f579SJohn Johansen 	int state_count;
179caa9f579SJohn Johansen 
180caa9f579SJohn Johansen 	AA_BUG(!xmatch);
181caa9f579SJohn Johansen 
182caa9f579SJohn Johansen 	state_count = xmatch->tables[YYTD_ID_BASE]->td_lolen;
183caa9f579SJohn Johansen 	/* DFAs are restricted from having a state_count of less than 2 */
184caa9f579SJohn Johansen 	perms = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
185caa9f579SJohn Johansen 	if (!perms)
1866600e9f6SJohn Johansen 		return NULL;
1876600e9f6SJohn Johansen 	*size = state_count;
188*6f442d42SJohn Johansen 
189caa9f579SJohn Johansen 	/* zero init so skip the trap state (state == 0) */
190caa9f579SJohn Johansen 	for (state = 1; state < state_count; state++)
191caa9f579SJohn Johansen 		perms[state].allow = dfa_user_allow(xmatch, state);
192caa9f579SJohn Johansen 
193caa9f579SJohn Johansen 	return perms;
194caa9f579SJohn Johansen }
195caa9f579SJohn Johansen 
map_other(u32 x)196caa9f579SJohn Johansen static u32 map_other(u32 x)
197caa9f579SJohn Johansen {
198caa9f579SJohn Johansen 	return ((x & 0x3) << 8) |	/* SETATTR/GETATTR */
199caa9f579SJohn Johansen 		((x & 0x1c) << 18) |	/* ACCEPT/BIND/LISTEN */
200caa9f579SJohn Johansen 		((x & 0x60) << 19);	/* SETOPT/GETOPT */
201caa9f579SJohn Johansen }
202caa9f579SJohn Johansen 
map_xbits(u32 x)203caa9f579SJohn Johansen static u32 map_xbits(u32 x)
204caa9f579SJohn Johansen {
205caa9f579SJohn Johansen 	return ((x & 0x1) << 7) |
206caa9f579SJohn Johansen 		((x & 0x7e) << 9);
207caa9f579SJohn Johansen }
208caa9f579SJohn Johansen 
compute_perms_entry(struct aa_dfa * dfa,aa_state_t state,u32 version)209caa9f579SJohn Johansen static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
210caa9f579SJohn Johansen 					   aa_state_t state,
211caa9f579SJohn Johansen 					   u32 version)
212caa9f579SJohn Johansen {
213caa9f579SJohn Johansen 	struct aa_perms perms = { };
214caa9f579SJohn Johansen 
215caa9f579SJohn Johansen 	perms.allow = dfa_user_allow(dfa, state);
216caa9f579SJohn Johansen 	perms.audit = dfa_user_audit(dfa, state);
217caa9f579SJohn Johansen 	perms.quiet = dfa_user_quiet(dfa, state);
218caa9f579SJohn Johansen 
219caa9f579SJohn Johansen 	/*
220caa9f579SJohn Johansen 	 * This mapping is convulated due to history.
221caa9f579SJohn Johansen 	 * v1-v4: only file perms, which are handled by compute_fperms
222caa9f579SJohn Johansen 	 * v5: added policydb which dropped user conditional to gain new
223caa9f579SJohn Johansen 	 *     perm bits, but had to map around the xbits because the
224caa9f579SJohn Johansen 	 *     userspace compiler was still munging them.
225caa9f579SJohn Johansen 	 * v9: adds using the xbits in policydb because the compiler now
226caa9f579SJohn Johansen 	 *     supports treating policydb permission bits different.
227caa9f579SJohn Johansen 	 *     Unfortunately there is no way to force auditing on the
228caa9f579SJohn Johansen 	 *     perms represented by the xbits
229caa9f579SJohn Johansen 	 */
230caa9f579SJohn Johansen 	perms.allow |= map_other(dfa_other_allow(dfa, state));
231caa9f579SJohn Johansen 	if (VERSION_LE(version, v8))
232caa9f579SJohn Johansen 		perms.allow |= AA_MAY_LOCK;
233caa9f579SJohn Johansen 	else
234caa9f579SJohn Johansen 		perms.allow |= map_xbits(dfa_user_xbits(dfa, state));
235caa9f579SJohn Johansen 
236caa9f579SJohn Johansen 	/*
237caa9f579SJohn Johansen 	 * for v5-v9 perm mapping in the policydb, the other set is used
238caa9f579SJohn Johansen 	 * to extend the general perm set
239caa9f579SJohn Johansen 	 */
240caa9f579SJohn Johansen 	perms.audit |= map_other(dfa_other_audit(dfa, state));
241caa9f579SJohn Johansen 	perms.quiet |= map_other(dfa_other_quiet(dfa, state));
242caa9f579SJohn Johansen 	if (VERSION_GT(version, v8))
243caa9f579SJohn Johansen 		perms.quiet |= map_xbits(dfa_other_xbits(dfa, state));
244caa9f579SJohn Johansen 
245caa9f579SJohn Johansen 	return perms;
246caa9f579SJohn Johansen }
247caa9f579SJohn Johansen 
compute_perms(struct aa_dfa * dfa,u32 version,u32 * size)248caa9f579SJohn Johansen static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version,
249*6f442d42SJohn Johansen 				      u32 *size)
250*6f442d42SJohn Johansen {
251caa9f579SJohn Johansen 	unsigned int state;
252caa9f579SJohn Johansen 	unsigned int state_count;
253caa9f579SJohn Johansen 	struct aa_perms *table;
254caa9f579SJohn Johansen 
255caa9f579SJohn Johansen 	AA_BUG(!dfa);
256caa9f579SJohn Johansen 
257caa9f579SJohn Johansen 	state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
258caa9f579SJohn Johansen 	/* DFAs are restricted from having a state_count of less than 2 */
259caa9f579SJohn Johansen 	table = kvcalloc(state_count, sizeof(struct aa_perms), GFP_KERNEL);
260caa9f579SJohn Johansen 	if (!table)
261caa9f579SJohn Johansen 		return NULL;
262caa9f579SJohn Johansen 	*size = state_count;
263*6f442d42SJohn Johansen 
264caa9f579SJohn Johansen 	/* zero init so skip the trap state (state == 0) */
265caa9f579SJohn Johansen 	for (state = 1; state < state_count; state++)
266caa9f579SJohn Johansen 		table[state] = compute_perms_entry(dfa, state, version);
267caa9f579SJohn Johansen 
268caa9f579SJohn Johansen 	return table;
269caa9f579SJohn Johansen }
270caa9f579SJohn Johansen 
271caa9f579SJohn Johansen /**
272caa9f579SJohn Johansen  * remap_dfa_accept - remap old dfa accept table to be an index
273caa9f579SJohn Johansen  * @dfa: dfa to do the remapping on
274caa9f579SJohn Johansen  * @factor: scaling factor for the index conversion.
275caa9f579SJohn Johansen  *
276caa9f579SJohn Johansen  * Used in conjunction with compute_Xperms, it converts old style perms
277caa9f579SJohn Johansen  * that are encoded in the dfa accept tables to the new style where
278caa9f579SJohn Johansen  * there is a permission table and the accept table is an index into
279caa9f579SJohn Johansen  * the permission table.
280caa9f579SJohn Johansen  */
remap_dfa_accept(struct aa_dfa * dfa,unsigned int factor)281caa9f579SJohn Johansen static void remap_dfa_accept(struct aa_dfa *dfa, unsigned int factor)
282caa9f579SJohn Johansen {
283caa9f579SJohn Johansen 	unsigned int state;
284caa9f579SJohn Johansen 	unsigned int state_count = dfa->tables[YYTD_ID_BASE]->td_lolen;
285caa9f579SJohn Johansen 
286caa9f579SJohn Johansen 	AA_BUG(!dfa);
287caa9f579SJohn Johansen 
288caa9f579SJohn Johansen 	for (state = 0; state < state_count; state++)
289caa9f579SJohn Johansen 		ACCEPT_TABLE(dfa)[state] = state * factor;
290caa9f579SJohn Johansen 	kvfree(dfa->tables[YYTD_ID_ACCEPT2]);
291caa9f579SJohn Johansen 	dfa->tables[YYTD_ID_ACCEPT2] = NULL;
292caa9f579SJohn Johansen }
293caa9f579SJohn Johansen 
294caa9f579SJohn Johansen /* TODO: merge different dfa mappings into single map_policy fn */
aa_compat_map_xmatch(struct aa_policydb * policy)295caa9f579SJohn Johansen int aa_compat_map_xmatch(struct aa_policydb *policy)
296caa9f579SJohn Johansen {
297caa9f579SJohn Johansen 	policy->perms = compute_xmatch_perms(policy->dfa, &policy->size);
298*6f442d42SJohn Johansen 	if (!policy->perms)
299caa9f579SJohn Johansen 		return -ENOMEM;
300caa9f579SJohn Johansen 
301caa9f579SJohn Johansen 	remap_dfa_accept(policy->dfa, 1);
302caa9f579SJohn Johansen 
303caa9f579SJohn Johansen 	return 0;
304caa9f579SJohn Johansen }
305caa9f579SJohn Johansen 
aa_compat_map_policy(struct aa_policydb * policy,u32 version)306caa9f579SJohn Johansen int aa_compat_map_policy(struct aa_policydb *policy, u32 version)
307caa9f579SJohn Johansen {
308caa9f579SJohn Johansen 	policy->perms = compute_perms(policy->dfa, version, &policy->size);
309*6f442d42SJohn Johansen 	if (!policy->perms)
310caa9f579SJohn Johansen 		return -ENOMEM;
311caa9f579SJohn Johansen 
312caa9f579SJohn Johansen 	remap_dfa_accept(policy->dfa, 1);
313caa9f579SJohn Johansen 
314caa9f579SJohn Johansen 	return 0;
315caa9f579SJohn Johansen }
316caa9f579SJohn Johansen 
aa_compat_map_file(struct aa_policydb * policy)317caa9f579SJohn Johansen int aa_compat_map_file(struct aa_policydb *policy)
318caa9f579SJohn Johansen {
319caa9f579SJohn Johansen 	policy->perms = compute_fperms(policy->dfa, &policy->size);
320*6f442d42SJohn Johansen 	if (!policy->perms)
321caa9f579SJohn Johansen 		return -ENOMEM;
322caa9f579SJohn Johansen 
323caa9f579SJohn Johansen 	remap_dfa_accept(policy->dfa, 2);
324caa9f579SJohn Johansen 
325caa9f579SJohn Johansen 	return 0;
326caa9f579SJohn Johansen }
327caa9f579SJohn Johansen