Merge tag 'spi-fix-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[platform/kernel/linux-starfive.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  * @str: 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         }
45 }
46
47 /**
48  * aa_split_fqname - split a fqname into a profile and namespace name
49  * @fqname: a full qualified name in namespace profile format (NOT NULL)
50  * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
51  *
52  * Returns: profile name or NULL if one is not specified
53  *
54  * Split a namespace name from a profile name (see policy.c for naming
55  * description).  If a portion of the name is missing it returns NULL for
56  * that portion.
57  *
58  * NOTE: may modify the @fqname string.  The pointers returned point
59  *       into the @fqname string.
60  */
61 char *aa_split_fqname(char *fqname, char **ns_name)
62 {
63         char *name = strim(fqname);
64
65         *ns_name = NULL;
66         if (name[0] == ':') {
67                 char *split = strchr(&name[1], ':');
68                 *ns_name = skip_spaces(&name[1]);
69                 if (split) {
70                         /* overwrite ':' with \0 */
71                         *split++ = 0;
72                         if (strncmp(split, "//", 2) == 0)
73                                 split += 2;
74                         name = skip_spaces(split);
75                 } else
76                         /* a ns name without a following profile is allowed */
77                         name = NULL;
78         }
79         if (name && *name == 0)
80                 name = NULL;
81
82         return name;
83 }
84
85 /**
86  * skipn_spaces - Removes leading whitespace from @str.
87  * @str: The string to be stripped.
88  *
89  * Returns a pointer to the first non-whitespace character in @str.
90  * if all whitespace will return NULL
91  */
92
93 const char *skipn_spaces(const char *str, size_t n)
94 {
95         for (; n && isspace(*str); --n)
96                 ++str;
97         if (n)
98                 return (char *)str;
99         return NULL;
100 }
101
102 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
103                              size_t *ns_len)
104 {
105         const char *end = fqname + n;
106         const char *name = skipn_spaces(fqname, n);
107
108         *ns_name = NULL;
109         *ns_len = 0;
110
111         if (!name)
112                 return NULL;
113
114         if (name[0] == ':') {
115                 char *split = strnchr(&name[1], end - &name[1], ':');
116                 *ns_name = skipn_spaces(&name[1], end - &name[1]);
117                 if (!*ns_name)
118                         return NULL;
119                 if (split) {
120                         *ns_len = split - *ns_name;
121                         if (*ns_len == 0)
122                                 *ns_name = NULL;
123                         split++;
124                         if (end - split > 1 && strncmp(split, "//", 2) == 0)
125                                 split += 2;
126                         name = skipn_spaces(split, end - split);
127                 } else {
128                         /* a ns name without a following profile is allowed */
129                         name = NULL;
130                         *ns_len = end - *ns_name;
131                 }
132         }
133         if (name && *name == 0)
134                 name = NULL;
135
136         return name;
137 }
138
139 /**
140  * aa_info_message - log a none profile related status message
141  * @str: message to log
142  */
143 void aa_info_message(const char *str)
144 {
145         if (audit_enabled) {
146                 DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
147
148                 aad(&sa)->info = str;
149                 aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
150         }
151         printk(KERN_INFO "AppArmor: %s\n", str);
152 }
153
154 __counted char *aa_str_alloc(int size, gfp_t gfp)
155 {
156         struct counted_str *str;
157
158         str = kmalloc(struct_size(str, name, size), gfp);
159         if (!str)
160                 return NULL;
161
162         kref_init(&str->count);
163         return str->name;
164 }
165
166 void aa_str_kref(struct kref *kref)
167 {
168         kfree(container_of(kref, struct counted_str, count));
169 }
170
171
172 const char aa_file_perm_chrs[] = "xwracd         km l     ";
173 const char *aa_file_perm_names[] = {
174         "exec",
175         "write",
176         "read",
177         "append",
178
179         "create",
180         "delete",
181         "open",
182         "rename",
183
184         "setattr",
185         "getattr",
186         "setcred",
187         "getcred",
188
189         "chmod",
190         "chown",
191         "chgrp",
192         "lock",
193
194         "mmap",
195         "mprot",
196         "link",
197         "snapshot",
198
199         "unknown",
200         "unknown",
201         "unknown",
202         "unknown",
203
204         "unknown",
205         "unknown",
206         "unknown",
207         "unknown",
208
209         "stack",
210         "change_onexec",
211         "change_profile",
212         "change_hat",
213 };
214
215 /**
216  * aa_perm_mask_to_str - convert a perm mask to its short string
217  * @str: character buffer to store string in (at least 10 characters)
218  * @str_size: size of the @str buffer
219  * @chrs: NUL-terminated character buffer of permission characters
220  * @mask: permission mask to convert
221  */
222 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
223 {
224         unsigned int i, perm = 1;
225         size_t num_chrs = strlen(chrs);
226
227         for (i = 0; i < num_chrs; perm <<= 1, i++) {
228                 if (mask & perm) {
229                         /* Ensure that one byte is left for NUL-termination */
230                         if (WARN_ON_ONCE(str_size <= 1))
231                                 break;
232
233                         *str++ = chrs[i];
234                         str_size--;
235                 }
236         }
237         *str = '\0';
238 }
239
240 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
241                          u32 mask)
242 {
243         const char *fmt = "%s";
244         unsigned int i, perm = 1;
245         bool prev = false;
246
247         for (i = 0; i < 32; perm <<= 1, i++) {
248                 if (mask & perm) {
249                         audit_log_format(ab, fmt, names[i]);
250                         if (!prev) {
251                                 prev = true;
252                                 fmt = " %s";
253                         }
254                 }
255         }
256 }
257
258 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
259                         u32 chrsmask, const char * const *names, u32 namesmask)
260 {
261         char str[33];
262
263         audit_log_format(ab, "\"");
264         if ((mask & chrsmask) && chrs) {
265                 aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
266                 mask &= ~chrsmask;
267                 audit_log_format(ab, "%s", str);
268                 if (mask & namesmask)
269                         audit_log_format(ab, " ");
270         }
271         if ((mask & namesmask) && names)
272                 aa_audit_perm_names(ab, names, mask & namesmask);
273         audit_log_format(ab, "\"");
274 }
275
276 /**
277  * aa_audit_perms_cb - generic callback fn for auditing perms
278  * @ab: audit buffer (NOT NULL)
279  * @va: audit struct to audit values of (NOT NULL)
280  */
281 static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
282 {
283         struct common_audit_data *sa = va;
284
285         if (aad(sa)->request) {
286                 audit_log_format(ab, " requested_mask=");
287                 aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
288                                    PERMS_CHRS_MASK, aa_file_perm_names,
289                                    PERMS_NAMES_MASK);
290         }
291         if (aad(sa)->denied) {
292                 audit_log_format(ab, "denied_mask=");
293                 aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
294                                    PERMS_CHRS_MASK, aa_file_perm_names,
295                                    PERMS_NAMES_MASK);
296         }
297         audit_log_format(ab, " peer=");
298         aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
299                                       FLAGS_NONE, GFP_ATOMIC);
300 }
301
302 /**
303  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
304  * @profile: that perms where computed from
305  * @perms: perms to apply mode modifiers to
306  *
307  * TODO: split into profile and ns based flags for when accumulating perms
308  */
309 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
310 {
311         switch (AUDIT_MODE(profile)) {
312         case AUDIT_ALL:
313                 perms->audit = ALL_PERMS_MASK;
314                 fallthrough;
315         case AUDIT_NOQUIET:
316                 perms->quiet = 0;
317                 break;
318         case AUDIT_QUIET:
319                 perms->audit = 0;
320                 fallthrough;
321         case AUDIT_QUIET_DENIED:
322                 perms->quiet = ALL_PERMS_MASK;
323                 break;
324         }
325
326         if (KILL_MODE(profile))
327                 perms->kill = ALL_PERMS_MASK;
328         else if (COMPLAIN_MODE(profile))
329                 perms->complain = ALL_PERMS_MASK;
330         else if (USER_MODE(profile))
331                 perms->prompt = ALL_PERMS_MASK;
332 }
333
334 void aa_profile_match_label(struct aa_profile *profile,
335                             struct aa_ruleset *rules,
336                             struct aa_label *label,
337                             int type, u32 request, struct aa_perms *perms)
338 {
339         /* TODO: doesn't yet handle extended types */
340         aa_state_t state;
341
342         state = aa_dfa_next(rules->policy.dfa,
343                             rules->policy.start[AA_CLASS_LABEL],
344                             type);
345         aa_label_match(profile, rules, label, state, false, request, perms);
346 }
347
348
349 /* currently unused */
350 int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
351                           u32 request, int type, u32 *deny,
352                           struct common_audit_data *sa)
353 {
354         struct aa_ruleset *rules = list_first_entry(&profile->rules,
355                                                     typeof(*rules), list);
356         struct aa_perms perms;
357
358         aad(sa)->label = &profile->label;
359         aad(sa)->peer = &target->label;
360         aad(sa)->request = request;
361
362         aa_profile_match_label(profile, rules, &target->label, type, request,
363                                &perms);
364         aa_apply_modes_to_perms(profile, &perms);
365         *deny |= request & perms.deny;
366         return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
367 }
368
369 /**
370  * aa_check_perms - do audit mode selection based on perms set
371  * @profile: profile being checked
372  * @perms: perms computed for the request
373  * @request: requested perms
374  * @deny: Returns: explicit deny set
375  * @sa: initialized audit structure (MAY BE NULL if not auditing)
376  * @cb: callback fn for type specific fields (MAY BE NULL)
377  *
378  * Returns: 0 if permission else error code
379  *
380  * Note: profile audit modes need to be set before calling by setting the
381  *       perm masks appropriately.
382  *
383  *       If not auditing then complain mode is not enabled and the
384  *       error code will indicate whether there was an explicit deny
385  *       with a positive value.
386  */
387 int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
388                    u32 request, struct common_audit_data *sa,
389                    void (*cb)(struct audit_buffer *, void *))
390 {
391         int type, error;
392         u32 denied = request & (~perms->allow | perms->deny);
393
394         if (likely(!denied)) {
395                 /* mask off perms that are not being force audited */
396                 request &= perms->audit;
397                 if (!request || !sa)
398                         return 0;
399
400                 type = AUDIT_APPARMOR_AUDIT;
401                 error = 0;
402         } else {
403                 error = -EACCES;
404
405                 if (denied & perms->kill)
406                         type = AUDIT_APPARMOR_KILL;
407                 else if (denied == (denied & perms->complain))
408                         type = AUDIT_APPARMOR_ALLOWED;
409                 else
410                         type = AUDIT_APPARMOR_DENIED;
411
412                 if (denied == (denied & perms->hide))
413                         error = -ENOENT;
414
415                 denied &= ~perms->quiet;
416                 if (!sa || !denied)
417                         return error;
418         }
419
420         if (sa) {
421                 aad(sa)->label = &profile->label;
422                 aad(sa)->request = request;
423                 aad(sa)->denied = denied;
424                 aad(sa)->error = error;
425                 aa_audit_msg(type, sa, cb);
426         }
427
428         if (type == AUDIT_APPARMOR_ALLOWED)
429                 error = 0;
430
431         return error;
432 }
433
434
435 /**
436  * aa_policy_init - initialize a policy structure
437  * @policy: policy to initialize  (NOT NULL)
438  * @prefix: prefix name if any is required.  (MAYBE NULL)
439  * @name: name of the policy, init will make a copy of it  (NOT NULL)
440  * @gfp: allocation mode
441  *
442  * Note: this fn creates a copy of strings passed in
443  *
444  * Returns: true if policy init successful
445  */
446 bool aa_policy_init(struct aa_policy *policy, const char *prefix,
447                     const char *name, gfp_t gfp)
448 {
449         char *hname;
450
451         /* freed by policy_free */
452         if (prefix) {
453                 hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
454                 if (hname)
455                         sprintf(hname, "%s//%s", prefix, name);
456         } else {
457                 hname = aa_str_alloc(strlen(name) + 1, gfp);
458                 if (hname)
459                         strcpy(hname, name);
460         }
461         if (!hname)
462                 return false;
463         policy->hname = hname;
464         /* base.name is a substring of fqname */
465         policy->name = basename(policy->hname);
466         INIT_LIST_HEAD(&policy->list);
467         INIT_LIST_HEAD(&policy->profiles);
468
469         return true;
470 }
471
472 /**
473  * aa_policy_destroy - free the elements referenced by @policy
474  * @policy: policy that is to have its elements freed  (NOT NULL)
475  */
476 void aa_policy_destroy(struct aa_policy *policy)
477 {
478         AA_BUG(on_list_rcu(&policy->profiles));
479         AA_BUG(on_list_rcu(&policy->list));
480
481         /* don't free name as its a subset of hname */
482         aa_put_str(policy->hname);
483 }