blob: 8818621b5d956023d9fc799fbff00a0b073fabc8 [file] [log] [blame]
John Johansencdff2642010-07-29 14:47:57 -07001/*
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
John Johansen3b0aaf52017-01-16 00:42:23 -080015#include <linux/ctype.h>
Alexey Dobriyanb7f080c2011-06-16 11:01:34 +000016#include <linux/mm.h>
John Johansencdff2642010-07-29 14:47:57 -070017#include <linux/slab.h>
18#include <linux/string.h>
19#include <linux/vmalloc.h>
20
21#include "include/audit.h"
James Morris32c3df62011-08-29 11:15:25 +100022#include "include/apparmor.h"
John Johansen12557dc2017-01-16 00:42:13 -080023#include "include/lib.h"
John Johansenfc7e0b22017-05-26 01:57:09 -070024#include "include/perms.h"
John Johansenfe6bb312017-01-16 00:42:14 -080025#include "include/policy.h"
John Johansencdff2642010-07-29 14:47:57 -070026
John Johansen2d679f32017-05-29 12:19:39 -070027struct aa_perms nullperms;
John Johansenaa9aeea2017-05-29 12:16:04 -070028struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
29 .quiet = ALL_PERMS_MASK,
30 .hide = ALL_PERMS_MASK };
31
John Johansencdff2642010-07-29 14:47:57 -070032/**
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 */
46char *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], ':');
John Johansen04ccd532010-08-27 18:33:28 -070053 *ns_name = skip_spaces(&name[1]);
John Johansencdff2642010-07-29 14:47:57 -070054 if (split) {
55 /* overwrite ':' with \0 */
John Johansen2654bfb2013-02-27 03:45:05 -080056 *split++ = 0;
57 if (strncmp(split, "//", 2) == 0)
58 split += 2;
59 name = skip_spaces(split);
John Johansencdff2642010-07-29 14:47:57 -070060 } else
61 /* a ns name without a following profile is allowed */
62 name = NULL;
John Johansencdff2642010-07-29 14:47:57 -070063 }
64 if (name && *name == 0)
65 name = NULL;
66
67 return name;
68}
69
70/**
John Johansen3b0aaf52017-01-16 00:42:23 -080071 * 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
John Johansenb91deb92017-05-22 02:47:22 -070078const char *skipn_spaces(const char *str, size_t n)
John Johansen3b0aaf52017-01-16 00:42:23 -080079{
80 for (; n && isspace(*str); --n)
81 ++str;
82 if (n)
83 return (char *)str;
84 return NULL;
85}
86
87const 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 if (!name)
94 return NULL;
95 *ns_name = NULL;
96 *ns_len = 0;
97 if (name[0] == ':') {
98 char *split = strnchr(&name[1], end - &name[1], ':');
99 *ns_name = skipn_spaces(&name[1], end - &name[1]);
100 if (!*ns_name)
101 return NULL;
102 if (split) {
103 *ns_len = split - *ns_name;
104 if (*ns_len == 0)
105 *ns_name = NULL;
106 split++;
107 if (end - split > 1 && strncmp(split, "//", 2) == 0)
108 split += 2;
109 name = skipn_spaces(split, end - split);
110 } else {
111 /* a ns name without a following profile is allowed */
112 name = NULL;
113 *ns_len = end - *ns_name;
114 }
115 }
116 if (name && *name == 0)
117 name = NULL;
118
119 return name;
120}
121
122/**
John Johansencdff2642010-07-29 14:47:57 -0700123 * aa_info_message - log a none profile related status message
124 * @str: message to log
125 */
126void aa_info_message(const char *str)
127{
128 if (audit_enabled) {
John Johansenef88a7a2017-01-16 00:43:02 -0800129 DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
130
131 aad(&sa)->info = str;
John Johansencdff2642010-07-29 14:47:57 -0700132 aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
133 }
134 printk(KERN_INFO "AppArmor: %s\n", str);
135}
136
John Johansena1bd6272017-06-09 07:09:05 -0700137__counted char *aa_str_alloc(int size, gfp_t gfp)
138{
139 struct counted_str *str;
140
141 str = kmalloc(sizeof(struct counted_str) + size, gfp);
142 if (!str)
143 return NULL;
144
145 kref_init(&str->count);
146 return str->name;
147}
148
149void aa_str_kref(struct kref *kref)
150{
151 kfree(container_of(kref, struct counted_str, count));
152}
153
154
John Johansene53cfe62017-05-26 15:07:22 -0700155const char aa_file_perm_chrs[] = "xwracd km l ";
156const char *aa_file_perm_names[] = {
157 "exec",
158 "write",
159 "read",
160 "append",
161
162 "create",
163 "delete",
164 "open",
165 "rename",
166
167 "setattr",
168 "getattr",
169 "setcred",
170 "getcred",
171
172 "chmod",
173 "chown",
174 "chgrp",
175 "lock",
176
177 "mmap",
178 "mprot",
179 "link",
180 "snapshot",
181
182 "unknown",
183 "unknown",
184 "unknown",
185 "unknown",
186
187 "unknown",
188 "unknown",
189 "unknown",
190 "unknown",
191
192 "stack",
193 "change_onexec",
194 "change_profile",
195 "change_hat",
196};
197
198/**
199 * aa_perm_mask_to_str - convert a perm mask to its short string
200 * @str: character buffer to store string in (at least 10 characters)
201 * @mask: permission mask to convert
202 */
203void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask)
204{
205 unsigned int i, perm = 1;
206
207 for (i = 0; i < 32; perm <<= 1, i++) {
208 if (mask & perm)
209 *str++ = chrs[i];
210 }
211 *str = '\0';
212}
213
John Johansen651e28c2017-07-18 23:18:33 -0700214void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
215 u32 mask)
John Johansenaa9aeea2017-05-29 12:16:04 -0700216{
217 const char *fmt = "%s";
218 unsigned int i, perm = 1;
219 bool prev = false;
220
221 for (i = 0; i < 32; perm <<= 1, i++) {
222 if (mask & perm) {
223 audit_log_format(ab, fmt, names[i]);
224 if (!prev) {
225 prev = true;
226 fmt = " %s";
227 }
228 }
229 }
230}
231
232void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
John Johansen651e28c2017-07-18 23:18:33 -0700233 u32 chrsmask, const char * const *names, u32 namesmask)
John Johansenaa9aeea2017-05-29 12:16:04 -0700234{
235 char str[33];
236
237 audit_log_format(ab, "\"");
238 if ((mask & chrsmask) && chrs) {
239 aa_perm_mask_to_str(str, chrs, mask & chrsmask);
240 mask &= ~chrsmask;
241 audit_log_format(ab, "%s", str);
242 if (mask & namesmask)
243 audit_log_format(ab, " ");
244 }
245 if ((mask & namesmask) && names)
246 aa_audit_perm_names(ab, names, mask & namesmask);
247 audit_log_format(ab, "\"");
248}
249
250/**
John Johansen637f6882017-06-09 08:14:28 -0700251 * aa_audit_perms_cb - generic callback fn for auditing perms
252 * @ab: audit buffer (NOT NULL)
253 * @va: audit struct to audit values of (NOT NULL)
254 */
255static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
256{
257 struct common_audit_data *sa = va;
258
259 if (aad(sa)->request) {
260 audit_log_format(ab, " requested_mask=");
261 aa_audit_perm_mask(ab, aad(sa)->request, aa_file_perm_chrs,
262 PERMS_CHRS_MASK, aa_file_perm_names,
263 PERMS_NAMES_MASK);
264 }
265 if (aad(sa)->denied) {
266 audit_log_format(ab, "denied_mask=");
267 aa_audit_perm_mask(ab, aad(sa)->denied, aa_file_perm_chrs,
268 PERMS_CHRS_MASK, aa_file_perm_names,
269 PERMS_NAMES_MASK);
270 }
271 audit_log_format(ab, " peer=");
272 aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
273 FLAGS_NONE, GFP_ATOMIC);
274}
275
276/**
John Johansenaa9aeea2017-05-29 12:16:04 -0700277 * aa_apply_modes_to_perms - apply namespace and profile flags to perms
278 * @profile: that perms where computed from
279 * @perms: perms to apply mode modifiers to
280 *
281 * TODO: split into profile and ns based flags for when accumulating perms
282 */
283void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
284{
285 switch (AUDIT_MODE(profile)) {
286 case AUDIT_ALL:
287 perms->audit = ALL_PERMS_MASK;
288 /* fall through */
289 case AUDIT_NOQUIET:
290 perms->quiet = 0;
291 break;
292 case AUDIT_QUIET:
293 perms->audit = 0;
294 /* fall through */
295 case AUDIT_QUIET_DENIED:
296 perms->quiet = ALL_PERMS_MASK;
297 break;
298 }
299
300 if (KILL_MODE(profile))
301 perms->kill = ALL_PERMS_MASK;
302 else if (COMPLAIN_MODE(profile))
303 perms->complain = ALL_PERMS_MASK;
304/*
305 * TODO:
306 * else if (PROMPT_MODE(profile))
307 * perms->prompt = ALL_PERMS_MASK;
308 */
309}
310
311static u32 map_other(u32 x)
312{
313 return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
314 ((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
315 ((x & 0x60) << 19); /* SETOPT/GETOPT */
316}
317
318void aa_compute_perms(struct aa_dfa *dfa, unsigned int state,
319 struct aa_perms *perms)
320{
321 perms->deny = 0;
322 perms->kill = perms->stop = 0;
323 perms->complain = perms->cond = 0;
324 perms->hide = 0;
325 perms->prompt = 0;
326 perms->allow = dfa_user_allow(dfa, state);
327 perms->audit = dfa_user_audit(dfa, state);
328 perms->quiet = dfa_user_quiet(dfa, state);
329
330 /* for v5 perm mapping in the policydb, the other set is used
331 * to extend the general perm set
332 */
333 perms->allow |= map_other(dfa_other_allow(dfa, state));
334 perms->audit |= map_other(dfa_other_audit(dfa, state));
335 perms->quiet |= map_other(dfa_other_quiet(dfa, state));
336// perms->xindex = dfa_user_xindex(dfa, state);
337}
338
John Johansencdff2642010-07-29 14:47:57 -0700339/**
John Johansen637f6882017-06-09 08:14:28 -0700340 * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
341 * @accum - perms struct to accumulate into
342 * @addend - perms struct to add to @accum
343 */
344void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend)
345{
346 accum->deny |= addend->deny;
347 accum->allow &= addend->allow & ~addend->deny;
348 accum->audit |= addend->audit & addend->allow;
349 accum->quiet &= addend->quiet & ~addend->allow;
350 accum->kill |= addend->kill & ~addend->allow;
351 accum->stop |= addend->stop & ~addend->allow;
352 accum->complain |= addend->complain & ~addend->allow & ~addend->deny;
353 accum->cond |= addend->cond & ~addend->allow & ~addend->deny;
354 accum->hide &= addend->hide & ~addend->allow;
355 accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny;
356}
357
358/**
359 * aa_perms_accum - accumulate perms, masking off overlapping perms
360 * @accum - perms struct to accumulate into
361 * @addend - perms struct to add to @accum
362 */
363void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend)
364{
365 accum->deny |= addend->deny;
366 accum->allow &= addend->allow & ~accum->deny;
367 accum->audit |= addend->audit & accum->allow;
368 accum->quiet &= addend->quiet & ~accum->allow;
369 accum->kill |= addend->kill & ~accum->allow;
370 accum->stop |= addend->stop & ~accum->allow;
371 accum->complain |= addend->complain & ~accum->allow & ~accum->deny;
372 accum->cond |= addend->cond & ~accum->allow & ~accum->deny;
373 accum->hide &= addend->hide & ~accum->allow;
374 accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny;
375}
376
377void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label,
378 int type, u32 request, struct aa_perms *perms)
379{
380 /* TODO: doesn't yet handle extended types */
381 unsigned int state;
382
383 state = aa_dfa_next(profile->policy.dfa,
384 profile->policy.start[AA_CLASS_LABEL],
385 type);
386 aa_label_match(profile, label, state, false, request, perms);
387}
388
389
390/* currently unused */
391int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
392 u32 request, int type, u32 *deny,
393 struct common_audit_data *sa)
394{
395 struct aa_perms perms;
396
397 aad(sa)->label = &profile->label;
398 aad(sa)->peer = &target->label;
399 aad(sa)->request = request;
400
401 aa_profile_match_label(profile, &target->label, type, request, &perms);
402 aa_apply_modes_to_perms(profile, &perms);
403 *deny |= request & perms.deny;
404 return aa_check_perms(profile, &perms, request, sa, aa_audit_perms_cb);
405}
406
407/**
408 * aa_check_perms - do audit mode selection based on perms set
409 * @profile: profile being checked
410 * @perms: perms computed for the request
411 * @request: requested perms
412 * @deny: Returns: explicit deny set
413 * @sa: initialized audit structure (MAY BE NULL if not auditing)
414 * @cb: callback fn for tpye specific fields (MAY BE NULL)
415 *
416 * Returns: 0 if permission else error code
417 *
418 * Note: profile audit modes need to be set before calling by setting the
419 * perm masks appropriately.
420 *
421 * If not auditing then complain mode is not enabled and the
422 * error code will indicate whether there was an explicit deny
423 * with a positive value.
424 */
425int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
426 u32 request, struct common_audit_data *sa,
427 void (*cb)(struct audit_buffer *, void *))
428{
429 int type, error;
430 bool stop = false;
431 u32 denied = request & (~perms->allow | perms->deny);
432
433 if (likely(!denied)) {
434 /* mask off perms that are not being force audited */
435 request &= perms->audit;
436 if (!request || !sa)
437 return 0;
438
439 type = AUDIT_APPARMOR_AUDIT;
440 error = 0;
441 } else {
442 error = -EACCES;
443
444 if (denied & perms->kill)
445 type = AUDIT_APPARMOR_KILL;
446 else if (denied == (denied & perms->complain))
447 type = AUDIT_APPARMOR_ALLOWED;
448 else
449 type = AUDIT_APPARMOR_DENIED;
450
451 if (denied & perms->stop)
452 stop = true;
453 if (denied == (denied & perms->hide))
454 error = -ENOENT;
455
456 denied &= ~perms->quiet;
457 if (!sa || !denied)
458 return error;
459 }
460
461 if (sa) {
462 aad(sa)->label = &profile->label;
463 aad(sa)->request = request;
464 aad(sa)->denied = denied;
465 aad(sa)->error = error;
466 aa_audit_msg(type, sa, cb);
467 }
468
469 if (type == AUDIT_APPARMOR_ALLOWED)
470 error = 0;
471
472 return error;
473}
474
475
476/**
John Johansenfe6bb312017-01-16 00:42:14 -0800477 * aa_policy_init - initialize a policy structure
478 * @policy: policy to initialize (NOT NULL)
479 * @prefix: prefix name if any is required. (MAYBE NULL)
480 * @name: name of the policy, init will make a copy of it (NOT NULL)
John Johansena1bd6272017-06-09 07:09:05 -0700481 * @gfp: allocation mode
John Johansenfe6bb312017-01-16 00:42:14 -0800482 *
483 * Note: this fn creates a copy of strings passed in
484 *
485 * Returns: true if policy init successful
486 */
487bool aa_policy_init(struct aa_policy *policy, const char *prefix,
John Johansend102d892017-01-16 00:42:31 -0800488 const char *name, gfp_t gfp)
John Johansenfe6bb312017-01-16 00:42:14 -0800489{
John Johansena1bd6272017-06-09 07:09:05 -0700490 char *hname;
491
John Johansenfe6bb312017-01-16 00:42:14 -0800492 /* freed by policy_free */
493 if (prefix) {
John Johansena1bd6272017-06-09 07:09:05 -0700494 hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
495 if (hname)
496 sprintf(hname, "%s//%s", prefix, name);
497 } else {
498 hname = aa_str_alloc(strlen(name) + 1, gfp);
499 if (hname)
500 strcpy(hname, name);
501 }
502 if (!hname)
kbuild test robotb9c42ac2017-04-06 06:55:19 -0700503 return false;
John Johansena1bd6272017-06-09 07:09:05 -0700504 policy->hname = hname;
John Johansenfe6bb312017-01-16 00:42:14 -0800505 /* base.name is a substring of fqname */
John Johansend102d892017-01-16 00:42:31 -0800506 policy->name = basename(policy->hname);
John Johansenfe6bb312017-01-16 00:42:14 -0800507 INIT_LIST_HEAD(&policy->list);
508 INIT_LIST_HEAD(&policy->profiles);
509
kbuild test robotb9c42ac2017-04-06 06:55:19 -0700510 return true;
John Johansenfe6bb312017-01-16 00:42:14 -0800511}
512
513/**
514 * aa_policy_destroy - free the elements referenced by @policy
515 * @policy: policy that is to have its elements freed (NOT NULL)
516 */
517void aa_policy_destroy(struct aa_policy *policy)
518{
John Johansen5fd1b952017-01-16 00:42:32 -0800519 AA_BUG(on_list_rcu(&policy->profiles));
520 AA_BUG(on_list_rcu(&policy->list));
John Johansenfe6bb312017-01-16 00:42:14 -0800521
522 /* don't free name as its a subset of hname */
John Johansena1bd6272017-06-09 07:09:05 -0700523 aa_put_str(policy->hname);
John Johansenfe6bb312017-01-16 00:42:14 -0800524}