Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/apparmor/lib.c
26377 views
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
struct val_table_ent {
29
const char *str;
30
int value;
31
};
32
33
static struct val_table_ent debug_values_table[] = {
34
{ "N", DEBUG_NONE },
35
{ "none", DEBUG_NONE },
36
{ "n", DEBUG_NONE },
37
{ "0", DEBUG_NONE },
38
{ "all", DEBUG_ALL },
39
{ "Y", DEBUG_ALL },
40
{ "y", DEBUG_ALL },
41
{ "1", DEBUG_ALL },
42
{ "abs_root", DEBUG_LABEL_ABS_ROOT },
43
{ "label", DEBUG_LABEL },
44
{ "domain", DEBUG_DOMAIN },
45
{ "policy", DEBUG_POLICY },
46
{ "interface", DEBUG_INTERFACE },
47
{ NULL, 0 }
48
};
49
50
static struct val_table_ent *val_table_find_ent(struct val_table_ent *table,
51
const char *name, size_t len)
52
{
53
struct val_table_ent *entry;
54
55
for (entry = table; entry->str != NULL; entry++) {
56
if (strncmp(entry->str, name, len) == 0 &&
57
strlen(entry->str) == len)
58
return entry;
59
}
60
return NULL;
61
}
62
63
int aa_parse_debug_params(const char *str)
64
{
65
struct val_table_ent *ent;
66
const char *next;
67
int val = 0;
68
69
do {
70
size_t n = strcspn(str, "\r\n,");
71
72
next = str + n;
73
ent = val_table_find_ent(debug_values_table, str, next - str);
74
if (ent)
75
val |= ent->value;
76
else
77
AA_DEBUG(DEBUG_INTERFACE, "unknown debug type '%.*s'",
78
(int)(next - str), str);
79
str = next + 1;
80
} while (*next != 0);
81
return val;
82
}
83
84
/**
85
* val_mask_to_str - convert a perm mask to its short string
86
* @str: character buffer to store string in (at least 10 characters)
87
* @size: size of the @str buffer
88
* @table: NUL-terminated character buffer of permission characters (NOT NULL)
89
* @mask: permission mask to convert
90
*/
91
static int val_mask_to_str(char *str, size_t size,
92
const struct val_table_ent *table, u32 mask)
93
{
94
const struct val_table_ent *ent;
95
int total = 0;
96
97
for (ent = table; ent->str; ent++) {
98
if (ent->value && (ent->value & mask) == ent->value) {
99
int len = scnprintf(str, size, "%s%s", total ? "," : "",
100
ent->str);
101
size -= len;
102
str += len;
103
total += len;
104
mask &= ~ent->value;
105
}
106
}
107
108
return total;
109
}
110
111
int aa_print_debug_params(char *buffer)
112
{
113
if (!aa_g_debug)
114
return sprintf(buffer, "N");
115
return val_mask_to_str(buffer, PAGE_SIZE, debug_values_table,
116
aa_g_debug);
117
}
118
119
bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp)
120
{
121
char **n;
122
int i;
123
124
if (t->size == newsize)
125
return true;
126
n = kcalloc(newsize, sizeof(*n), gfp);
127
if (!n)
128
return false;
129
for (i = 0; i < min(t->size, newsize); i++)
130
n[i] = t->table[i];
131
for (; i < t->size; i++)
132
kfree_sensitive(t->table[i]);
133
if (newsize > t->size)
134
memset(&n[t->size], 0, (newsize-t->size)*sizeof(*n));
135
kfree_sensitive(t->table);
136
t->table = n;
137
t->size = newsize;
138
139
return true;
140
}
141
142
/**
143
* aa_free_str_table - free entries str table
144
* @t: the string table to free (MAYBE NULL)
145
*/
146
void aa_free_str_table(struct aa_str_table *t)
147
{
148
int i;
149
150
if (t) {
151
if (!t->table)
152
return;
153
154
for (i = 0; i < t->size; i++)
155
kfree_sensitive(t->table[i]);
156
kfree_sensitive(t->table);
157
t->table = NULL;
158
t->size = 0;
159
}
160
}
161
162
/**
163
* skipn_spaces - Removes leading whitespace from @str.
164
* @str: The string to be stripped.
165
* @n: length of str to parse, will stop at \0 if encountered before n
166
*
167
* Returns a pointer to the first non-whitespace character in @str.
168
* if all whitespace will return NULL
169
*/
170
171
const char *skipn_spaces(const char *str, size_t n)
172
{
173
for (; n && isspace(*str); --n)
174
++str;
175
if (n)
176
return (char *)str;
177
return NULL;
178
}
179
180
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
181
size_t *ns_len)
182
{
183
const char *end = fqname + n;
184
const char *name = skipn_spaces(fqname, n);
185
186
*ns_name = NULL;
187
*ns_len = 0;
188
189
if (!name)
190
return NULL;
191
192
if (name[0] == ':') {
193
char *split = strnchr(&name[1], end - &name[1], ':');
194
*ns_name = skipn_spaces(&name[1], end - &name[1]);
195
if (!*ns_name)
196
return NULL;
197
if (split) {
198
*ns_len = split - *ns_name;
199
if (*ns_len == 0)
200
*ns_name = NULL;
201
split++;
202
if (end - split > 1 && strncmp(split, "//", 2) == 0)
203
split += 2;
204
name = skipn_spaces(split, end - split);
205
} else {
206
/* a ns name without a following profile is allowed */
207
name = NULL;
208
*ns_len = end - *ns_name;
209
}
210
}
211
if (name && *name == 0)
212
name = NULL;
213
214
return name;
215
}
216
217
/**
218
* aa_info_message - log a none profile related status message
219
* @str: message to log
220
*/
221
void aa_info_message(const char *str)
222
{
223
if (audit_enabled) {
224
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE, NULL);
225
226
ad.info = str;
227
aa_audit_msg(AUDIT_APPARMOR_STATUS, &ad, NULL);
228
}
229
printk(KERN_INFO "AppArmor: %s\n", str);
230
}
231
232
__counted char *aa_str_alloc(int size, gfp_t gfp)
233
{
234
struct counted_str *str;
235
236
str = kmalloc(struct_size(str, name, size), gfp);
237
if (!str)
238
return NULL;
239
240
kref_init(&str->count);
241
return str->name;
242
}
243
244
void aa_str_kref(struct kref *kref)
245
{
246
kfree(container_of(kref, struct counted_str, count));
247
}
248
249
250
const char aa_file_perm_chrs[] = "xwracd km l ";
251
const char *aa_file_perm_names[] = {
252
"exec",
253
"write",
254
"read",
255
"append",
256
257
"create",
258
"delete",
259
"open",
260
"rename",
261
262
"setattr",
263
"getattr",
264
"setcred",
265
"getcred",
266
267
"chmod",
268
"chown",
269
"chgrp",
270
"lock",
271
272
"mmap",
273
"mprot",
274
"link",
275
"snapshot",
276
277
"unknown",
278
"unknown",
279
"unknown",
280
"unknown",
281
282
"unknown",
283
"unknown",
284
"unknown",
285
"unknown",
286
287
"stack",
288
"change_onexec",
289
"change_profile",
290
"change_hat",
291
};
292
293
/**
294
* aa_perm_mask_to_str - convert a perm mask to its short string
295
* @str: character buffer to store string in (at least 10 characters)
296
* @str_size: size of the @str buffer
297
* @chrs: NUL-terminated character buffer of permission characters
298
* @mask: permission mask to convert
299
*/
300
void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, u32 mask)
301
{
302
unsigned int i, perm = 1;
303
size_t num_chrs = strlen(chrs);
304
305
for (i = 0; i < num_chrs; perm <<= 1, i++) {
306
if (mask & perm) {
307
/* Ensure that one byte is left for NUL-termination */
308
if (WARN_ON_ONCE(str_size <= 1))
309
break;
310
311
*str++ = chrs[i];
312
str_size--;
313
}
314
}
315
*str = '\0';
316
}
317
318
void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
319
u32 mask)
320
{
321
const char *fmt = "%s";
322
unsigned int i, perm = 1;
323
bool prev = false;
324
325
for (i = 0; i < 32; perm <<= 1, i++) {
326
if (mask & perm) {
327
audit_log_format(ab, fmt, names[i]);
328
if (!prev) {
329
prev = true;
330
fmt = " %s";
331
}
332
}
333
}
334
}
335
336
void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
337
u32 chrsmask, const char * const *names, u32 namesmask)
338
{
339
char str[33];
340
341
audit_log_format(ab, "\"");
342
if ((mask & chrsmask) && chrs) {
343
aa_perm_mask_to_str(str, sizeof(str), chrs, mask & chrsmask);
344
mask &= ~chrsmask;
345
audit_log_format(ab, "%s", str);
346
if (mask & namesmask)
347
audit_log_format(ab, " ");
348
}
349
if ((mask & namesmask) && names)
350
aa_audit_perm_names(ab, names, mask & namesmask);
351
audit_log_format(ab, "\"");
352
}
353
354
/**
355
* aa_apply_modes_to_perms - apply namespace and profile flags to perms
356
* @profile: that perms where computed from
357
* @perms: perms to apply mode modifiers to
358
*
359
* TODO: split into profile and ns based flags for when accumulating perms
360
*/
361
void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
362
{
363
switch (AUDIT_MODE(profile)) {
364
case AUDIT_ALL:
365
perms->audit = ALL_PERMS_MASK;
366
fallthrough;
367
case AUDIT_NOQUIET:
368
perms->quiet = 0;
369
break;
370
case AUDIT_QUIET:
371
perms->audit = 0;
372
fallthrough;
373
case AUDIT_QUIET_DENIED:
374
perms->quiet = ALL_PERMS_MASK;
375
break;
376
}
377
378
if (KILL_MODE(profile))
379
perms->kill = ALL_PERMS_MASK;
380
else if (COMPLAIN_MODE(profile))
381
perms->complain = ALL_PERMS_MASK;
382
else if (USER_MODE(profile))
383
perms->prompt = ALL_PERMS_MASK;
384
}
385
386
void aa_profile_match_label(struct aa_profile *profile,
387
struct aa_ruleset *rules,
388
struct aa_label *label,
389
int type, u32 request, struct aa_perms *perms)
390
{
391
/* TODO: doesn't yet handle extended types */
392
aa_state_t state;
393
394
state = aa_dfa_next(rules->policy->dfa,
395
rules->policy->start[AA_CLASS_LABEL],
396
type);
397
aa_label_match(profile, rules, label, state, false, request, perms);
398
}
399
400
401
/**
402
* aa_check_perms - do audit mode selection based on perms set
403
* @profile: profile being checked
404
* @perms: perms computed for the request
405
* @request: requested perms
406
* @ad: initialized audit structure (MAY BE NULL if not auditing)
407
* @cb: callback fn for type specific fields (MAY BE NULL)
408
*
409
* Returns: 0 if permission else error code
410
*
411
* Note: profile audit modes need to be set before calling by setting the
412
* perm masks appropriately.
413
*
414
* If not auditing then complain mode is not enabled and the
415
* error code will indicate whether there was an explicit deny
416
* with a positive value.
417
*/
418
int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
419
u32 request, struct apparmor_audit_data *ad,
420
void (*cb)(struct audit_buffer *, void *))
421
{
422
int type, error;
423
u32 denied = request & (~perms->allow | perms->deny);
424
425
if (likely(!denied)) {
426
/* mask off perms that are not being force audited */
427
request &= perms->audit;
428
if (!request || !ad)
429
return 0;
430
431
type = AUDIT_APPARMOR_AUDIT;
432
error = 0;
433
} else {
434
error = -EACCES;
435
436
if (denied & perms->kill)
437
type = AUDIT_APPARMOR_KILL;
438
else if (denied == (denied & perms->complain))
439
type = AUDIT_APPARMOR_ALLOWED;
440
else
441
type = AUDIT_APPARMOR_DENIED;
442
443
if (denied == (denied & perms->hide))
444
error = -ENOENT;
445
446
denied &= ~perms->quiet;
447
if (!ad || !denied)
448
return error;
449
}
450
451
if (ad) {
452
ad->subj_label = &profile->label;
453
ad->request = request;
454
ad->denied = denied;
455
ad->error = error;
456
aa_audit_msg(type, ad, cb);
457
}
458
459
if (type == AUDIT_APPARMOR_ALLOWED)
460
error = 0;
461
462
return error;
463
}
464
465
466
/**
467
* aa_policy_init - initialize a policy structure
468
* @policy: policy to initialize (NOT NULL)
469
* @prefix: prefix name if any is required. (MAYBE NULL)
470
* @name: name of the policy, init will make a copy of it (NOT NULL)
471
* @gfp: allocation mode
472
*
473
* Note: this fn creates a copy of strings passed in
474
*
475
* Returns: true if policy init successful
476
*/
477
bool aa_policy_init(struct aa_policy *policy, const char *prefix,
478
const char *name, gfp_t gfp)
479
{
480
char *hname;
481
482
/* freed by policy_free */
483
if (prefix) {
484
hname = aa_str_alloc(strlen(prefix) + strlen(name) + 3, gfp);
485
if (hname)
486
sprintf(hname, "%s//%s", prefix, name);
487
} else {
488
hname = aa_str_alloc(strlen(name) + 1, gfp);
489
if (hname)
490
strcpy(hname, name);
491
}
492
if (!hname)
493
return false;
494
policy->hname = hname;
495
/* base.name is a substring of fqname */
496
policy->name = basename(policy->hname);
497
INIT_LIST_HEAD(&policy->list);
498
INIT_LIST_HEAD(&policy->profiles);
499
500
return true;
501
}
502
503
/**
504
* aa_policy_destroy - free the elements referenced by @policy
505
* @policy: policy that is to have its elements freed (NOT NULL)
506
*/
507
void aa_policy_destroy(struct aa_policy *policy)
508
{
509
AA_BUG(on_list_rcu(&policy->profiles));
510
AA_BUG(on_list_rcu(&policy->list));
511
512
/* don't free name as its a subset of hname */
513
aa_put_str(policy->hname);
514
}
515
516