1 // SPDX-License-Identifier: GPL-2.0-only
3 * AppArmor security module
5 * This file contains AppArmor functions for unpacking policy loaded
8 * Copyright (C) 1998-2008 Novell/SUSE
9 * Copyright 2009-2022 Canonical Ltd.
11 * Code to provide backwards compatibility with older policy versions,
12 * by converting/mapping older policy formats into the newer internal
16 #include <linux/ctype.h>
17 #include <linux/errno.h>
19 #include "include/lib.h"
20 #include "include/policy_unpack.h"
21 #include "include/policy_compat.h"
23 /* remap old accept table embedded permissions to separate permission table */
24 static u32
dfa_map_xindex(u16 mask
)
26 u16 old_index
= (mask
>> 10) & 0xf;
32 index
|= AA_X_INHERIT
;
34 index
|= AA_X_UNCONFINED
;
37 index
|= AA_X_UNCONFINED
;
38 } else if (old_index
== 2) {
40 } else if (old_index
== 3) {
41 index
|= AA_X_NAME
| AA_X_CHILD
;
42 } else if (old_index
) {
44 index
|= old_index
- 4;
51 * map old dfa inline permissions to new format
53 #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \
54 ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
55 #define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f)
56 #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f)
57 #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f)
58 #define dfa_user_xindex(dfa, state) \
59 (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff))
61 #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \
63 ((ACCEPT_TABLE(dfa)[state]) & 0x80000000))
64 #define dfa_other_xbits(dfa, state) \
65 ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f)
66 #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f)
67 #define dfa_other_quiet(dfa, state) \
68 ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f)
69 #define dfa_other_xindex(dfa, state) \
70 dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff)
73 * map_old_perms - map old file perms layout to the new layout
74 * @old: permission set in old mapping
76 * Returns: new permission mapping
78 static u32
map_old_perms(u32 old
)
83 new |= AA_MAY_GETATTR
| AA_MAY_OPEN
;
85 new |= AA_MAY_SETATTR
| AA_MAY_CREATE
| AA_MAY_DELETE
|
86 AA_MAY_CHMOD
| AA_MAY_CHOWN
| AA_MAY_OPEN
;
89 /* the old mapping lock and link_subset flags where overlaid
90 * and use was determined by part of a pair that they were in
93 new |= AA_MAY_LOCK
| AA_LINK_SUBSET
;
94 if (old
& 0x40) /* AA_EXEC_MMAP */
100 static void compute_fperms_allow(struct aa_perms
*perms
, struct aa_dfa
*dfa
,
103 perms
->allow
|= AA_MAY_GETATTR
;
105 /* change_profile wasn't determined by ownership in old mapping */
106 if (ACCEPT_TABLE(dfa
)[state
] & 0x80000000)
107 perms
->allow
|= AA_MAY_CHANGE_PROFILE
;
108 if (ACCEPT_TABLE(dfa
)[state
] & 0x40000000)
109 perms
->allow
|= AA_MAY_ONEXEC
;
112 static struct aa_perms
compute_fperms_user(struct aa_dfa
*dfa
,
115 struct aa_perms perms
= { };
117 perms
.allow
= map_old_perms(dfa_user_allow(dfa
, state
));
118 perms
.audit
= map_old_perms(dfa_user_audit(dfa
, state
));
119 perms
.quiet
= map_old_perms(dfa_user_quiet(dfa
, state
));
120 perms
.xindex
= dfa_user_xindex(dfa
, state
);
122 compute_fperms_allow(&perms
, dfa
, state
);
127 static struct aa_perms
compute_fperms_other(struct aa_dfa
*dfa
,
130 struct aa_perms perms
= { };
132 perms
.allow
= map_old_perms(dfa_other_allow(dfa
, state
));
133 perms
.audit
= map_old_perms(dfa_other_audit(dfa
, state
));
134 perms
.quiet
= map_old_perms(dfa_other_quiet(dfa
, state
));
135 perms
.xindex
= dfa_other_xindex(dfa
, state
);
137 compute_fperms_allow(&perms
, dfa
, state
);
143 * compute_fperms - convert dfa compressed perms to internal perms and store
144 * them so they can be retrieved later.
145 * @dfa: a dfa using fperms to remap to internal permissions
146 * @size: Returns the permission table size
148 * Returns: remapped perm table
150 static struct aa_perms
*compute_fperms(struct aa_dfa
*dfa
,
154 unsigned int state_count
;
155 struct aa_perms
*table
;
159 state_count
= dfa
->tables
[YYTD_ID_BASE
]->td_lolen
;
160 /* DFAs are restricted from having a state_count of less than 2 */
161 table
= kvcalloc(state_count
* 2, sizeof(struct aa_perms
), GFP_KERNEL
);
164 *size
= state_count
* 2;
166 for (state
= 0; state
< state_count
; state
++) {
167 table
[state
* 2] = compute_fperms_user(dfa
, state
);
168 table
[state
* 2 + 1] = compute_fperms_other(dfa
, state
);
174 static struct aa_perms
*compute_xmatch_perms(struct aa_dfa
*xmatch
,
177 struct aa_perms
*perms
;
183 state_count
= xmatch
->tables
[YYTD_ID_BASE
]->td_lolen
;
184 /* DFAs are restricted from having a state_count of less than 2 */
185 perms
= kvcalloc(state_count
, sizeof(struct aa_perms
), GFP_KERNEL
);
190 /* zero init so skip the trap state (state == 0) */
191 for (state
= 1; state
< state_count
; state
++)
192 perms
[state
].allow
= dfa_user_allow(xmatch
, state
);
197 static u32
map_other(u32 x
)
199 return ((x
& 0x3) << 8) | /* SETATTR/GETATTR */
200 ((x
& 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
201 ((x
& 0x60) << 19); /* SETOPT/GETOPT */
204 static u32
map_xbits(u32 x
)
206 return ((x
& 0x1) << 7) |
210 static struct aa_perms
compute_perms_entry(struct aa_dfa
*dfa
,
214 struct aa_perms perms
= { };
216 perms
.allow
= dfa_user_allow(dfa
, state
);
217 perms
.audit
= dfa_user_audit(dfa
, state
);
218 perms
.quiet
= dfa_user_quiet(dfa
, state
);
221 * This mapping is convulated due to history.
222 * v1-v4: only file perms, which are handled by compute_fperms
223 * v5: added policydb which dropped user conditional to gain new
224 * perm bits, but had to map around the xbits because the
225 * userspace compiler was still munging them.
226 * v9: adds using the xbits in policydb because the compiler now
227 * supports treating policydb permission bits different.
228 * Unfortunately there is no way to force auditing on the
229 * perms represented by the xbits
231 perms
.allow
|= map_other(dfa_other_allow(dfa
, state
));
232 if (VERSION_LE(version
, v8
))
233 perms
.allow
|= AA_MAY_LOCK
;
235 perms
.allow
|= map_xbits(dfa_user_xbits(dfa
, state
));
238 * for v5-v9 perm mapping in the policydb, the other set is used
239 * to extend the general perm set
241 perms
.audit
|= map_other(dfa_other_audit(dfa
, state
));
242 perms
.quiet
|= map_other(dfa_other_quiet(dfa
, state
));
243 if (VERSION_GT(version
, v8
))
244 perms
.quiet
|= map_xbits(dfa_other_xbits(dfa
, state
));
249 static struct aa_perms
*compute_perms(struct aa_dfa
*dfa
, u32 version
,
253 unsigned int state_count
;
254 struct aa_perms
*table
;
258 state_count
= dfa
->tables
[YYTD_ID_BASE
]->td_lolen
;
259 /* DFAs are restricted from having a state_count of less than 2 */
260 table
= kvcalloc(state_count
, sizeof(struct aa_perms
), GFP_KERNEL
);
265 /* zero init so skip the trap state (state == 0) */
266 for (state
= 1; state
< state_count
; state
++)
267 table
[state
] = compute_perms_entry(dfa
, state
, version
);
273 * remap_dfa_accept - remap old dfa accept table to be an index
274 * @dfa: dfa to do the remapping on
275 * @factor: scaling factor for the index conversion.
277 * Used in conjunction with compute_Xperms, it converts old style perms
278 * that are encoded in the dfa accept tables to the new style where
279 * there is a permission table and the accept table is an index into
280 * the permission table.
282 static void remap_dfa_accept(struct aa_dfa
*dfa
, unsigned int factor
)
285 unsigned int state_count
= dfa
->tables
[YYTD_ID_BASE
]->td_lolen
;
289 for (state
= 0; state
< state_count
; state
++)
290 ACCEPT_TABLE(dfa
)[state
] = state
* factor
;
291 kvfree(dfa
->tables
[YYTD_ID_ACCEPT2
]);
292 dfa
->tables
[YYTD_ID_ACCEPT2
] = NULL
;
295 /* TODO: merge different dfa mappings into single map_policy fn */
296 int aa_compat_map_xmatch(struct aa_policydb
*policy
)
298 policy
->perms
= compute_xmatch_perms(policy
->dfa
, &policy
->size
);
302 remap_dfa_accept(policy
->dfa
, 1);
307 int aa_compat_map_policy(struct aa_policydb
*policy
, u32 version
)
309 policy
->perms
= compute_perms(policy
->dfa
, version
, &policy
->size
);
313 remap_dfa_accept(policy
->dfa
, 1);
318 int aa_compat_map_file(struct aa_policydb
*policy
)
320 policy
->perms
= compute_fperms(policy
->dfa
, &policy
->size
);
324 remap_dfa_accept(policy
->dfa
, 2);