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