domain.c 14.8 KB
Newer Older
K
Kentaro Takeda 已提交
1 2 3
/*
 * security/tomoyo/domain.c
 *
4
 * Domain transition functions for TOMOYO.
K
Kentaro Takeda 已提交
5
 *
6
 * Copyright (C) 2005-2010  NTT DATA CORPORATION
K
Kentaro Takeda 已提交
7 8 9 10
 */

#include "common.h"
#include <linux/binfmts.h>
11
#include <linux/slab.h>
K
Kentaro Takeda 已提交
12 13 14

/* Variables definitions.*/

T
Tetsuo Handa 已提交
15 16 17
/* The global ACL referred by "use_group" keyword. */
struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS];

K
Kentaro Takeda 已提交
18 19 20
/* The initial domain. */
struct tomoyo_domain_info tomoyo_kernel_domain;

21 22 23 24 25
/**
 * tomoyo_update_policy - Update an entry for exception policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
26
 * @param:           Pointer to "struct tomoyo_acl_param".
27 28 29 30 31 32 33
 * @check_duplicate: Callback function to find duplicated entry.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
34
			 struct tomoyo_acl_param *param,
35 36 37 38 39
			 bool (*check_duplicate) (const struct tomoyo_acl_head
						  *,
						  const struct tomoyo_acl_head
						  *))
{
40
	int error = param->is_delete ? -ENOENT : -ENOMEM;
41
	struct tomoyo_acl_head *entry;
42
	struct list_head *list = param->list;
43 44 45 46 47 48

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return -ENOMEM;
	list_for_each_entry_rcu(entry, list, list) {
		if (!check_duplicate(entry, new_entry))
			continue;
49
		entry->is_deleted = param->is_delete;
50 51 52
		error = 0;
		break;
	}
53
	if (error && !param->is_delete) {
54 55 56 57 58 59 60 61 62 63
		entry = tomoyo_commit_ok(new_entry, size);
		if (entry) {
			list_add_tail_rcu(&entry->list, list);
			error = 0;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
	return error;
}

T
Tetsuo Handa 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77
/**
 * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry.
 *
 * @a: Pointer to "struct tomoyo_acl_info".
 * @b: Pointer to "struct tomoyo_acl_info".
 *
 * Returns true if @a == @b, false otherwise.
 */
static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
					const struct tomoyo_acl_info *b)
{
	return a->type == b->type;
}

78 79 80 81 82
/**
 * tomoyo_update_domain - Update an entry for domain policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
83
 * @param:           Pointer to "struct tomoyo_acl_param".
84 85 86 87 88 89 90 91
 * @check_duplicate: Callback function to find duplicated entry.
 * @merge_duplicate: Callback function to merge duplicated entry.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
92
			 struct tomoyo_acl_param *param,
93 94 95 96 97 98 99 100
			 bool (*check_duplicate) (const struct tomoyo_acl_info
						  *,
						  const struct tomoyo_acl_info
						  *),
			 bool (*merge_duplicate) (struct tomoyo_acl_info *,
						  struct tomoyo_acl_info *,
						  const bool))
{
101
	const bool is_delete = param->is_delete;
102 103
	int error = is_delete ? -ENOENT : -ENOMEM;
	struct tomoyo_acl_info *entry;
104
	struct list_head * const list = param->list;
105 106 107

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return error;
108
	list_for_each_entry_rcu(entry, list, list) {
T
Tetsuo Handa 已提交
109 110
		if (!tomoyo_same_acl_head(entry, new_entry) ||
		    !check_duplicate(entry, new_entry))
111 112 113 114 115 116 117 118 119 120 121 122
			continue;
		if (merge_duplicate)
			entry->is_deleted = merge_duplicate(entry, new_entry,
							    is_delete);
		else
			entry->is_deleted = is_delete;
		error = 0;
		break;
	}
	if (error && !is_delete) {
		entry = tomoyo_commit_ok(new_entry, size);
		if (entry) {
123
			list_add_tail_rcu(&entry->list, list);
124 125 126 127 128 129 130
			error = 0;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
	return error;
}

T
Tetsuo Handa 已提交
131 132 133 134 135 136 137 138 139 140
/**
 * tomoyo_check_acl - Do permission check.
 *
 * @r:           Pointer to "struct tomoyo_request_info".
 * @check_entry: Callback function to check type specific parameters.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
141
void tomoyo_check_acl(struct tomoyo_request_info *r,
142
		      bool (*check_entry) (struct tomoyo_request_info *,
143 144 145 146
					   const struct tomoyo_acl_info *))
{
	const struct tomoyo_domain_info *domain = r->domain;
	struct tomoyo_acl_info *ptr;
T
Tetsuo Handa 已提交
147 148
	bool retried = false;
	const struct list_head *list = &domain->acl_info_list;
149

T
Tetsuo Handa 已提交
150 151
retry:
	list_for_each_entry_rcu(ptr, list, list) {
152 153 154 155 156 157 158
		if (ptr->is_deleted || ptr->type != r->param_type)
			continue;
		if (check_entry(r, ptr)) {
			r->granted = true;
			return;
		}
	}
T
Tetsuo Handa 已提交
159 160 161 162 163
	if (!retried) {
		retried = true;
		list = &tomoyo_acl_group[domain->group];
		goto retry;
	}
164 165 166
	r->granted = false;
}

167
/* The list for "struct tomoyo_domain_info". */
K
Kentaro Takeda 已提交
168 169
LIST_HEAD(tomoyo_domain_list);

