blob: fa49b81eb54caf9332728260844422433b6b85d0 [file] [log] [blame]
Thomas Gleixnerb886d83c2019-06-01 10:08:55 +02001// SPDX-License-Identifier: GPL-2.0-only
John Johansencdff2642010-07-29 14:47:57 -07002/*
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.
John Johansencdff2642010-07-29 14:47:57 -07009 */
10
John Johansen3b0aaf52017-01-16 00:42:23 -080011#include <linux/ctype.h>
Alexey Dobriyanb7f080c2011-06-16 11:01:34 +000012#include <linux/mm.h>
John Johansencdff2642010-07-29 14:47:57 -070013#include <linux/slab.h>
14#include <linux/string.h>
15#include <linux/vmalloc.h>
16
17#include "include/audit.h"
James Morris32c3df62011-08-29 11:15:25 +100018#include "include/apparmor.h"
John Johansen12557dc2017-01-16 00:42:13 -080019#include "include/lib.h"
John Johansenfc7e0b22017-05-26 01:57:09 -070020#include "include/perms.h"
John Johansenfe6bb312017-01-16 00:42:14 -080021#include "include/policy.h"
John Johansencdff2642010-07-29 14:47:57 -070022
John Johansen2d679f32017-05-29 12:19:39 -070023struct aa_perms nullperms;
John Johansenaa9aeea2017-05-29 12:16:04 -070024struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
25 .quiet = ALL_PERMS_MASK,
26 .hide = ALL_PERMS_MASK };
27
John Johansencdff2642010-07-29 14:47:57 -070028/**
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 */
42char *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], ':');
John Johansen04ccd532010-08-27 18:33:28 -070049 *ns_name = skip_spaces(&name[1]);
John Johansencdff2642010-07-29 14:47:57 -070050 if (split) {
51 /* overwrite ':' with \0 */
John Johansen2654bfb2013-02-27 03:45:05 -080052 *split++ = 0;
53 if (strncmp(split, "//", 2) == 0)
54 split += 2;
55 name = skip_spaces(split);
John Johansencdff2642010-07-29 14:47:57 -070056 } else
57 /* a ns name without a following profile is allowed */
58 name = NULL;
John Johansencdff2642010-07-29 14:47:57 -070059 }
60 if (name && *name == 0)
61 name = NULL;
62
63 return name;
64}
65
66/**
John Johansen3b0aaf52017-01-16 00:42:23 -080067 * 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
John Johansenb91deb92017-05-22 02:47:22 -070074const char *skipn_spaces(const char *str, size_t n)
John Johansen3b0aaf52017-01-16 00:42:23 -080075{
76 for (; n && isspace(*str); --n)
77 ++str;
78 if (n)
79 return (char *)str;
80 return NULL;
81}
82
83const 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
John Johansen3b0aaf52017-01-16 00:42:23 -080089 *ns_name = NULL;
90 *ns_len = 0;
Zubin Mithra250f2da2018-09-27 14:49:17 -070091
92 if (!name)
93 return NULL;
94
John Johansen3b0aaf52017-01-16 00:42:23 -080095 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/**
John Johansencdff2642010-07-29 14:47:57 -0700121 * aa_info_message - log a none profile related status message
122 * @str: message to log
123 */
124void aa_info_message(const char *str)
125{
126 if (audit_enabled) {
John Johansenef88a7a2017-01-16 00:43:02 -0800127 DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
128
129 aad(&sa)->info = str;
John Johansencdff2642010-07-29 14:47:57 -0700130 aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
131 }
132 printk(KERN_INFO "AppArmor: %s\n", str);
133}
134
John Johansena1bd6272017-06-09 07:09:05 -0700135__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
147void aa_str_kref(struct kref *kref)
148{
149 kfree(container_of(kref, struct counted_str, count));
150}
151
152
John Johansene53cfe62017-05-26 15:07:22 -0700153const char aa_file_perm_chrs[] = "xwracd km l ";
154const 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)
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000199 * @str_size: size of the @str buffer
200 * @chrs: NUL-terminated character buffer of permission characters
John Johansene53cfe62017-05-26 15:07:22 -0700201 * @mask: permission mask to convert
202 */
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000203void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
John Johansene53cfe62017-05-26 15:07:22 -0700204{
205 unsigned int i, perm = 1;
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000206 size_t num_chrs = strlen(chrs);
John Johansene53cfe62017-05-26 15:07:22 -0700207
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000208 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
John Johansene53cfe62017-05-26 15:07:22 -0700214 *str++ = chrs[i];
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000215 str_size--;
216 }
John Johansene53cfe62017-05-26 15:07:22 -0700217 }
218 *str = '\0';
219}
220
John Johansen56974a62017-07-18 23:18:33 -0700221void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
222 u32 mask)
John Johansenaa9aeea2017-05-29 12:16:04 -0700223{
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
239void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
John Johansen56974a62017-07-18 23:18:33 -0700240 u32 chrsmask, const char * const *names, u32 namesmask)
John Johansenaa9aeea2017-05-29 12:16:04 -0700241{
242 char str[33];
243
244 audit_log_format(ab, "\"");
245 if ((mask & chrsmask) && chrs) {
Tyler Hicks7f3ebcf2018-07-06 05:25:00 +0000246 aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
John Johansenaa9aeea2017-05-29 12:16:04 -0700247 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/**
John Johansen637f6882017-06-09 08:14:28 -0700258 * 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 */
262static 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/**
John Johansenaa9aeea2017-05-29 12:16:04 -0700284 * 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 */
290void 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;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500295 fallthrough;
John Johansenaa9aeea2017-05-29 12:16:04 -0700296 case AUDIT_NOQUIET:
297 perms->quiet = 0;
298 break;
299 case AUDIT_QUIET:
300 perms->audit = 0;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500301 fallthrough;
John Johansenaa9aeea2017-05-29 12:16:04 -0700302 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
318static 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
325void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
326 struct aa_perms *perms)
327{
Arnd Bergmann7bba39a2017-09-15 21:55:46 +0200328 *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 };
John Johansenaa9aeea2017-05-29 12:16:04 -0700333
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
John Johansencdff2642010-07-29 14:47:57 -0700343/**
John Johansen637f6882017-06-09 08:14:28 -0700344 * 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 */
348void 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 */
367void 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
381void 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 */
395int 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)
Zygmunt Krynicki69ad4a42018-04-12 12:34:31 +0200418 * @cb: callback fn for type specific fields (MAY BE NULL)
John Johansen637f6882017-06-09 08:14:28 -0700419 *
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 */
429int 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;
John Johansen637f6882017-06-09 08:14:28 -0700434 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
John Johansen637f6882017-06-09 08:14:28 -0700454 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/**
John Johansenfe6bb312017-01-16 00:42:14 -0800478 * 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)
John Johansena1bd6272017-06-09 07:09:05 -0700482 * @gfp: allocation mode
John Johansenfe6bb312017-01-16 00:42:14 -0800483 *
484 * Note: this fn creates a copy of strings passed in
485 *
486 * Returns: true if policy init successful
487 */
488bool aa_policy_init(struct aa_policy *policy, const char *prefix,
John Johansend102d892017-01-16 00:42:31 -0800489 const char *name, gfp_t gfp)
John Johansenfe6bb312017-01-16 00:42:14 -0800490{
John Johansena1bd6272017-06-09 07:09:05 -0700491 char *hname;
492
John Johansenfe6bb312017-01-16 00:42:14 -0800493 /* freed by policy_free */
494 if (prefix) {
John Johansena1bd6272017-06-09 07:09:05 -0700495 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)
kbuild test robotb9c42ac2017-04-06 06:55:19 -0700504 return false;
John Johansena1bd6272017-06-09 07:09:05 -0700505 policy->hname = hname;
John Johansenfe6bb312017-01-16 00:42:14 -0800506 /* base.name is a substring of fqname */
John Johansend102d892017-01-16 00:42:31 -0800507 policy->name = basename(policy->hname);
John Johansenfe6bb312017-01-16 00:42:14 -0800508 INIT_LIST_HEAD(&policy->list);
509 INIT_LIST_HEAD(&policy->profiles);
510
kbuild test robotb9c42ac2017-04-06 06:55:19 -0700511 return true;
John Johansenfe6bb312017-01-16 00:42:14 -0800512}
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 */
518void aa_policy_destroy(struct aa_policy *policy)
519{
John Johansen5fd1b952017-01-16 00:42:32 -0800520 AA_BUG(on_list_rcu(&policy->profiles));
521 AA_BUG(on_list_rcu(&policy->list));
John Johansenfe6bb312017-01-16 00:42:14 -0800522
523 /* don't free name as its a subset of hname */
John Johansena1bd6272017-06-09 07:09:05 -0700524 aa_put_str(policy->hname);
John Johansenfe6bb312017-01-16 00:42:14 -0800525}