Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/security/tomoyo/domain.c
10814 views
1
/*
2
* security/tomoyo/domain.c
3
*
4
* Domain transition functions for TOMOYO.
5
*
6
* Copyright (C) 2005-2010 NTT DATA CORPORATION
7
*/
8
9
#include "common.h"
10
#include <linux/binfmts.h>
11
#include <linux/slab.h>
12
13
/* Variables definitions.*/
14
15
/* The initial domain. */
16
struct tomoyo_domain_info tomoyo_kernel_domain;
17
18
/**
19
* tomoyo_update_policy - Update an entry for exception policy.
20
*
21
* @new_entry: Pointer to "struct tomoyo_acl_info".
22
* @size: Size of @new_entry in bytes.
23
* @is_delete: True if it is a delete request.
24
* @list: Pointer to "struct list_head".
25
* @check_duplicate: Callback function to find duplicated entry.
26
*
27
* Returns 0 on success, negative value otherwise.
28
*
29
* Caller holds tomoyo_read_lock().
30
*/
31
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
32
bool is_delete, struct list_head *list,
33
bool (*check_duplicate) (const struct tomoyo_acl_head
34
*,
35
const struct tomoyo_acl_head
36
*))
37
{
38
int error = is_delete ? -ENOENT : -ENOMEM;
39
struct tomoyo_acl_head *entry;
40
41
if (mutex_lock_interruptible(&tomoyo_policy_lock))
42
return -ENOMEM;
43
list_for_each_entry_rcu(entry, list, list) {
44
if (!check_duplicate(entry, new_entry))
45
continue;
46
entry->is_deleted = is_delete;
47
error = 0;
48
break;
49
}
50
if (error && !is_delete) {
51
entry = tomoyo_commit_ok(new_entry, size);
52
if (entry) {
53
list_add_tail_rcu(&entry->list, list);
54
error = 0;
55
}
56
}
57
mutex_unlock(&tomoyo_policy_lock);
58
return error;
59
}
60
61
/**
62
* tomoyo_update_domain - Update an entry for domain policy.
63
*
64
* @new_entry: Pointer to "struct tomoyo_acl_info".
65
* @size: Size of @new_entry in bytes.
66
* @is_delete: True if it is a delete request.
67
* @domain: Pointer to "struct tomoyo_domain_info".
68
* @check_duplicate: Callback function to find duplicated entry.
69
* @merge_duplicate: Callback function to merge duplicated entry.
70
*
71
* Returns 0 on success, negative value otherwise.
72
*
73
* Caller holds tomoyo_read_lock().
74
*/
75
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
76
bool is_delete, struct tomoyo_domain_info *domain,
77
bool (*check_duplicate) (const struct tomoyo_acl_info
78
*,
79
const struct tomoyo_acl_info
80
*),
81
bool (*merge_duplicate) (struct tomoyo_acl_info *,
82
struct tomoyo_acl_info *,
83
const bool))
84
{
85
int error = is_delete ? -ENOENT : -ENOMEM;
86
struct tomoyo_acl_info *entry;
87
88
if (mutex_lock_interruptible(&tomoyo_policy_lock))
89
return error;
90
list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
91
if (!check_duplicate(entry, new_entry))
92
continue;
93
if (merge_duplicate)
94
entry->is_deleted = merge_duplicate(entry, new_entry,
95
is_delete);
96
else
97
entry->is_deleted = is_delete;
98
error = 0;
99
break;
100
}
101
if (error && !is_delete) {
102
entry = tomoyo_commit_ok(new_entry, size);
103
if (entry) {
104
list_add_tail_rcu(&entry->list, &domain->acl_info_list);
105
error = 0;
106
}
107
}
108
mutex_unlock(&tomoyo_policy_lock);
109
return error;
110
}
111
112
void tomoyo_check_acl(struct tomoyo_request_info *r,
113
bool (*check_entry) (struct tomoyo_request_info *,
114
const struct tomoyo_acl_info *))
115
{
116
const struct tomoyo_domain_info *domain = r->domain;
117
struct tomoyo_acl_info *ptr;
118
119
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
120
if (ptr->is_deleted || ptr->type != r->param_type)
121
continue;
122
if (check_entry(r, ptr)) {
123
r->granted = true;
124
return;
125
}
126
}
127
r->granted = false;
128
}
129
130
/* The list for "struct tomoyo_domain_info". */
131
LIST_HEAD(tomoyo_domain_list);
132
133
struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
134
struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
135
136
/**
137
* tomoyo_last_word - Get last component of a domainname.
138
*
139
* @domainname: Domainname to check.
140
*
141
* Returns the last word of @domainname.
142
*/
143
static const char *tomoyo_last_word(const char *name)
144
{
145
const char *cp = strrchr(name, ' ');
146
if (cp)
147
return cp + 1;
148
return name;
149
}
150
151
static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
152
const struct tomoyo_acl_head *b)
153
{
154
const struct tomoyo_transition_control *p1 = container_of(a,
155
typeof(*p1),
156
head);
157
const struct tomoyo_transition_control *p2 = container_of(b,
158
typeof(*p2),
159
head);
160
return p1->type == p2->type && p1->is_last_name == p2->is_last_name
161
&& p1->domainname == p2->domainname
162
&& p1->program == p2->program;
163
}
164
165
/**
166
* tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
167
*
168
* @domainname: The name of domain. Maybe NULL.
169
* @program: The name of program. Maybe NULL.
170
* @type: Type of transition.
171
* @is_delete: True if it is a delete request.
172
*
173
* Returns 0 on success, negative value otherwise.
174
*/
175
static int tomoyo_update_transition_control_entry(const char *domainname,
176
const char *program,
177
const u8 type,
178
const bool is_delete)
179
{
180
struct tomoyo_transition_control e = { .type = type };
181
int error = is_delete ? -ENOENT : -ENOMEM;
182
if (program) {
183
if (!tomoyo_correct_path(program))
184
return -EINVAL;
185
e.program = tomoyo_get_name(program);
186
if (!e.program)
187
goto out;
188
}
189
if (domainname) {
190
if (!tomoyo_correct_domain(domainname)) {
191
if (!tomoyo_correct_path(domainname))
192
goto out;
193
e.is_last_name = true;
194
}
195
e.domainname = tomoyo_get_name(domainname);
196
if (!e.domainname)
197
goto out;
198
}
199
error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
200
&tomoyo_policy_list
201
[TOMOYO_ID_TRANSITION_CONTROL],
202
tomoyo_same_transition_control);
203
out:
204
tomoyo_put_name(e.domainname);
205
tomoyo_put_name(e.program);
206
return error;
207
}
208
209
/**
210
* tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
211
*
212
* @data: String to parse.
213
* @is_delete: True if it is a delete request.
214
* @type: Type of this entry.
215
*
216
* Returns 0 on success, negative value otherwise.
217
*/
218
int tomoyo_write_transition_control(char *data, const bool is_delete,
219
const u8 type)
220
{
221
char *domainname = strstr(data, " from ");
222
if (domainname) {
223
*domainname = '\0';
224
domainname += 6;
225
} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
226
type == TOMOYO_TRANSITION_CONTROL_KEEP) {
227
domainname = data;
228
data = NULL;
229
}
230
return tomoyo_update_transition_control_entry(domainname, data, type,
231
is_delete);
232
}
233
234
/**
235
* tomoyo_transition_type - Get domain transition type.
236
*
237
* @domainname: The name of domain.
238
* @program: The name of program.
239
*
240
* Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
241
* reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
242
* @program suppresses domain transition, others otherwise.
243
*
244
* Caller holds tomoyo_read_lock().
245
*/
246
static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
247
const struct tomoyo_path_info *program)
248
{
249
const struct tomoyo_transition_control *ptr;
250
const char *last_name = tomoyo_last_word(domainname->name);
251
u8 type;
252
for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
253
next:
254
list_for_each_entry_rcu(ptr, &tomoyo_policy_list
255
[TOMOYO_ID_TRANSITION_CONTROL],
256
head.list) {
257
if (ptr->head.is_deleted || ptr->type != type)
258
continue;
259
if (ptr->domainname) {
260
if (!ptr->is_last_name) {
261
if (ptr->domainname != domainname)
262
continue;
263
} else {
264
/*
265
* Use direct strcmp() since this is
266
* unlikely used.
267
*/
268
if (strcmp(ptr->domainname->name,
269
last_name))
270
continue;
271
}
272
}
273
if (ptr->program &&
274
tomoyo_pathcmp(ptr->program, program))
275
continue;
276
if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
277
/*
278
* Do not check for initialize_domain if
279
* no_initialize_domain matched.
280
*/
281
type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
282
goto next;
283
}
284
goto done;
285
}
286
}
287
done:
288
return type;
289
}
290
291
static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
292
const struct tomoyo_acl_head *b)
293
{
294
const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
295
const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
296
return p1->original_name == p2->original_name &&
297
p1->aggregated_name == p2->aggregated_name;
298
}
299
300
/**
301
* tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
302
*
303
* @original_name: The original program's name.
304
* @aggregated_name: The program name to use.
305
* @is_delete: True if it is a delete request.
306
*
307
* Returns 0 on success, negative value otherwise.
308
*
309
* Caller holds tomoyo_read_lock().
310
*/
311
static int tomoyo_update_aggregator_entry(const char *original_name,
312
const char *aggregated_name,
313
const bool is_delete)
314
{
315
struct tomoyo_aggregator e = { };
316
int error = is_delete ? -ENOENT : -ENOMEM;
317
318
if (!tomoyo_correct_path(original_name) ||
319
!tomoyo_correct_path(aggregated_name))
320
return -EINVAL;
321
e.original_name = tomoyo_get_name(original_name);
322
e.aggregated_name = tomoyo_get_name(aggregated_name);
323
if (!e.original_name || !e.aggregated_name ||
324
e.aggregated_name->is_patterned) /* No patterns allowed. */
325
goto out;
326
error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
327
&tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
328
tomoyo_same_aggregator);
329
out:
330
tomoyo_put_name(e.original_name);
331
tomoyo_put_name(e.aggregated_name);
332
return error;
333
}
334
335
/**
336
* tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
337
*
338
* @data: String to parse.
339
* @is_delete: True if it is a delete request.
340
*
341
* Returns 0 on success, negative value otherwise.
342
*
343
* Caller holds tomoyo_read_lock().
344
*/
345
int tomoyo_write_aggregator(char *data, const bool is_delete)
346
{
347
char *cp = strchr(data, ' ');
348
349
if (!cp)
350
return -EINVAL;
351
*cp++ = '\0';
352
return tomoyo_update_aggregator_entry(data, cp, is_delete);
353
}
354
355
/**
356
* tomoyo_assign_domain - Create a domain.
357
*
358
* @domainname: The name of domain.
359
* @profile: Profile number to assign if the domain was newly created.
360
*
361
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
362
*
363
* Caller holds tomoyo_read_lock().
364
*/
365
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
366
const u8 profile)
367
{
368
struct tomoyo_domain_info *entry;
369
struct tomoyo_domain_info *domain = NULL;
370
const struct tomoyo_path_info *saved_domainname;
371
bool found = false;
372
373
if (!tomoyo_correct_domain(domainname))
374
return NULL;
375
saved_domainname = tomoyo_get_name(domainname);
376
if (!saved_domainname)
377
return NULL;
378
entry = kzalloc(sizeof(*entry), GFP_NOFS);
379
if (mutex_lock_interruptible(&tomoyo_policy_lock))
380
goto out;
381
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
382
if (domain->is_deleted ||
383
tomoyo_pathcmp(saved_domainname, domain->domainname))
384
continue;
385
found = true;
386
break;
387
}
388
if (!found && tomoyo_memory_ok(entry)) {
389
INIT_LIST_HEAD(&entry->acl_info_list);
390
entry->domainname = saved_domainname;
391
saved_domainname = NULL;
392
entry->profile = profile;
393
list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
394
domain = entry;
395
entry = NULL;
396
found = true;
397
}
398
mutex_unlock(&tomoyo_policy_lock);
399
out:
400
tomoyo_put_name(saved_domainname);
401
kfree(entry);
402
return found ? domain : NULL;
403
}
404
405
/**
406
* tomoyo_find_next_domain - Find a domain.
407
*
408
* @bprm: Pointer to "struct linux_binprm".
409
*
410
* Returns 0 on success, negative value otherwise.
411
*
412
* Caller holds tomoyo_read_lock().
413
*/
414
int tomoyo_find_next_domain(struct linux_binprm *bprm)
415
{
416
struct tomoyo_request_info r;
417
char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
418
struct tomoyo_domain_info *old_domain = tomoyo_domain();
419
struct tomoyo_domain_info *domain = NULL;
420
const char *original_name = bprm->filename;
421
u8 mode;
422
bool is_enforce;
423
int retval = -ENOMEM;
424
bool need_kfree = false;
425
struct tomoyo_path_info rn = { }; /* real name */
426
427
mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
428
is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
429
if (!tmp)
430
goto out;
431
432
retry:
433
if (need_kfree) {
434
kfree(rn.name);
435
need_kfree = false;
436
}
437
/* Get symlink's pathname of program. */
438
retval = -ENOENT;
439
rn.name = tomoyo_realpath_nofollow(original_name);
440
if (!rn.name)
441
goto out;
442
tomoyo_fill_path_info(&rn);
443
need_kfree = true;
444
445
/* Check 'aggregator' directive. */
446
{
447
struct tomoyo_aggregator *ptr;
448
list_for_each_entry_rcu(ptr, &tomoyo_policy_list
449
[TOMOYO_ID_AGGREGATOR], head.list) {
450
if (ptr->head.is_deleted ||
451
!tomoyo_path_matches_pattern(&rn,
452
ptr->original_name))
453
continue;
454
kfree(rn.name);
455
need_kfree = false;
456
/* This is OK because it is read only. */
457
rn = *ptr->aggregated_name;
458
break;
459
}
460
}
461
462
/* Check execute permission. */
463
retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
464
if (retval == TOMOYO_RETRY_REQUEST)
465
goto retry;
466
if (retval < 0)
467
goto out;
468
/*
469
* To be able to specify domainnames with wildcards, use the
470
* pathname specified in the policy (which may contain
471
* wildcard) rather than the pathname passed to execve()
472
* (which never contains wildcard).
473
*/
474
if (r.param.path.matched_path) {
475
if (need_kfree)
476
kfree(rn.name);
477
need_kfree = false;
478
/* This is OK because it is read only. */
479
rn = *r.param.path.matched_path;
480
}
481
482
/* Calculate domain to transit to. */
483
switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
484
case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
485
/* Transit to the child of tomoyo_kernel_domain domain. */
486
snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
487
"%s", rn.name);
488
break;
489
case TOMOYO_TRANSITION_CONTROL_KEEP:
490
/* Keep current domain. */
491
domain = old_domain;
492
break;
493
default:
494
if (old_domain == &tomoyo_kernel_domain &&
495
!tomoyo_policy_loaded) {
496
/*
497
* Needn't to transit from kernel domain before
498
* starting /sbin/init. But transit from kernel domain
499
* if executing initializers because they might start
500
* before /sbin/init.
501
*/
502
domain = old_domain;
503
} else {
504
/* Normal domain transition. */
505
snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
506
old_domain->domainname->name, rn.name);
507
}
508
break;
509
}
510
if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
511
goto done;
512
domain = tomoyo_find_domain(tmp);
513
if (domain)
514
goto done;
515
if (is_enforce) {
516
int error = tomoyo_supervisor(&r, "# wants to create domain\n"
517
"%s\n", tmp);
518
if (error == TOMOYO_RETRY_REQUEST)
519
goto retry;
520
if (error < 0)
521
goto done;
522
}
523
domain = tomoyo_assign_domain(tmp, old_domain->profile);
524
done:
525
if (domain)
526
goto out;
527
printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
528
if (is_enforce)
529
retval = -EPERM;
530
else
531
old_domain->transition_failed = true;
532
out:
533
if (!domain)
534
domain = old_domain;
535
/* Update reference count on "struct tomoyo_domain_info". */
536
atomic_inc(&domain->users);
537
bprm->cred->security = domain;
538
if (need_kfree)
539
kfree(rn.name);
540
kfree(tmp);
541
return retval;
542
}
543
544