170 171 172
struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];

K
Kentaro Takeda 已提交
173
/**
T
Tetsuo Handa 已提交
174
 * tomoyo_last_word - Get last component of a domainname.
K
Kentaro Takeda 已提交
175
 *
T
Tetsuo Handa 已提交
176
 * @domainname: Domainname to check.
K
Kentaro Takeda 已提交
177
 *
T
Tetsuo Handa 已提交
178
 * Returns the last word of @domainname.
K
Kentaro Takeda 已提交
179
 */
T
Tetsuo Handa 已提交
180
static const char *tomoyo_last_word(const char *name)
K
Kentaro Takeda 已提交
181
{
T
Tetsuo Handa 已提交
182 183 184 185
        const char *cp = strrchr(name, ' ');
        if (cp)
                return cp + 1;
        return name;
K
Kentaro Takeda 已提交
186 187
}

188 189 190 191 192 193 194 195
/**
 * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry.
 *
 * @a: Pointer to "struct tomoyo_acl_head".
 * @b: Pointer to "struct tomoyo_acl_head".
 *
 * Returns true if @a == @b, false otherwise.
 */
T
Tetsuo Handa 已提交
196 197
static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
					   const struct tomoyo_acl_head *b)
198
{
199 200 201 202 203 204 205
	const struct tomoyo_transition_control *p1 = container_of(a,
								  typeof(*p1),
								  head);
	const struct tomoyo_transition_control *p2 = container_of(b,
								  typeof(*p2),
								  head);
	return p1->type == p2->type && p1->is_last_name == p2->is_last_name
206 207 208 209
		&& p1->domainname == p2->domainname
		&& p1->program == p2->program;
}

K
Kentaro Takeda 已提交
210
/**
211
 * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
K
Kentaro Takeda 已提交
212
 *
213 214
 * @param: Pointer to "struct tomoyo_acl_param".
 * @type:  Type of this entry.
K
Kentaro Takeda 已提交
215 216 217
 *
 * Returns 0 on success, negative value otherwise.
 */
218 219
int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
				    const u8 type)
