c87bccafff446c333fd649aae7525bbfdf7488f5
[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  * @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         }
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  * @n: length of str to parse, will stop at \0 if encountered before n
89  *
90  * Returns a pointer to the first non-whitespace character in @str.
91  * if all whitespace will return NULL
92  */
93
94 const char *skipn_spaces(const char *str, size_t n)
95 {
96         for (; n && isspace(*str); --n)
97                 ++str;
98         if (n)
99                 return (char *)str;
100         return NULL;
101 }
102
103 const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
104                              size_t *ns_len)
105 {
106         const char *end = fqname + n;
107         const char *name = skipn_spaces(fqname, n);
108
109         *ns_name = NULL;
110         *ns_len = 0;
111
112         if (!name)
113                 return NULL;
114
115         if (name[0] == ':') {
116                 char *split = strnchr(&name[1], end - &name[1], ':');
117                 *ns_name = skipn_spaces(&name[1], end - &name[1]);
118                 if (!*ns_name)
119                         return NULL;
120                 if (split) {
121                         *ns_len = split - *ns_name;
122                         if (*ns_len == 0)
123                                 *ns_name = NULL;
124                         split++;
125                         if (end - split > 1 && strncmp(split, "//", 2) == 0)
126                                 split += 2;
127                         name = skipn_spaces(split, end - split);
128                 } else {
129                         /* a ns name without a following profile is allowed */
130                         name = NULL;
131                         *ns_len = end - *ns_name;
132                 }
133         }
134         if (name && *name == 0)
135                 name = NULL;
136
137         return name;
138 }
139
140 /**
141  * aa_info_message - log a none profile related status message
142  * @str: message to log
143  */
144 void aa_info_message(const char *str)
145 {
146         if (audit_enabled) {
147                 DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
148
149                 ad.info = str;
150                 aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL);
151         }
152         printk(KERN_INFO "AppArmor: %s\n", str);
153 }
154
155 __counted char *aa_str_alloc(int size, gfp_t gfp)
156 {
157         struct counted_str *str;
158
159         str = kmalloc(struct_size(str, name, size), gfp);
160         if (!str)
161                 return NULL;
162
163         kref_init(&str->count);
164         return str->name;
165 }
166
167 void aa_str_kref(struct kref *kref)
168 {
169         kfree(container_of(kref, struct counted_str, count));
170 }
171
172
173 const char aa_file_perm_chrs[] = "xwracd         km l     ";
174 const char *aa_file_perm_names[] = {
175         "exec",
176         "write",
177         "read",
178         "append",
179
180         "create",
181         "delete",
182         "open",
183         "rename",
184
185         "setattr",
186         "getattr",
187         "setcred",
188         "getcred",
189
190         "chmod",
191         "chown",
192         "chgrp",
193         "lock",
194
195         "mmap",
196         "mprot",
197         "link",
198         "snapshot",
199
200         "unknown",
201         "unknown",
202         "unknown",
203         "unknown",
204
205         "unknown",
206         "unknown",
207         "unknown",
208         "unknown",
209
210         "stack",
211         "change_onexec",
212         "change_profile",
213         "change_hat",
214 };
215
216 /**
217  * aa_perm_mask_to_str - convert a perm mask to its short string
218  * @str: character buffer to store string in (at least 10 characters)
219  * @str_size: size of the @str buffer
220  * @chrs: NUL-terminated character buffer of permission characters
221  * @mask: permission mask to convert
222  */
223 void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
224 {
225         unsigned int i, perm = 1;
226         size_t num_chrs = strlen(chrs);
227
228         for (i = 0; i < num_chrs; perm <<= 1, i++) {
229                 if (mask & perm) {
230                         /* Ensure that one byte is left for NUL-termination */
231                         if (WARN_ON_ONCE(str_size <= 1))
232                                 break;
233
234                         *str++ = chrs[i];
235                         str_size--;
236                 }
237         }
238         *str = '\0';
239 }
240
241 void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
242                          u32 mask)
243 {
244         const char *fmt = "%s";
245         unsigned int i, perm = 1;
246         bool prev = false;
247
248         for (i = 0; i < 32; perm <<= 1, i++) {
249                 if (mask & perm) {
250                         audit_log_format(ab, fmt, names[i]);
251                         if (!prev) {
252                                 prev = true;
253                                 fmt = " %s";
254                         }
255                 }
256         }
257 }
258
259 void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
260                         u32 chrsmask, const char * const *names, u32 namesmask)
261 {
262         char str[33];
263
264         audit_log_format(ab, "\"");
265         if ((mask & chrsmask) && chrs) {
266                 aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
267                 mask &= ~chrsmask;
268                 audit_log_format(ab, "%s", str);
269                 if (mask & namesmask)
270                         audit_log_format(ab, " ");
271         }
272         if ((mask & namesmask) && names)
273                 aa_audit_perm_names(ab, names, mask & namesmask);
274         audit_log_format(ab, "\"");
275 }
276
277 /**
278  * aa_audit_perms_cb - generic callback fn for auditing perms
279  * @ab: audit buffer (NOT NULL)
280  * @va: audit struct to audit values of (NOT NULL)
281  */
282 static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
283 {
284         struct common_audit_data *sa = va;
285         struct apparmor_audit_data *ad = aad(sa);
286
287         if (ad->request) {
288                 audit_log_format(ab, " requested_mask=");
289                 aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs,
290                                    PERMS_CHRS_MASK, aa_file_perm_names,
291                                    PERMS_NAMES_MASK);
292         }
293         if (ad->denied) {
294                 audit_log_format(ab, "denied_mask=");
295                 aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs,
296                                    PERMS_CHRS_MASK, aa_file_perm_names,
297                                    PERMS_NAMES_MASK);
298         }
299         audit_log_format(ab, " peer=");
300         aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
301                                       FLAGS_NONE, GFP_ATOMIC);
302 }
303
304 /**
305  * aa_apply_modes_to_perms - apply namespace and profile flags to perms
306  * @profile: that perms where computed from
307  * @perms: perms to apply mode modifiers to
308  *
309  * TODO: split into profile and ns based flags for when accumulating perms
310  */
311 void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
312 {
313         switch (AUDIT_MODE(profile)) {
314         case AUDIT_ALL:
315                 perms->audit = ALL_PERMS_MASK;
316                 fallthrough;
317         case AUDIT_NOQUIET:
318                 perms->quiet = 0;
319                 break;
320         case AUDIT_QUIET:
321                 perms->audit = 0;
322                 fallthrough;
323         case AUDIT_QUIET_DENIED:
324                 perms->quiet = ALL_PERMS_MASK;
325                 break;
326         }
327
328         if (KILL_MODE(profile))
329                 perms->kill = ALL_PERMS_MASK;
330         else if (COMPLAIN_MODE(profile))
331                 perms->complain = ALL_PERMS_MASK;
332         else if (USER_MODE(profile))
333                 perms->prompt = ALL_PERMS_MASK;
334 }
335
336 void aa_profile_match_label(struct aa_profile *profile,
337                             struct aa_ruleset *rules,
338                             struct aa_label *label,
339                             int type, u32 request, struct aa_perms *perms)
340 {
341         /* TODO: doesn't yet handle extended types */
342         aa_state_t state;
343
344         state = aa_dfa_next(rules->policy.dfa,
345                             rules->policy.start[AA_CLASS_LABEL],
346                             type);
347         aa_label_match(profile, rules, label, state, false, request, perms);
348 }
349
350
351 /* currently unused */
352 int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
353                           u32 request, int type, u32 *deny,
354                           struct apparmor_audit_data *ad)
355 {
356         struct aa_ruleset *rules = list_first_entry(&profile->rules,
357                                                     typeof(*rules), list);
358         struct aa_perms perms;
359
360         ad->peer = &target->label;
361         ad->request = request;
362
363         aa_profile_match_label(profile, rules, &target->label, type, request,
364                                &perms);
365         aa_apply_modes_to_perms(profile, &perms);
366         *deny |= request & perms.deny;
367         return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb);
368 }
369
370 /**
371  * aa_check_perms - do audit mode selection based on perms set
372  * @profile: profile being checked
373  * @perms: perms computed for the request
374  * @request: requested perms
375  * @ad: 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 apparmor_audit_data *ad,
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 || !ad)
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 (!ad || !denied)
417                         return error;
418         }
419
420         if (ad) {
421                 ad->subj_label = &profile->label;
422                 ad->request = request;
423                 ad->denied = denied;
424                 ad->error = error;
425                 aa_audit_msg(type, ad, 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 }