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