K
Kentaro Takeda 已提交
220
{
221
	struct tomoyo_transition_control e = { .type = type };
222 223 224 225 226 227 228 229 230 231 232
	int error = param->is_delete ? -ENOENT : -ENOMEM;
	char *program = param->data;
	char *domainname = strstr(program, " from ");
	if (domainname) {
		*domainname = '\0';
		domainname += 6;
	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
		   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
		domainname = program;
		program = NULL;
	}
T
Tetsuo Handa 已提交
233
	if (program && strcmp(program, "any")) {
234 235 236 237 238 239
		if (!tomoyo_correct_path(program))
			return -EINVAL;
		e.program = tomoyo_get_name(program);
		if (!e.program)
			goto out;
	}
T
Tetsuo Handa 已提交
240
	if (domainname && strcmp(domainname, "any")) {
241 242 243
		if (!tomoyo_correct_domain(domainname)) {
			if (!tomoyo_correct_path(domainname))
				goto out;
244
			e.is_last_name = true;
245
		}
246 247
		e.domainname = tomoyo_get_name(domainname);
		if (!e.domainname)
248
			goto out;
K
Kentaro Takeda 已提交
249
	}
250 251
	param->list = &tomoyo_policy_list[TOMOYO_ID_TRANSITION_CONTROL];
	error = tomoyo_update_policy(&e.head, sizeof(e), param,
T
Tetsuo Handa 已提交
252
				     tomoyo_same_transition_control);
253
out:
254 255
	tomoyo_put_name(e.domainname);
	tomoyo_put_name(e.program);
K
Kentaro Takeda 已提交
256 257 258 259
	return error;
}

/**
260
 * tomoyo_transition_type - Get domain transition type.
K
Kentaro Takeda 已提交
261 262 263 264
 *
 * @domainname: The name of domain.
 * @program:    The name of program.
 *
265 266 267
 * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
 * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
 * @program suppresses domain transition, others otherwise.
268 269
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
270
 */
271 272
static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
				 const struct tomoyo_path_info *program)
K
Kentaro Takeda 已提交
273
{
274 275 276 277 278 279 280 281 282
	const struct tomoyo_transition_control *ptr;
	const char *last_name = tomoyo_last_word(domainname->name);
	u8 type;
	for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
 next:
		list_for_each_entry_rcu(ptr, &tomoyo_policy_list
					[TOMOYO_ID_TRANSITION_CONTROL],
					head.list) {
			if (ptr->head.is_deleted || ptr->type != type)
K
Kentaro Takeda 已提交
283
				continue;
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
			if (ptr->domainname) {
				if (!ptr->is_last_name) {
					if (ptr->domainname != domainname)
						continue;
				} else {
					/*
					 * Use direct strcmp() since this is
					 * unlikely used.
					 */
					if (strcmp(ptr->domainname->name,
						   last_name))
						continue;
				}
			}
			if (ptr->program &&
			    tomoyo_pathcmp(ptr->program, program))
K
Kentaro Takeda 已提交
300
				continue;
301 302 303 304 305 306 307 308 309
			if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
				/*
				 * Do not check for initialize_domain if
				 * no_initialize_domain matched.
				 */
				type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
				goto next;
			}
			goto done;
K
Kentaro Takeda 已提交
310 311
		}
	}
312 313
 done:
	return type;
K
Kentaro Takeda 已提交
314 315
}

316 317 318 319 320 321 322 323
/**
 * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry.
 *
 * @a: Pointer to "struct tomoyo_acl_head".
 * @b: Pointer to "struct tomoyo_acl_head".
 *
 * Returns true if @a == @b, false otherwise.
 */
T
Tetsuo Handa 已提交
324 325
static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
				   const struct tomoyo_acl_head *b)
326
{
327 328 329 330
	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
							  head);
	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
							  head);
331 332 333 334
	return p1->original_name == p2->original_name &&
		p1->aggregated_name == p2->aggregated_name;
}

335
/**
336
 * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
337
 *
338
 * @param: Pointer to "struct tomoyo_acl_param".
339 340 341 342 343
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
344
int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
345
{
T
Tetsuo Handa 已提交
346
	struct tomoyo_aggregator e = { };
347 348 349 350
	int error = param->is_delete ? -ENOENT : -ENOMEM;
	const char *original_name = tomoyo_read_token(param);
	const char *aggregated_name = tomoyo_read_token(param);
	if (!tomoyo_correct_word(original_name) ||
T
Tetsuo Handa 已提交
351
	    !tomoyo_correct_path(aggregated_name))
352 353 354 355 356 357
		return -EINVAL;
	e.original_name = tomoyo_get_name(original_name);
	e.aggregated_name = tomoyo_get_name(aggregated_name);
	if (!e.original_name || !e.aggregated_name ||
	    e.aggregated_name->is_patterned) /* No patterns allowed. */
		goto out;
