1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * AppArmor security module
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This file contains AppArmor mediation of files
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Copyright (C) 1998-2008 Novell/SUSE
8*4882a593Smuzhiyun * Copyright 2009-2010 Canonical Ltd.
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/tty.h>
12*4882a593Smuzhiyun #include <linux/fdtable.h>
13*4882a593Smuzhiyun #include <linux/file.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include "include/apparmor.h"
16*4882a593Smuzhiyun #include "include/audit.h"
17*4882a593Smuzhiyun #include "include/cred.h"
18*4882a593Smuzhiyun #include "include/file.h"
19*4882a593Smuzhiyun #include "include/match.h"
20*4882a593Smuzhiyun #include "include/net.h"
21*4882a593Smuzhiyun #include "include/path.h"
22*4882a593Smuzhiyun #include "include/policy.h"
23*4882a593Smuzhiyun #include "include/label.h"
24*4882a593Smuzhiyun
map_mask_to_chr_mask(u32 mask)25*4882a593Smuzhiyun static u32 map_mask_to_chr_mask(u32 mask)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun u32 m = mask & PERMS_CHRS_MASK;
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun if (mask & AA_MAY_GETATTR)
30*4882a593Smuzhiyun m |= MAY_READ;
31*4882a593Smuzhiyun if (mask & (AA_MAY_SETATTR | AA_MAY_CHMOD | AA_MAY_CHOWN))
32*4882a593Smuzhiyun m |= MAY_WRITE;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun return m;
35*4882a593Smuzhiyun }
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /**
38*4882a593Smuzhiyun * file_audit_cb - call back for file specific audit fields
39*4882a593Smuzhiyun * @ab: audit_buffer (NOT NULL)
40*4882a593Smuzhiyun * @va: audit struct to audit values of (NOT NULL)
41*4882a593Smuzhiyun */
file_audit_cb(struct audit_buffer * ab,void * va)42*4882a593Smuzhiyun static void file_audit_cb(struct audit_buffer *ab, void *va)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct common_audit_data *sa = va;
45*4882a593Smuzhiyun kuid_t fsuid = current_fsuid();
46*4882a593Smuzhiyun char str[10];
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
49*4882a593Smuzhiyun aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs,
50*4882a593Smuzhiyun map_mask_to_chr_mask(aad(sa)->request));
51*4882a593Smuzhiyun audit_log_format(ab, " requested_mask=\"%s\"", str);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun if (aad(sa)->denied & AA_AUDIT_FILE_MASK) {
54*4882a593Smuzhiyun aa_perm_mask_to_str(str, sizeof(str), aa_file_perm_chrs,
55*4882a593Smuzhiyun map_mask_to_chr_mask(aad(sa)->denied));
56*4882a593Smuzhiyun audit_log_format(ab, " denied_mask=\"%s\"", str);
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun if (aad(sa)->request & AA_AUDIT_FILE_MASK) {
59*4882a593Smuzhiyun audit_log_format(ab, " fsuid=%d",
60*4882a593Smuzhiyun from_kuid(&init_user_ns, fsuid));
61*4882a593Smuzhiyun audit_log_format(ab, " ouid=%d",
62*4882a593Smuzhiyun from_kuid(&init_user_ns, aad(sa)->fs.ouid));
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun if (aad(sa)->peer) {
66*4882a593Smuzhiyun audit_log_format(ab, " target=");
67*4882a593Smuzhiyun aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
68*4882a593Smuzhiyun FLAG_VIEW_SUBNS, GFP_KERNEL);
69*4882a593Smuzhiyun } else if (aad(sa)->fs.target) {
70*4882a593Smuzhiyun audit_log_format(ab, " target=");
71*4882a593Smuzhiyun audit_log_untrustedstring(ab, aad(sa)->fs.target);
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /**
76*4882a593Smuzhiyun * aa_audit_file - handle the auditing of file operations
77*4882a593Smuzhiyun * @profile: the profile being enforced (NOT NULL)
78*4882a593Smuzhiyun * @perms: the permissions computed for the request (NOT NULL)
79*4882a593Smuzhiyun * @op: operation being mediated
80*4882a593Smuzhiyun * @request: permissions requested
81*4882a593Smuzhiyun * @name: name of object being mediated (MAYBE NULL)
82*4882a593Smuzhiyun * @target: name of target (MAYBE NULL)
83*4882a593Smuzhiyun * @tlabel: target label (MAY BE NULL)
84*4882a593Smuzhiyun * @ouid: object uid
85*4882a593Smuzhiyun * @info: extra information message (MAYBE NULL)
86*4882a593Smuzhiyun * @error: 0 if operation allowed else failure error code
87*4882a593Smuzhiyun *
88*4882a593Smuzhiyun * Returns: %0 or error on failure
89*4882a593Smuzhiyun */
aa_audit_file(struct aa_profile * profile,struct aa_perms * perms,const char * op,u32 request,const char * name,const char * target,struct aa_label * tlabel,kuid_t ouid,const char * info,int error)90*4882a593Smuzhiyun int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms,
91*4882a593Smuzhiyun const char *op, u32 request, const char *name,
92*4882a593Smuzhiyun const char *target, struct aa_label *tlabel,
93*4882a593Smuzhiyun kuid_t ouid, const char *info, int error)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun int type = AUDIT_APPARMOR_AUTO;
96*4882a593Smuzhiyun DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_TASK, op);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun sa.u.tsk = NULL;
99*4882a593Smuzhiyun aad(&sa)->request = request;
100*4882a593Smuzhiyun aad(&sa)->name = name;
101*4882a593Smuzhiyun aad(&sa)->fs.target = target;
102*4882a593Smuzhiyun aad(&sa)->peer = tlabel;
103*4882a593Smuzhiyun aad(&sa)->fs.ouid = ouid;
104*4882a593Smuzhiyun aad(&sa)->info = info;
105*4882a593Smuzhiyun aad(&sa)->error = error;
106*4882a593Smuzhiyun sa.u.tsk = NULL;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (likely(!aad(&sa)->error)) {
109*4882a593Smuzhiyun u32 mask = perms->audit;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
112*4882a593Smuzhiyun mask = 0xffff;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* mask off perms that are not being force audited */
115*4882a593Smuzhiyun aad(&sa)->request &= mask;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (likely(!aad(&sa)->request))
118*4882a593Smuzhiyun return 0;
119*4882a593Smuzhiyun type = AUDIT_APPARMOR_AUDIT;
120*4882a593Smuzhiyun } else {
121*4882a593Smuzhiyun /* only report permissions that were denied */
122*4882a593Smuzhiyun aad(&sa)->request = aad(&sa)->request & ~perms->allow;
123*4882a593Smuzhiyun AA_BUG(!aad(&sa)->request);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun if (aad(&sa)->request & perms->kill)
126*4882a593Smuzhiyun type = AUDIT_APPARMOR_KILL;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /* quiet known rejects, assumes quiet and kill do not overlap */
129*4882a593Smuzhiyun if ((aad(&sa)->request & perms->quiet) &&
130*4882a593Smuzhiyun AUDIT_MODE(profile) != AUDIT_NOQUIET &&
131*4882a593Smuzhiyun AUDIT_MODE(profile) != AUDIT_ALL)
132*4882a593Smuzhiyun aad(&sa)->request &= ~perms->quiet;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun if (!aad(&sa)->request)
135*4882a593Smuzhiyun return aad(&sa)->error;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun aad(&sa)->denied = aad(&sa)->request & ~perms->allow;
139*4882a593Smuzhiyun return aa_audit(type, profile, &sa, file_audit_cb);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /**
143*4882a593Smuzhiyun * is_deleted - test if a file has been completely unlinked
144*4882a593Smuzhiyun * @dentry: dentry of file to test for deletion (NOT NULL)
145*4882a593Smuzhiyun *
146*4882a593Smuzhiyun * Returns: true if deleted else false
147*4882a593Smuzhiyun */
is_deleted(struct dentry * dentry)148*4882a593Smuzhiyun static inline bool is_deleted(struct dentry *dentry)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
151*4882a593Smuzhiyun return true;
152*4882a593Smuzhiyun return false;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
path_name(const char * op,struct aa_label * label,const struct path * path,int flags,char * buffer,const char ** name,struct path_cond * cond,u32 request)155*4882a593Smuzhiyun static int path_name(const char *op, struct aa_label *label,
156*4882a593Smuzhiyun const struct path *path, int flags, char *buffer,
157*4882a593Smuzhiyun const char **name, struct path_cond *cond, u32 request)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun struct aa_profile *profile;
160*4882a593Smuzhiyun const char *info = NULL;
161*4882a593Smuzhiyun int error;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun error = aa_path_name(path, flags, buffer, name, &info,
164*4882a593Smuzhiyun labels_profile(label)->disconnected);
165*4882a593Smuzhiyun if (error) {
166*4882a593Smuzhiyun fn_for_each_confined(label, profile,
167*4882a593Smuzhiyun aa_audit_file(profile, &nullperms, op, request, *name,
168*4882a593Smuzhiyun NULL, NULL, cond->uid, info, error));
169*4882a593Smuzhiyun return error;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun return 0;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun /**
176*4882a593Smuzhiyun * map_old_perms - map old file perms layout to the new layout
177*4882a593Smuzhiyun * @old: permission set in old mapping
178*4882a593Smuzhiyun *
179*4882a593Smuzhiyun * Returns: new permission mapping
180*4882a593Smuzhiyun */
map_old_perms(u32 old)181*4882a593Smuzhiyun static u32 map_old_perms(u32 old)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun u32 new = old & 0xf;
184*4882a593Smuzhiyun if (old & MAY_READ)
185*4882a593Smuzhiyun new |= AA_MAY_GETATTR | AA_MAY_OPEN;
186*4882a593Smuzhiyun if (old & MAY_WRITE)
187*4882a593Smuzhiyun new |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
188*4882a593Smuzhiyun AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
189*4882a593Smuzhiyun if (old & 0x10)
190*4882a593Smuzhiyun new |= AA_MAY_LINK;
191*4882a593Smuzhiyun /* the old mapping lock and link_subset flags where overlaid
192*4882a593Smuzhiyun * and use was determined by part of a pair that they were in
193*4882a593Smuzhiyun */
194*4882a593Smuzhiyun if (old & 0x20)
195*4882a593Smuzhiyun new |= AA_MAY_LOCK | AA_LINK_SUBSET;
196*4882a593Smuzhiyun if (old & 0x40) /* AA_EXEC_MMAP */
197*4882a593Smuzhiyun new |= AA_EXEC_MMAP;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun return new;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun /**
203*4882a593Smuzhiyun * aa_compute_fperms - convert dfa compressed perms to internal perms
204*4882a593Smuzhiyun * @dfa: dfa to compute perms for (NOT NULL)
205*4882a593Smuzhiyun * @state: state in dfa
206*4882a593Smuzhiyun * @cond: conditions to consider (NOT NULL)
207*4882a593Smuzhiyun *
208*4882a593Smuzhiyun * TODO: convert from dfa + state to permission entry, do computation conversion
209*4882a593Smuzhiyun * at load time.
210*4882a593Smuzhiyun *
211*4882a593Smuzhiyun * Returns: computed permission set
212*4882a593Smuzhiyun */
aa_compute_fperms(struct aa_dfa * dfa,unsigned int state,struct path_cond * cond)213*4882a593Smuzhiyun struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state,
214*4882a593Smuzhiyun struct path_cond *cond)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun /* FIXME: change over to new dfa format
217*4882a593Smuzhiyun * currently file perms are encoded in the dfa, new format
218*4882a593Smuzhiyun * splits the permissions from the dfa. This mapping can be
219*4882a593Smuzhiyun * done at profile load
220*4882a593Smuzhiyun */
221*4882a593Smuzhiyun struct aa_perms perms = { };
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (uid_eq(current_fsuid(), cond->uid)) {
224*4882a593Smuzhiyun perms.allow = map_old_perms(dfa_user_allow(dfa, state));
225*4882a593Smuzhiyun perms.audit = map_old_perms(dfa_user_audit(dfa, state));
226*4882a593Smuzhiyun perms.quiet = map_old_perms(dfa_user_quiet(dfa, state));
227*4882a593Smuzhiyun perms.xindex = dfa_user_xindex(dfa, state);
228*4882a593Smuzhiyun } else {
229*4882a593Smuzhiyun perms.allow = map_old_perms(dfa_other_allow(dfa, state));
230*4882a593Smuzhiyun perms.audit = map_old_perms(dfa_other_audit(dfa, state));
231*4882a593Smuzhiyun perms.quiet = map_old_perms(dfa_other_quiet(dfa, state));
232*4882a593Smuzhiyun perms.xindex = dfa_other_xindex(dfa, state);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun perms.allow |= AA_MAY_GETATTR;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun /* change_profile wasn't determined by ownership in old mapping */
237*4882a593Smuzhiyun if (ACCEPT_TABLE(dfa)[state] & 0x80000000)
238*4882a593Smuzhiyun perms.allow |= AA_MAY_CHANGE_PROFILE;
239*4882a593Smuzhiyun if (ACCEPT_TABLE(dfa)[state] & 0x40000000)
240*4882a593Smuzhiyun perms.allow |= AA_MAY_ONEXEC;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun return perms;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun /**
246*4882a593Smuzhiyun * aa_str_perms - find permission that match @name
247*4882a593Smuzhiyun * @dfa: to match against (MAYBE NULL)
248*4882a593Smuzhiyun * @state: state to start matching in
249*4882a593Smuzhiyun * @name: string to match against dfa (NOT NULL)
250*4882a593Smuzhiyun * @cond: conditions to consider for permission set computation (NOT NULL)
251*4882a593Smuzhiyun * @perms: Returns - the permissions found when matching @name
252*4882a593Smuzhiyun *
253*4882a593Smuzhiyun * Returns: the final state in @dfa when beginning @start and walking @name
254*4882a593Smuzhiyun */
aa_str_perms(struct aa_dfa * dfa,unsigned int start,const char * name,struct path_cond * cond,struct aa_perms * perms)255*4882a593Smuzhiyun unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start,
256*4882a593Smuzhiyun const char *name, struct path_cond *cond,
257*4882a593Smuzhiyun struct aa_perms *perms)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun unsigned int state;
260*4882a593Smuzhiyun state = aa_dfa_match(dfa, start, name);
261*4882a593Smuzhiyun *perms = aa_compute_fperms(dfa, state, cond);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return state;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
__aa_path_perm(const char * op,struct aa_profile * profile,const char * name,u32 request,struct path_cond * cond,int flags,struct aa_perms * perms)266*4882a593Smuzhiyun int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name,
267*4882a593Smuzhiyun u32 request, struct path_cond *cond, int flags,
268*4882a593Smuzhiyun struct aa_perms *perms)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun int e = 0;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun if (profile_unconfined(profile))
273*4882a593Smuzhiyun return 0;
274*4882a593Smuzhiyun aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms);
275*4882a593Smuzhiyun if (request & ~perms->allow)
276*4882a593Smuzhiyun e = -EACCES;
277*4882a593Smuzhiyun return aa_audit_file(profile, perms, op, request, name, NULL, NULL,
278*4882a593Smuzhiyun cond->uid, NULL, e);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun
profile_path_perm(const char * op,struct aa_profile * profile,const struct path * path,char * buffer,u32 request,struct path_cond * cond,int flags,struct aa_perms * perms)282*4882a593Smuzhiyun static int profile_path_perm(const char *op, struct aa_profile *profile,
283*4882a593Smuzhiyun const struct path *path, char *buffer, u32 request,
284*4882a593Smuzhiyun struct path_cond *cond, int flags,
285*4882a593Smuzhiyun struct aa_perms *perms)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun const char *name;
288*4882a593Smuzhiyun int error;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun if (profile_unconfined(profile))
291*4882a593Smuzhiyun return 0;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun error = path_name(op, &profile->label, path,
294*4882a593Smuzhiyun flags | profile->path_flags, buffer, &name, cond,
295*4882a593Smuzhiyun request);
296*4882a593Smuzhiyun if (error)
297*4882a593Smuzhiyun return error;
298*4882a593Smuzhiyun return __aa_path_perm(op, profile, name, request, cond, flags,
299*4882a593Smuzhiyun perms);
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun /**
303*4882a593Smuzhiyun * aa_path_perm - do permissions check & audit for @path
304*4882a593Smuzhiyun * @op: operation being checked
305*4882a593Smuzhiyun * @label: profile being enforced (NOT NULL)
306*4882a593Smuzhiyun * @path: path to check permissions of (NOT NULL)
307*4882a593Smuzhiyun * @flags: any additional path flags beyond what the profile specifies
308*4882a593Smuzhiyun * @request: requested permissions
309*4882a593Smuzhiyun * @cond: conditional info for this request (NOT NULL)
310*4882a593Smuzhiyun *
311*4882a593Smuzhiyun * Returns: %0 else error if access denied or other error
312*4882a593Smuzhiyun */
aa_path_perm(const char * op,struct aa_label * label,const struct path * path,int flags,u32 request,struct path_cond * cond)313*4882a593Smuzhiyun int aa_path_perm(const char *op, struct aa_label *label,
314*4882a593Smuzhiyun const struct path *path, int flags, u32 request,
315*4882a593Smuzhiyun struct path_cond *cond)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun struct aa_perms perms = {};
318*4882a593Smuzhiyun struct aa_profile *profile;
319*4882a593Smuzhiyun char *buffer = NULL;
320*4882a593Smuzhiyun int error;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun flags |= PATH_DELEGATE_DELETED | (S_ISDIR(cond->mode) ? PATH_IS_DIR :
323*4882a593Smuzhiyun 0);
324*4882a593Smuzhiyun buffer = aa_get_buffer(false);
325*4882a593Smuzhiyun if (!buffer)
326*4882a593Smuzhiyun return -ENOMEM;
327*4882a593Smuzhiyun error = fn_for_each_confined(label, profile,
328*4882a593Smuzhiyun profile_path_perm(op, profile, path, buffer, request,
329*4882a593Smuzhiyun cond, flags, &perms));
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun aa_put_buffer(buffer);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun return error;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun /**
337*4882a593Smuzhiyun * xindex_is_subset - helper for aa_path_link
338*4882a593Smuzhiyun * @link: link permission set
339*4882a593Smuzhiyun * @target: target permission set
340*4882a593Smuzhiyun *
341*4882a593Smuzhiyun * test target x permissions are equal OR a subset of link x permissions
342*4882a593Smuzhiyun * this is done as part of the subset test, where a hardlink must have
343*4882a593Smuzhiyun * a subset of permissions that the target has.
344*4882a593Smuzhiyun *
345*4882a593Smuzhiyun * Returns: true if subset else false
346*4882a593Smuzhiyun */
xindex_is_subset(u32 link,u32 target)347*4882a593Smuzhiyun static inline bool xindex_is_subset(u32 link, u32 target)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) ||
350*4882a593Smuzhiyun ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE)))
351*4882a593Smuzhiyun return false;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun return true;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
profile_path_link(struct aa_profile * profile,const struct path * link,char * buffer,const struct path * target,char * buffer2,struct path_cond * cond)356*4882a593Smuzhiyun static int profile_path_link(struct aa_profile *profile,
357*4882a593Smuzhiyun const struct path *link, char *buffer,
358*4882a593Smuzhiyun const struct path *target, char *buffer2,
359*4882a593Smuzhiyun struct path_cond *cond)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun const char *lname, *tname = NULL;
362*4882a593Smuzhiyun struct aa_perms lperms = {}, perms;
363*4882a593Smuzhiyun const char *info = NULL;
364*4882a593Smuzhiyun u32 request = AA_MAY_LINK;
365*4882a593Smuzhiyun unsigned int state;
366*4882a593Smuzhiyun int error;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun error = path_name(OP_LINK, &profile->label, link, profile->path_flags,
369*4882a593Smuzhiyun buffer, &lname, cond, AA_MAY_LINK);
370*4882a593Smuzhiyun if (error)
371*4882a593Smuzhiyun goto audit;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun /* buffer2 freed below, tname is pointer in buffer2 */
374*4882a593Smuzhiyun error = path_name(OP_LINK, &profile->label, target, profile->path_flags,
375*4882a593Smuzhiyun buffer2, &tname, cond, AA_MAY_LINK);
376*4882a593Smuzhiyun if (error)
377*4882a593Smuzhiyun goto audit;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun error = -EACCES;
380*4882a593Smuzhiyun /* aa_str_perms - handles the case of the dfa being NULL */
381*4882a593Smuzhiyun state = aa_str_perms(profile->file.dfa, profile->file.start, lname,
382*4882a593Smuzhiyun cond, &lperms);
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (!(lperms.allow & AA_MAY_LINK))
385*4882a593Smuzhiyun goto audit;
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun /* test to see if target can be paired with link */
388*4882a593Smuzhiyun state = aa_dfa_null_transition(profile->file.dfa, state);
389*4882a593Smuzhiyun aa_str_perms(profile->file.dfa, state, tname, cond, &perms);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun /* force audit/quiet masks for link are stored in the second entry
392*4882a593Smuzhiyun * in the link pair.
393*4882a593Smuzhiyun */
394*4882a593Smuzhiyun lperms.audit = perms.audit;
395*4882a593Smuzhiyun lperms.quiet = perms.quiet;
396*4882a593Smuzhiyun lperms.kill = perms.kill;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun if (!(perms.allow & AA_MAY_LINK)) {
399*4882a593Smuzhiyun info = "target restricted";
400*4882a593Smuzhiyun lperms = perms;
401*4882a593Smuzhiyun goto audit;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun /* done if link subset test is not required */
405*4882a593Smuzhiyun if (!(perms.allow & AA_LINK_SUBSET))
406*4882a593Smuzhiyun goto done_tests;
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun /* Do link perm subset test requiring allowed permission on link are
409*4882a593Smuzhiyun * a subset of the allowed permissions on target.
410*4882a593Smuzhiyun */
411*4882a593Smuzhiyun aa_str_perms(profile->file.dfa, profile->file.start, tname, cond,
412*4882a593Smuzhiyun &perms);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun /* AA_MAY_LINK is not considered in the subset test */
415*4882a593Smuzhiyun request = lperms.allow & ~AA_MAY_LINK;
416*4882a593Smuzhiyun lperms.allow &= perms.allow | AA_MAY_LINK;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow);
419*4882a593Smuzhiyun if (request & ~lperms.allow) {
420*4882a593Smuzhiyun goto audit;
421*4882a593Smuzhiyun } else if ((lperms.allow & MAY_EXEC) &&
422*4882a593Smuzhiyun !xindex_is_subset(lperms.xindex, perms.xindex)) {
423*4882a593Smuzhiyun lperms.allow &= ~MAY_EXEC;
424*4882a593Smuzhiyun request |= MAY_EXEC;
425*4882a593Smuzhiyun info = "link not subset of target";
426*4882a593Smuzhiyun goto audit;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun done_tests:
430*4882a593Smuzhiyun error = 0;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun audit:
433*4882a593Smuzhiyun return aa_audit_file(profile, &lperms, OP_LINK, request, lname, tname,
434*4882a593Smuzhiyun NULL, cond->uid, info, error);
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun /**
438*4882a593Smuzhiyun * aa_path_link - Handle hard link permission check
439*4882a593Smuzhiyun * @label: the label being enforced (NOT NULL)
440*4882a593Smuzhiyun * @old_dentry: the target dentry (NOT NULL)
441*4882a593Smuzhiyun * @new_dir: directory the new link will be created in (NOT NULL)
442*4882a593Smuzhiyun * @new_dentry: the link being created (NOT NULL)
443*4882a593Smuzhiyun *
444*4882a593Smuzhiyun * Handle the permission test for a link & target pair. Permission
445*4882a593Smuzhiyun * is encoded as a pair where the link permission is determined
446*4882a593Smuzhiyun * first, and if allowed, the target is tested. The target test
447*4882a593Smuzhiyun * is done from the point of the link match (not start of DFA)
448*4882a593Smuzhiyun * making the target permission dependent on the link permission match.
449*4882a593Smuzhiyun *
450*4882a593Smuzhiyun * The subset test if required forces that permissions granted
451*4882a593Smuzhiyun * on link are a subset of the permission granted to target.
452*4882a593Smuzhiyun *
453*4882a593Smuzhiyun * Returns: %0 if allowed else error
454*4882a593Smuzhiyun */
aa_path_link(struct aa_label * label,struct dentry * old_dentry,const struct path * new_dir,struct dentry * new_dentry)455*4882a593Smuzhiyun int aa_path_link(struct aa_label *label, struct dentry *old_dentry,
456*4882a593Smuzhiyun const struct path *new_dir, struct dentry *new_dentry)
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun struct path link = { .mnt = new_dir->mnt, .dentry = new_dentry };
459*4882a593Smuzhiyun struct path target = { .mnt = new_dir->mnt, .dentry = old_dentry };
460*4882a593Smuzhiyun struct path_cond cond = {
461*4882a593Smuzhiyun d_backing_inode(old_dentry)->i_uid,
462*4882a593Smuzhiyun d_backing_inode(old_dentry)->i_mode
463*4882a593Smuzhiyun };
464*4882a593Smuzhiyun char *buffer = NULL, *buffer2 = NULL;
465*4882a593Smuzhiyun struct aa_profile *profile;
466*4882a593Smuzhiyun int error;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun /* buffer freed below, lname is pointer in buffer */
469*4882a593Smuzhiyun buffer = aa_get_buffer(false);
470*4882a593Smuzhiyun buffer2 = aa_get_buffer(false);
471*4882a593Smuzhiyun error = -ENOMEM;
472*4882a593Smuzhiyun if (!buffer || !buffer2)
473*4882a593Smuzhiyun goto out;
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun error = fn_for_each_confined(label, profile,
476*4882a593Smuzhiyun profile_path_link(profile, &link, buffer, &target,
477*4882a593Smuzhiyun buffer2, &cond));
478*4882a593Smuzhiyun out:
479*4882a593Smuzhiyun aa_put_buffer(buffer);
480*4882a593Smuzhiyun aa_put_buffer(buffer2);
481*4882a593Smuzhiyun return error;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
update_file_ctx(struct aa_file_ctx * fctx,struct aa_label * label,u32 request)484*4882a593Smuzhiyun static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
485*4882a593Smuzhiyun u32 request)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun struct aa_label *l, *old;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun /* update caching of label on file_ctx */
490*4882a593Smuzhiyun spin_lock(&fctx->lock);
491*4882a593Smuzhiyun old = rcu_dereference_protected(fctx->label,
492*4882a593Smuzhiyun lockdep_is_held(&fctx->lock));
493*4882a593Smuzhiyun l = aa_label_merge(old, label, GFP_ATOMIC);
494*4882a593Smuzhiyun if (l) {
495*4882a593Smuzhiyun if (l != old) {
496*4882a593Smuzhiyun rcu_assign_pointer(fctx->label, l);
497*4882a593Smuzhiyun aa_put_label(old);
498*4882a593Smuzhiyun } else
499*4882a593Smuzhiyun aa_put_label(l);
500*4882a593Smuzhiyun fctx->allow |= request;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun spin_unlock(&fctx->lock);
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun
__file_path_perm(const char * op,struct aa_label * label,struct aa_label * flabel,struct file * file,u32 request,u32 denied,bool in_atomic)505*4882a593Smuzhiyun static int __file_path_perm(const char *op, struct aa_label *label,
506*4882a593Smuzhiyun struct aa_label *flabel, struct file *file,
507*4882a593Smuzhiyun u32 request, u32 denied, bool in_atomic)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun struct aa_profile *profile;
510*4882a593Smuzhiyun struct aa_perms perms = {};
511*4882a593Smuzhiyun struct path_cond cond = {
512*4882a593Smuzhiyun .uid = file_inode(file)->i_uid,
513*4882a593Smuzhiyun .mode = file_inode(file)->i_mode
514*4882a593Smuzhiyun };
515*4882a593Smuzhiyun char *buffer;
516*4882a593Smuzhiyun int flags, error;
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun /* revalidation due to label out of date. No revocation at this time */
519*4882a593Smuzhiyun if (!denied && aa_label_is_subset(flabel, label))
520*4882a593Smuzhiyun /* TODO: check for revocation on stale profiles */
521*4882a593Smuzhiyun return 0;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun flags = PATH_DELEGATE_DELETED | (S_ISDIR(cond.mode) ? PATH_IS_DIR : 0);
524*4882a593Smuzhiyun buffer = aa_get_buffer(in_atomic);
525*4882a593Smuzhiyun if (!buffer)
526*4882a593Smuzhiyun return -ENOMEM;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun /* check every profile in task label not in current cache */
529*4882a593Smuzhiyun error = fn_for_each_not_in_set(flabel, label, profile,
530*4882a593Smuzhiyun profile_path_perm(op, profile, &file->f_path, buffer,
531*4882a593Smuzhiyun request, &cond, flags, &perms));
532*4882a593Smuzhiyun if (denied && !error) {
533*4882a593Smuzhiyun /*
534*4882a593Smuzhiyun * check every profile in file label that was not tested
535*4882a593Smuzhiyun * in the initial check above.
536*4882a593Smuzhiyun *
537*4882a593Smuzhiyun * TODO: cache full perms so this only happens because of
538*4882a593Smuzhiyun * conditionals
539*4882a593Smuzhiyun * TODO: don't audit here
540*4882a593Smuzhiyun */
541*4882a593Smuzhiyun if (label == flabel)
542*4882a593Smuzhiyun error = fn_for_each(label, profile,
543*4882a593Smuzhiyun profile_path_perm(op, profile, &file->f_path,
544*4882a593Smuzhiyun buffer, request, &cond, flags,
545*4882a593Smuzhiyun &perms));
546*4882a593Smuzhiyun else
547*4882a593Smuzhiyun error = fn_for_each_not_in_set(label, flabel, profile,
548*4882a593Smuzhiyun profile_path_perm(op, profile, &file->f_path,
549*4882a593Smuzhiyun buffer, request, &cond, flags,
550*4882a593Smuzhiyun &perms));
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun if (!error)
553*4882a593Smuzhiyun update_file_ctx(file_ctx(file), label, request);
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun aa_put_buffer(buffer);
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun return error;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun
__file_sock_perm(const char * op,struct aa_label * label,struct aa_label * flabel,struct file * file,u32 request,u32 denied)560*4882a593Smuzhiyun static int __file_sock_perm(const char *op, struct aa_label *label,
561*4882a593Smuzhiyun struct aa_label *flabel, struct file *file,
562*4882a593Smuzhiyun u32 request, u32 denied)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun struct socket *sock = (struct socket *) file->private_data;
565*4882a593Smuzhiyun int error;
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun AA_BUG(!sock);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun /* revalidation due to label out of date. No revocation at this time */
570*4882a593Smuzhiyun if (!denied && aa_label_is_subset(flabel, label))
571*4882a593Smuzhiyun return 0;
572*4882a593Smuzhiyun
573*4882a593Smuzhiyun /* TODO: improve to skip profiles cached in flabel */
574*4882a593Smuzhiyun error = aa_sock_file_perm(label, op, request, sock);
575*4882a593Smuzhiyun if (denied) {
576*4882a593Smuzhiyun /* TODO: improve to skip profiles checked above */
577*4882a593Smuzhiyun /* check every profile in file label to is cached */
578*4882a593Smuzhiyun last_error(error, aa_sock_file_perm(flabel, op, request, sock));
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun if (!error)
581*4882a593Smuzhiyun update_file_ctx(file_ctx(file), label, request);
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun return error;
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun /**
587*4882a593Smuzhiyun * aa_file_perm - do permission revalidation check & audit for @file
588*4882a593Smuzhiyun * @op: operation being checked
589*4882a593Smuzhiyun * @label: label being enforced (NOT NULL)
590*4882a593Smuzhiyun * @file: file to revalidate access permissions on (NOT NULL)
591*4882a593Smuzhiyun * @request: requested permissions
592*4882a593Smuzhiyun * @in_atomic: whether allocations need to be done in atomic context
593*4882a593Smuzhiyun *
594*4882a593Smuzhiyun * Returns: %0 if access allowed else error
595*4882a593Smuzhiyun */
aa_file_perm(const char * op,struct aa_label * label,struct file * file,u32 request,bool in_atomic)596*4882a593Smuzhiyun int aa_file_perm(const char *op, struct aa_label *label, struct file *file,
597*4882a593Smuzhiyun u32 request, bool in_atomic)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun struct aa_file_ctx *fctx;
600*4882a593Smuzhiyun struct aa_label *flabel;
601*4882a593Smuzhiyun u32 denied;
602*4882a593Smuzhiyun int error = 0;
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun AA_BUG(!label);
605*4882a593Smuzhiyun AA_BUG(!file);
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun fctx = file_ctx(file);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun rcu_read_lock();
610*4882a593Smuzhiyun flabel = rcu_dereference(fctx->label);
611*4882a593Smuzhiyun AA_BUG(!flabel);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun /* revalidate access, if task is unconfined, or the cached cred
614*4882a593Smuzhiyun * doesn't match or if the request is for more permissions than
615*4882a593Smuzhiyun * was granted.
616*4882a593Smuzhiyun *
617*4882a593Smuzhiyun * Note: the test for !unconfined(flabel) is to handle file
618*4882a593Smuzhiyun * delegation from unconfined tasks
619*4882a593Smuzhiyun */
620*4882a593Smuzhiyun denied = request & ~fctx->allow;
621*4882a593Smuzhiyun if (unconfined(label) || unconfined(flabel) ||
622*4882a593Smuzhiyun (!denied && aa_label_is_subset(flabel, label))) {
623*4882a593Smuzhiyun rcu_read_unlock();
624*4882a593Smuzhiyun goto done;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun flabel = aa_get_newest_label(flabel);
628*4882a593Smuzhiyun rcu_read_unlock();
629*4882a593Smuzhiyun /* TODO: label cross check */
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry))
632*4882a593Smuzhiyun error = __file_path_perm(op, label, flabel, file, request,
633*4882a593Smuzhiyun denied, in_atomic);
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun else if (S_ISSOCK(file_inode(file)->i_mode))
636*4882a593Smuzhiyun error = __file_sock_perm(op, label, flabel, file, request,
637*4882a593Smuzhiyun denied);
638*4882a593Smuzhiyun aa_put_label(flabel);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun done:
641*4882a593Smuzhiyun return error;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun
revalidate_tty(struct aa_label * label)644*4882a593Smuzhiyun static void revalidate_tty(struct aa_label *label)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun struct tty_struct *tty;
647*4882a593Smuzhiyun int drop_tty = 0;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun tty = get_current_tty();
650*4882a593Smuzhiyun if (!tty)
651*4882a593Smuzhiyun return;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun spin_lock(&tty->files_lock);
654*4882a593Smuzhiyun if (!list_empty(&tty->tty_files)) {
655*4882a593Smuzhiyun struct tty_file_private *file_priv;
656*4882a593Smuzhiyun struct file *file;
657*4882a593Smuzhiyun /* TODO: Revalidate access to controlling tty. */
658*4882a593Smuzhiyun file_priv = list_first_entry(&tty->tty_files,
659*4882a593Smuzhiyun struct tty_file_private, list);
660*4882a593Smuzhiyun file = file_priv->file;
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun if (aa_file_perm(OP_INHERIT, label, file, MAY_READ | MAY_WRITE,
663*4882a593Smuzhiyun IN_ATOMIC))
664*4882a593Smuzhiyun drop_tty = 1;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun spin_unlock(&tty->files_lock);
667*4882a593Smuzhiyun tty_kref_put(tty);
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun if (drop_tty)
670*4882a593Smuzhiyun no_tty();
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun
match_file(const void * p,struct file * file,unsigned int fd)673*4882a593Smuzhiyun static int match_file(const void *p, struct file *file, unsigned int fd)
674*4882a593Smuzhiyun {
675*4882a593Smuzhiyun struct aa_label *label = (struct aa_label *)p;
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun if (aa_file_perm(OP_INHERIT, label, file, aa_map_file_to_perms(file),
678*4882a593Smuzhiyun IN_ATOMIC))
679*4882a593Smuzhiyun return fd + 1;
680*4882a593Smuzhiyun return 0;
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun /* based on selinux's flush_unauthorized_files */
aa_inherit_files(const struct cred * cred,struct files_struct * files)685*4882a593Smuzhiyun void aa_inherit_files(const struct cred *cred, struct files_struct *files)
686*4882a593Smuzhiyun {
687*4882a593Smuzhiyun struct aa_label *label = aa_get_newest_cred_label(cred);
688*4882a593Smuzhiyun struct file *devnull = NULL;
689*4882a593Smuzhiyun unsigned int n;
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun revalidate_tty(label);
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun /* Revalidate access to inherited open files. */
694*4882a593Smuzhiyun n = iterate_fd(files, 0, match_file, label);
695*4882a593Smuzhiyun if (!n) /* none found? */
696*4882a593Smuzhiyun goto out;
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun devnull = dentry_open(&aa_null, O_RDWR, cred);
699*4882a593Smuzhiyun if (IS_ERR(devnull))
700*4882a593Smuzhiyun devnull = NULL;
701*4882a593Smuzhiyun /* replace all the matching ones with this */
702*4882a593Smuzhiyun do {
703*4882a593Smuzhiyun replace_fd(n - 1, devnull, 0);
704*4882a593Smuzhiyun } while ((n = iterate_fd(files, n, match_file, label)) != 0);
705*4882a593Smuzhiyun if (devnull)
706*4882a593Smuzhiyun fput(devnull);
707*4882a593Smuzhiyun out:
708*4882a593Smuzhiyun aa_put_label(label);
709*4882a593Smuzhiyun }
710