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

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

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | 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.

osdn.jp