358 359
	param->list = &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR];
	error = tomoyo_update_policy(&e.head, sizeof(e), param,
T
Tetsuo Handa 已提交
360
				     tomoyo_same_aggregator);
361
out:
362 363 364 365 366
	tomoyo_put_name(e.original_name);
	tomoyo_put_name(e.aggregated_name);
	return error;
}

K
Kentaro Takeda 已提交
367
/**
T
Tetsuo Handa 已提交
368
 * tomoyo_assign_domain - Create a domain.
K
Kentaro Takeda 已提交
369 370 371 372 373
 *
 * @domainname: The name of domain.
 * @profile:    Profile number to assign if the domain was newly created.
 *
 * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
374 375
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
376
 */
T
Tetsuo Handa 已提交
377 378
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
						const u8 profile)
K
Kentaro Takeda 已提交
379
{
380
	struct tomoyo_domain_info *entry;
381
	struct tomoyo_domain_info *domain = NULL;
K
Kentaro Takeda 已提交
382
	const struct tomoyo_path_info *saved_domainname;
383
	bool found = false;
K
Kentaro Takeda 已提交
384

T
Tetsuo Handa 已提交
385
	if (!tomoyo_correct_domain(domainname))
386
		return NULL;
387
	saved_domainname = tomoyo_get_name(domainname);
K
Kentaro Takeda 已提交
388
	if (!saved_domainname)
389
		return NULL;
390
	entry = kzalloc(sizeof(*entry), GFP_NOFS);
391 392
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
393 394 395 396 397 398 399 400 401 402
	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
		if (domain->is_deleted ||
		    tomoyo_pathcmp(saved_domainname, domain->domainname))
			continue;
		found = true;
		break;
	}
	if (!found && tomoyo_memory_ok(entry)) {
		INIT_LIST_HEAD(&entry->acl_info_list);
		entry->domainname = saved_domainname;
403
		saved_domainname = NULL;
404 405 406 407 408
		entry->profile = profile;
		list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
		domain = entry;
		entry = NULL;
		found = true;
K
Kentaro Takeda 已提交
409
	}
410
	mutex_unlock(&tomoyo_policy_lock);
411
 out:
412
	tomoyo_put_name(saved_domainname);
413 414
	kfree(entry);
	return found ? domain : NULL;
K
Kentaro Takeda 已提交
415 416 417 418 419
}

/**
 * tomoyo_find_next_domain - Find a domain.
 *
420
 * @bprm: Pointer to "struct linux_binprm".
K
Kentaro Takeda 已提交
421 422
 *
 * Returns 0 on success, negative value otherwise.
423 424
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
425
 */
