]> Git Repo - J-linux.git/blob - security/apparmor/lib.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / security / apparmor / lib.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AppArmor security module
4  *
5  * This file contains basic common functions used in AppArmor
6  *
7  * Copyright (C) 1998-2008 Novell/SUSE
8  * Copyright 2009-2010 Canonical Ltd.
9  */
10
11 #include <linux/ctype.h>
12 #include <linux/mm.h>
13 #include <linux/slab.h>
14 #include <linux/string.h>
15 #include <linux/vmalloc.h>
16
17 #include "include/audit.h"
18 #include "include/apparmor.h"
19 #include "include/lib.h"
20 #include "include/perms.h"
21 #include "include/policy.h"
22
23 struct aa_perms nullperms;
24 struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
25                              .quiet = ALL_PERMS_MASK,
26                              .hide = ALL_PERMS_MASK };
27
28 /**
29  * aa_free_str_table - free entries str table
30  * @t: the string table to free  (MAYBE NULL)
31  */
32 void aa_free_str_table(struct aa_str_table *t)
33 {
34         int i;
35
36         if (t) {
37                 if (!t->table)
38                         return;
39
40                 for (i = 0; i < t->size; i++)
41                         kfree_sensitive(t->table[i]);
42                 kfree_sensitive(t->table);
43                 t->table = NULL;
44                 t->size = 0;
45         }
46 }
47
48 /**
49  * skipn_spaces - Removes leading whitespace from @str.
50  * @str: The string to be stripped.
51  * @n: length of str to parse, will stop at \0 if encountered before n
52  *
53  * Returns a pointer to the first non-whitespace character in @str.
54  * if all whitespace will return NULL
55  */
56
57 const char *skipn_spaces(const char *str, size_t n)
58 {
59         for (; n && isspace(*str); --n)
60                 ++str;
61         if (n)
62                 return (char *)str;
63         return NULL;
64 }
65
66 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
67                              size_t *ns_len)
68 {
69         const char *end = fqname + n;
70         const char *name = skipn_spaces(fqname, n);
71
72         *ns_name = NULL;
73         *ns_len = 0;
74
75         if (!name)
76                 return NULL;
77
78         if (name[0] == ':') {
79                 char *split = strnchr(&name[1], end - &name[1], ':');
80                 *ns_name = skipn_spaces(&name[1], end - &name[1]);
81                 if (!*ns_name)
82                         return NULL;
83                 if (split) {
84                         *ns_len = split - *ns_name;
85                         if (*ns_len == 0)
86                                 *ns_name = NULL;
87                         split++;
88                         if (end - split > 1 && strncmp(split, "//", 2) == 0)
89                                 split += 2;
90                         name = skipn_spaces(split, end - split);
91                 } else {
92                         /* a ns name without a following profile is allowed */
93                         name = NULL;
94                         *ns_len = end - *ns_name;
95                 }
96         }
97         if (name && *name == 0)
98                 name = NULL;
99
100         return name;
101 }
102
103 /**
104  * aa_info_message - log a none profile related status message
105  * @str: message to log
106  */
107 void aa_info_message(const char *str)
108 {
109         if (audit_enabled) {
110                 DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
111
112                 ad.info = str;
113                 aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL);
114         }
115         printk(KERN_INFO "AppArmor: %s\n", str);
116 }
117
118 __counted char *aa_str_alloc(int size, gfp_t gfp)
119 {
120         struct counted_str *str;
121
122         str = kmalloc(struct_size(str, name, size), gfp);
123         if (!str)
124                 return NULL;
125
126         kref_init(&str->count);
127         return str->name;
128 }
129
130 void aa_str_kref(struct kref *kref)
131 {
132         kfree(container_of(kref, struct counted_str, count));
133 }
134
135
136 const char aa_file_perm_chrs[] = "xwracd         km l     ";
137 const char *aa_file_perm_names[] = {
138         "exec",
139         "write",
140         "read",
141         "append",
142
143         "create",
144         "delete",
145         "open",
146         "rename",
147
148         "setattr",
149         "getattr",
150         "setcred",
151         "getcred",
152
153         "chmod",
154         "chown",
155         "chgrp",
156         "lock",
157
158         "mmap",
159         "mprot",
160         "link",
161         "snapshot",
162
163         "unknown",
164         "unknown",
165         "unknown",
166         "unknown",
167
168         "unknown",
169         "unknown",
170         "unknown",
171         "unknown",
172
173         "stack",
174         "change_onexec",
175         "change_profile",
176         "change_hat",
177 };
178
179 /**
180  * aa_perm_mask_to_str - convert a perm mask to its short string
181  * @str: character buffer to store string in (at least 10 characters)
182  * @str_size: size of the @str buffer
183  * @chrs: NUL-terminated character buffer of permission characters
184  * @mask: permission mask to convert
185  */
186 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
187 {
188         unsigned int i, perm = 1;
189         size_t num_chrs = strlen(chrs);
190
191         for (i = 0; i < num_chrs; perm <<= 1, i++) {
192                 if (mask & perm) {
193                         /* Ensure that one byte is left for NUL-termination */
194                         if (WARN_ON_ONCE(str_size <= 1))
195                                 break;
196
197                         *str++ = chrs[i];
198                         str_size--;
199                 }
200         }
201         *str = '\0';
202 }
203
204 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
205                          u32 mask)
206 {
207         const char *fmt = "%s";
208         unsigned int i, perm = 1;
209         bool prev = false;
210
211         for (i = 0; i < 32; perm <<= 1, i++) {
212                 if (mask & perm) {
213                         audit_log_format(ab, fmt, names[i]);
214                         if (!prev) {
215                                 prev = true;
216                                 fmt = " %s";
217                         }
218                 }
219         }
220 }
221
222 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
223                         u32 chrsmask, const char * const *names, u32 namesmask)
224 {
225         char str[33];
226
227         audit_log_format(ab, "\"");
228         if ((mask & chrsmask) && chrs) {
229                 aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
230                 mask &= ~chrsmask;
231                 audit_log_format(ab, "%s", str);
232                 if (mask & namesmask)
233                         audit_log_format(ab, " ");
234         }
235         if ((mask & namesmask) && names)
236                 aa_audit_perm_names(ab, names, mask & namesmask);
237         audit_log_format(ab, "\"");
238 }
239
240 /**
241  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
242  * @profile: that perms where computed from
243  * @perms: perms to apply mode modifiers to
244  *
245  * TODO: split into profile and ns based flags for when accumulating perms
246  */
247 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
248 {
249         switch (AUDIT_MODE(profile)) {
250         case AUDIT_ALL:
251                 perms->audit = ALL_PERMS_MASK;
252                 fallthrough;
253         case AUDIT_NOQUIET:
254                 perms->quiet = 0;
255                 break;
256         case AUDIT_QUIET:
257                 perms->audit = 0;
258                 fallthrough;
259         case AUDIT_QUIET_DENIED:
260                 perms->quiet = ALL_PERMS_MASK;
261                 break;
262         }
263
264         if (KILL_MODE(profile))
265                 perms->kill = ALL_PERMS_MASK;
266         else if (COMPLAIN_MODE(profile))
267                 perms->complain = ALL_PERMS_MASK;
268         else if (USER_MODE(profile))
269                 perms->prompt = ALL_PERMS_MASK;
270 }
271
272 void aa_profile_match_label(struct aa_profile *profile,
273                             struct aa_ruleset *rules,
274                             struct aa_label *label,
275                             int type, u32 request, struct aa_perms *perms)
276 {
277         /* TODO: doesn't yet handle extended types */
278         aa_state_t state;
279
280         state = aa_dfa_next(rules->policy->dfa,
281                             rules->policy->start[AA_CLASS_LABEL],
282                             type);
283         aa_label_match(profile, rules, label, state, false, request, perms);
284 }
285
286
287 /**
288  * aa_check_perms - do audit mode selection based on perms set
289  * @profile: profile being checked
290  * @perms: perms computed for the request
291  * @request: requested perms
292  * @ad: initialized audit structure (MAY BE NULL if not auditing)
293  * @cb: callback fn for type specific fields (MAY BE NULL)
294  *
295  * Returns: 0 if permission else error code
296  *
297  * Note: profile audit modes need to be set before calling by setting the
298  *       perm masks appropriately.
299  *
300  *       If not auditing then complain mode is not enabled and the
301  *       error code will indicate whether there was an explicit deny
302  *       with a positive value.
303  */
304 int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
305                    u32 request, struct apparmor_audit_data *ad,
306                    void (*cb)(struct audit_buffer *, void *))
307 {
308         int type, error;
309         u32 denied = request & (~perms->allow | perms->deny);
310
311         if (likely(!denied)) {
312                 /* mask off perms that are not being force audited */
313                 request &= perms->audit;
314                 if (!request || !ad)
315                         return 0;
316
317                 type = AUDIT_APPARMOR_AUDIT;
318                 error = 0;
319         } else {
320                 error = -EACCES;
321
322                 if (denied & perms->kill)
323                         type = AUDIT_APPARMOR_KILL;
324                 else if (denied == (denied & perms->complain))
325                         type = AUDIT_APPARMOR_ALLOWED;
326                 else
327                         type = AUDIT_APPARMOR_DENIED;
328
329                 if (denied == (denied & perms->hide))
330                         error = -ENOENT;
331
332                 denied &= ~perms->quiet;
333                 if (!ad || !denied)
334                         return error;
335         }
336
337         if (ad) {
338                 ad->subj_label = &profile->label;
339                 ad->request = request;
340                 ad->denied = denied;
341                 ad->error = error;
342                 aa_audit_msg(type, ad, cb);
343         }
344
345         if (type == AUDIT_APPARMOR_ALLOWED)
346                 error = 0;
347
348         return error;
349 }
350
351
352 /**
353  * aa_policy_init - initialize a policy structure
354  * @policy: policy to initialize  (NOT NULL)
355  * @prefix: prefix name if any is required.  (MAYBE NULL)
356  * @name: name of the policy, init will make a copy of it  (NOT NULL)
357  * @gfp: allocation mode
358  *
359  * Note: this fn creates a copy of strings passed in
360  *
361  * Returns: true if policy init successful
362  */
363 bool aa_policy_init(struct aa_policy *policy, const char *prefix,
364                     const char *name, gfp_t gfp)
365 {
366         char *hname;
367
368         /* freed by policy_free */
369         if (prefix) {
370                 hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
371                 if (hname)
372                         sprintf(hname, "%s//%s", prefix, name);
373         } else {
374                 hname = aa_str_alloc(strlen(name) + 1, gfp);
375                 if (hname)
376                         strcpy(hname, name);
377         }
378         if (!hname)
379                 return false;
380         policy->hname = hname;
381         /* base.name is a substring of fqname */
382         policy->name = basename(policy->hname);
383         INIT_LIST_HEAD(&policy->list);
384         INIT_LIST_HEAD(&policy->profiles);
385
386         return true;
387 }
388
389 /**
390  * aa_policy_destroy - free the elements referenced by @policy
391  * @policy: policy that is to have its elements freed  (NOT NULL)
392  */
393 void aa_policy_destroy(struct aa_policy *policy)
394 {
395         AA_BUG(on_list_rcu(&policy->profiles));
396         AA_BUG(on_list_rcu(&policy->list));
397
398         /* don't free name as its a subset of hname */
399         aa_put_str(policy->hname);
400 }
This page took 0.048708 seconds and 4 git commands to generate.