~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/security/apparmor/lib.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php