426
int tomoyo_find_next_domain(struct linux_binprm *bprm)
K
Kentaro Takeda 已提交
427
{
428
	struct tomoyo_request_info r;
T
Tetsuo Handa 已提交
429
	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
K
Kentaro Takeda 已提交
430 431 432
	struct tomoyo_domain_info *old_domain = tomoyo_domain();
	struct tomoyo_domain_info *domain = NULL;
	const char *original_name = bprm->filename;
T
Tetsuo Handa 已提交
433 434
	u8 mode;
	bool is_enforce;
K
Kentaro Takeda 已提交
435
	int retval = -ENOMEM;
T
Tetsuo Handa 已提交
436 437
	bool need_kfree = false;
	struct tomoyo_path_info rn = { }; /* real name */
K
Kentaro Takeda 已提交
438

T
Tetsuo Handa 已提交
439 440
	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
K
Kentaro Takeda 已提交
441 442 443
	if (!tmp)
		goto out;

444
 retry:
T
Tetsuo Handa 已提交
445 446 447 448
	if (need_kfree) {
		kfree(rn.name);
		need_kfree = false;
	}
T
Tetsuo Handa 已提交
449
	/* Get symlink's pathname of program. */
K
Kentaro Takeda 已提交
450
	retval = -ENOENT;
T
Tetsuo Handa 已提交
451
	rn.name = tomoyo_realpath_nofollow(original_name);
T
Tetsuo Handa 已提交
452
	if (!rn.name)
K
Kentaro Takeda 已提交
453
		goto out;
T
Tetsuo Handa 已提交
454 455 456
	tomoyo_fill_path_info(&rn);
	need_kfree = true;

457 458
	/* Check 'aggregator' directive. */
	{
T
Tetsuo Handa 已提交
459
		struct tomoyo_aggregator *ptr;
460 461
		list_for_each_entry_rcu(ptr, &tomoyo_policy_list
					[TOMOYO_ID_AGGREGATOR], head.list) {
462
			if (ptr->head.is_deleted ||
463 464 465
			    !tomoyo_path_matches_pattern(&rn,
							 ptr->original_name))
				continue;
T
Tetsuo Handa 已提交
466
			kfree(rn.name);
467 468 469 470 471 472 473
			need_kfree = false;
			/* This is OK because it is read only. */
			rn = *ptr->aggregated_name;
			break;
		}
	}

K
Kentaro Takeda 已提交
474
	/* Check execute permission. */
475
	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
476 477
	if (retval == TOMOYO_RETRY_REQUEST)
		goto retry;
K
Kentaro Takeda 已提交
478 479
	if (retval < 0)
		goto out;
480 481 482 483 484 485 486 487 488 489 490 491 492
	/*
	 * To be able to specify domainnames with wildcards, use the
	 * pathname specified in the policy (which may contain
	 * wildcard) rather than the pathname passed to execve()
	 * (which never contains wildcard).
	 */
	if (r.param.path.matched_path) {
		if (need_kfree)
			kfree(rn.name);
		need_kfree = false;
		/* This is OK because it is read only. */
		rn = *r.param.path.matched_path;
	}
K
Kentaro Takeda 已提交
493

494 495 496
	/* Calculate domain to transit to. */
	switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
K
Kentaro Takeda 已提交
497
		/* Transit to the child of tomoyo_kernel_domain domain. */
498 499 500 501
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
			 "%s", rn.name);
		break;
	case TOMOYO_TRANSITION_CONTROL_KEEP:
K
Kentaro Takeda 已提交
502 503
		/* Keep current domain. */
		domain = old_domain;
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
		break;
	default:
		if (old_domain == &tomoyo_kernel_domain &&
		    !tomoyo_policy_loaded) {
			/*
			 * Needn't to transit from kernel domain before
			 * starting /sbin/init. But transit from kernel domain
			 * if executing initializers because they might start
			 * before /sbin/init.
			 */
			domain = old_domain;
		} else {
			/* Normal domain transition. */
			snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
				 old_domain->domainname->name, rn.name);
		}
		break;
K
Kentaro Takeda 已提交
521
	}
T
Tetsuo Handa 已提交
522
	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
K
Kentaro Takeda 已提交
523
		goto done;
T
Tetsuo Handa 已提交
524
	domain = tomoyo_find_domain(tmp);
T
Tetsuo Handa 已提交
525 526
	if (!domain)
		domain = tomoyo_assign_domain(tmp, old_domain->profile);
K
Kentaro Takeda 已提交
527 528 529
 done:
	if (domain)
		goto out;
T
Tetsuo Handa 已提交
530
	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
K
Kentaro Takeda 已提交
531 532 533
	if (is_enforce)
		retval = -EPERM;
	else
T
Tetsuo Handa 已提交
534
		old_domain->transition_failed = true;
K
Kentaro Takeda 已提交
535
 out:
536 537
	if (!domain)
		domain = old_domain;
538 539
	/* Update reference count on "struct tomoyo_domain_info". */
	atomic_inc(&domain->users);
540
	bprm->cred->security = domain;
T
Tetsuo Handa 已提交
541 542
	if (need_kfree)
		kfree(rn.name);
543
	kfree(tmp);
K
Kentaro Takeda 已提交
544 545
	return retval;
}