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
23struct aa_perms nullperms;
24struct 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 */
32void 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(objp: t->table[i]);
42 kfree_sensitive(objp: 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 */
61char *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
94const 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
103const 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(str: 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(str: &name[1], n: 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(str: split, n: 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 */
144void 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(type: AUDIT_APPARMOR_STATUS, ad: &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), flags: gfp);
160 if (!str)
161 return NULL;
162
163 kref_init(kref: &str->count);
164 return str->name;
165}
166
167void aa_str_kref(struct kref *kref)
168{
169 kfree(container_of(kref, struct counted_str, count));
170}
171
172
173const char aa_file_perm_chrs[] = "xwracd km l ";
174const 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 */
223void 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
241void 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
259void 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, fmt: "\"");
265 if ((mask & chrsmask) && chrs) {
266 aa_perm_mask_to_str(str, str_size: sizeof(str), chrs, mask: mask & chrsmask);
267 mask &= ~chrsmask;
268 audit_log_format(ab, fmt: "%s", str);
269 if (mask & namesmask)
270 audit_log_format(ab, fmt: " ");
271 }
272 if ((mask & namesmask) && names)
273 aa_audit_perm_names(ab, names, mask: mask & namesmask);
274 audit_log_format(ab, fmt: "\"");
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 */
282static 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, fmt: " requested_mask=");
289 aa_audit_perm_mask(ab, mask: ad->request, chrs: aa_file_perm_chrs,
290 PERMS_CHRS_MASK, names: aa_file_perm_names,
291 PERMS_NAMES_MASK);
292 }
293 if (ad->denied) {
294 audit_log_format(ab, fmt: "denied_mask=");
295 aa_audit_perm_mask(ab, mask: ad->denied, chrs: aa_file_perm_chrs,
296 PERMS_CHRS_MASK, names: aa_file_perm_names,
297 PERMS_NAMES_MASK);
298 }
299 audit_log_format(ab, fmt: " peer=");
300 aa_label_xaudit(ab, labels_ns(ad->subj_label), 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 */
311void 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
336void 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(dfa: rules->policy->dfa,
345 state: rules->policy->start[AA_CLASS_LABEL],
346 c: type);
347 aa_label_match(profile, rules, label, state, subns: false, request, perms);
348}
349
350
351/* currently unused */
352int 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, label: &target->label, type, request,
364 perms: &perms);
365 aa_apply_modes_to_perms(profile, perms: &perms);
366 *deny |= request & perms.deny;
367 return aa_check_perms(profile, perms: &perms, request, ad, cb: 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 */
387int 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 */
446bool 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(buf: hname, fmt: "%s//%s", prefix, name);
456 } else {
457 hname = aa_str_alloc(strlen(name) + 1, gfp);
458 if (hname)
459 strcpy(p: hname, q: name);
460 }
461 if (!hname)
462 return false;
463 policy->hname = hname;
464 /* base.name is a substring of fqname */
465 policy->name = basename(hname: policy->hname);
466 INIT_LIST_HEAD(list: &policy->list);
467 INIT_LIST_HEAD(list: &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 */
476void 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(str: policy->hname);
483}
484

source code of linux/security/apparmor/lib.c