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