domain.c 14.2 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 15 16 17

/* Variables definitions.*/

/* The initial domain. */
struct tomoyo_domain_info tomoyo_kernel_domain;

18 19 20 21 22
/**
 * tomoyo_update_policy - Update an entry for exception policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
23
 * @param:           Pointer to "struct tomoyo_acl_param".
24 25 26 27 28 29 30
 * @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,
31
			 struct tomoyo_acl_param *param,
32 33 34 35 36
			 bool (*check_duplicate) (const struct tomoyo_acl_head
						  *,
						  const struct tomoyo_acl_head
						  *))
{
37
	int error = param->is_delete ? -ENOENT : -ENOMEM;
38
	struct tomoyo_acl_head *entry;
39
	struct list_head *list = param->list;
40 41 42 43 44 45

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return -ENOMEM;
	list_for_each_entry_rcu(entry, list, list) {
		if (!check_duplicate(entry, new_entry))
			continue;
46
		entry->is_deleted = param->is_delete;
47 48 49
		error = 0;
		break;
	}
50
	if (error && !param->is_delete) {
51 52 53 54 55 56 57 58 59 60
		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 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74
/**
 * 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;
}

75 76 77 78 79
/**
 * tomoyo_update_domain - Update an entry for domain policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
80
 * @param:           Pointer to "struct tomoyo_acl_param".
81 82 83 84 85 86 87 88
 * @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,
89
			 struct tomoyo_acl_param *param,
90 91 92 93 94 95 96 97
			 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))
{
98
	const bool is_delete = param->is_delete;
99 100
	int error = is_delete ? -ENOENT : -ENOMEM;
	struct tomoyo_acl_info *entry;
101
	struct list_head * const list = param->list;
102 103 104

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return error;
105
	list_for_each_entry_rcu(entry, list, list) {
T
Tetsuo Handa 已提交
106 107
		if (!tomoyo_same_acl_head(entry, new_entry) ||
		    !check_duplicate(entry, new_entry))
108 109 110 111 112 113 114 115 116 117 118 119
			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) {
120
			list_add_tail_rcu(&entry->list, list);
121 122 123 124 125 126 127
			error = 0;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
	return error;
}

128
void tomoyo_check_acl(struct tomoyo_request_info *r,
129
		      bool (*check_entry) (struct tomoyo_request_info *,
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
					   const struct tomoyo_acl_info *))
{
	const struct tomoyo_domain_info *domain = r->domain;
	struct tomoyo_acl_info *ptr;

	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
		if (ptr->is_deleted || ptr->type != r->param_type)
			continue;
		if (check_entry(r, ptr)) {
			r->granted = true;
			return;
		}
	}
	r->granted = false;
}

146
/* The list for "struct tomoyo_domain_info". */
K
Kentaro Takeda 已提交
147 148
LIST_HEAD(tomoyo_domain_list);

149 150 151
struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];

K
Kentaro Takeda 已提交
152
/**
T
Tetsuo Handa 已提交
153
 * tomoyo_last_word - Get last component of a domainname.
K
Kentaro Takeda 已提交
154
 *
T
Tetsuo Handa 已提交
155
 * @domainname: Domainname to check.
K
Kentaro Takeda 已提交
156
 *
T
Tetsuo Handa 已提交
157
 * Returns the last word of @domainname.
K
Kentaro Takeda 已提交
158
 */
T
Tetsuo Handa 已提交
159
static const char *tomoyo_last_word(const char *name)
K
Kentaro Takeda 已提交
160
{
T
Tetsuo Handa 已提交
161 162 163 164
        const char *cp = strrchr(name, ' ');
        if (cp)
                return cp + 1;
        return name;
K
Kentaro Takeda 已提交
165 166
}

167 168 169 170 171 172 173 174
/**
 * 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 已提交
175 176
static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
					   const struct tomoyo_acl_head *b)
177
{
178 179 180 181 182 183 184
	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
185 186 187 188
		&& p1->domainname == p2->domainname
		&& p1->program == p2->program;
}

K
Kentaro Takeda 已提交
189
/**
190
 * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
K
Kentaro Takeda 已提交
191
 *
192 193
 * @param: Pointer to "struct tomoyo_acl_param".
 * @type:  Type of this entry.
K
Kentaro Takeda 已提交
194 195 196
 *
 * Returns 0 on success, negative value otherwise.
 */
197 198
int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
				    const u8 type)
K
Kentaro Takeda 已提交
199
{
200
	struct tomoyo_transition_control e = { .type = type };
201 202 203 204 205 206 207 208 209 210 211
	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 已提交
212
	if (program && strcmp(program, "any")) {
213 214 215 216 217 218
		if (!tomoyo_correct_path(program))
			return -EINVAL;
		e.program = tomoyo_get_name(program);
		if (!e.program)
			goto out;
	}
T
Tetsuo Handa 已提交
219
	if (domainname && strcmp(domainname, "any")) {
220 221 222
		if (!tomoyo_correct_domain(domainname)) {
			if (!tomoyo_correct_path(domainname))
				goto out;
223
			e.is_last_name = true;
224
		}
225 226
		e.domainname = tomoyo_get_name(domainname);
		if (!e.domainname)
227
			goto out;
K
Kentaro Takeda 已提交
228
	}
229 230
	param->list = &tomoyo_policy_list[TOMOYO_ID_TRANSITION_CONTROL];
	error = tomoyo_update_policy(&e.head, sizeof(e), param,
T
Tetsuo Handa 已提交
231
				     tomoyo_same_transition_control);
232
out:
233 234
	tomoyo_put_name(e.domainname);
	tomoyo_put_name(e.program);
K
Kentaro Takeda 已提交
235 236 237 238
	return error;
}

/**
239
 * tomoyo_transition_type - Get domain transition type.
K
Kentaro Takeda 已提交
240 241 242 243
 *
 * @domainname: The name of domain.
 * @program:    The name of program.
 *
244 245 246
 * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
 * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
 * @program suppresses domain transition, others otherwise.
247 248
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
249
 */
250 251
static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
				 const struct tomoyo_path_info *program)
K
Kentaro Takeda 已提交
252
{
253 254 255 256 257 258 259 260 261
	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 已提交
262
				continue;
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
			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 已提交
279
				continue;
280 281 282 283 284 285 286 287 288
			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 已提交
289 290
		}
	}
291 292
 done:
	return type;
K
Kentaro Takeda 已提交
293 294
}

295 296 297 298 299 300 301 302
/**
 * 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 已提交
303 304
static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
				   const struct tomoyo_acl_head *b)
305
{
306 307 308 309
	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
							  head);
	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
							  head);
310 311 312 313
	return p1->original_name == p2->original_name &&
		p1->aggregated_name == p2->aggregated_name;
}

314
/**
315
 * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
316
 *
317
 * @param: Pointer to "struct tomoyo_acl_param".
318 319 320 321 322
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
323
int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
324
{
T
Tetsuo Handa 已提交
325
	struct tomoyo_aggregator e = { };
326 327 328 329
	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 已提交
330
	    !tomoyo_correct_path(aggregated_name))
331 332 333 334 335 336
		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;
337 338
	param->list = &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR];
	error = tomoyo_update_policy(&e.head, sizeof(e), param,
T
Tetsuo Handa 已提交
339
				     tomoyo_same_aggregator);
340
out:
341 342 343 344 345
	tomoyo_put_name(e.original_name);
	tomoyo_put_name(e.aggregated_name);
	return error;
}

K
Kentaro Takeda 已提交
346
/**
T
Tetsuo Handa 已提交
347
 * tomoyo_assign_domain - Create a domain.
K
Kentaro Takeda 已提交
348 349 350 351 352
 *
 * @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.
353 354
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
355
 */
T
Tetsuo Handa 已提交
356 357
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
						const u8 profile)
K
Kentaro Takeda 已提交
358
{
359
	struct tomoyo_domain_info *entry;
360
	struct tomoyo_domain_info *domain = NULL;
K
Kentaro Takeda 已提交
361
	const struct tomoyo_path_info *saved_domainname;
362
	bool found = false;
K
Kentaro Takeda 已提交
363

T
Tetsuo Handa 已提交
364
	if (!tomoyo_correct_domain(domainname))
365
		return NULL;
366
	saved_domainname = tomoyo_get_name(domainname);
K
Kentaro Takeda 已提交
367
	if (!saved_domainname)
368
		return NULL;
369
	entry = kzalloc(sizeof(*entry), GFP_NOFS);
370 371
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
372 373 374 375 376 377 378 379 380 381
	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;
382
		saved_domainname = NULL;
383 384 385 386 387
		entry->profile = profile;
		list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
		domain = entry;
		entry = NULL;
		found = true;
K
Kentaro Takeda 已提交
388
	}
389
	mutex_unlock(&tomoyo_policy_lock);
390
 out:
391
	tomoyo_put_name(saved_domainname);
392 393
	kfree(entry);
	return found ? domain : NULL;
K
Kentaro Takeda 已提交
394 395 396 397 398
}

/**
 * tomoyo_find_next_domain - Find a domain.
 *
399
 * @bprm: Pointer to "struct linux_binprm".
K
Kentaro Takeda 已提交
400 401
 *
 * Returns 0 on success, negative value otherwise.
402 403
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
404
 */
405
int tomoyo_find_next_domain(struct linux_binprm *bprm)
K
Kentaro Takeda 已提交
406
{
407
	struct tomoyo_request_info r;
T
Tetsuo Handa 已提交
408
	char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
K
Kentaro Takeda 已提交
409 410 411
	struct tomoyo_domain_info *old_domain = tomoyo_domain();
	struct tomoyo_domain_info *domain = NULL;
	const char *original_name = bprm->filename;
T
Tetsuo Handa 已提交
412 413
	u8 mode;
	bool is_enforce;
K
Kentaro Takeda 已提交
414
	int retval = -ENOMEM;
T
Tetsuo Handa 已提交
415 416
	bool need_kfree = false;
	struct tomoyo_path_info rn = { }; /* real name */
K
Kentaro Takeda 已提交
417

T
Tetsuo Handa 已提交
418 419
	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
K
Kentaro Takeda 已提交
420 421 422
	if (!tmp)
		goto out;

423
 retry:
T
Tetsuo Handa 已提交
424 425 426 427
	if (need_kfree) {
		kfree(rn.name);
		need_kfree = false;
	}
T
Tetsuo Handa 已提交
428
	/* Get symlink's pathname of program. */
K
Kentaro Takeda 已提交
429
	retval = -ENOENT;
T
Tetsuo Handa 已提交
430
	rn.name = tomoyo_realpath_nofollow(original_name);
T
Tetsuo Handa 已提交
431
	if (!rn.name)
K
Kentaro Takeda 已提交
432
		goto out;
T
Tetsuo Handa 已提交
433 434 435
	tomoyo_fill_path_info(&rn);
	need_kfree = true;

436 437
	/* Check 'aggregator' directive. */
	{
T
Tetsuo Handa 已提交
438
		struct tomoyo_aggregator *ptr;
439 440
		list_for_each_entry_rcu(ptr, &tomoyo_policy_list
					[TOMOYO_ID_AGGREGATOR], head.list) {
441
			if (ptr->head.is_deleted ||
442 443 444
			    !tomoyo_path_matches_pattern(&rn,
							 ptr->original_name))
				continue;
T
Tetsuo Handa 已提交
445
			kfree(rn.name);
446 447 448 449 450 451 452
			need_kfree = false;
			/* This is OK because it is read only. */
			rn = *ptr->aggregated_name;
			break;
		}
	}

K
Kentaro Takeda 已提交
453
	/* Check execute permission. */
454
	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
455 456
	if (retval == TOMOYO_RETRY_REQUEST)
		goto retry;
K
Kentaro Takeda 已提交
457 458
	if (retval < 0)
		goto out;
459 460 461 462 463 464 465 466 467 468 469 470 471
	/*
	 * 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 已提交
472

473 474 475
	/* Calculate domain to transit to. */
	switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
	case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
K
Kentaro Takeda 已提交
476
		/* Transit to the child of tomoyo_kernel_domain domain. */
477 478 479 480
		snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
			 "%s", rn.name);
		break;
	case TOMOYO_TRANSITION_CONTROL_KEEP:
K
Kentaro Takeda 已提交
481 482
		/* Keep current domain. */
		domain = old_domain;
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
		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 已提交
500
	}
T
Tetsuo Handa 已提交
501
	if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
K
Kentaro Takeda 已提交
502
		goto done;
T
Tetsuo Handa 已提交
503
	domain = tomoyo_find_domain(tmp);
T
Tetsuo Handa 已提交
504 505
	if (!domain)
		domain = tomoyo_assign_domain(tmp, old_domain->profile);
K
Kentaro Takeda 已提交
506 507 508
 done:
	if (domain)
		goto out;
T
Tetsuo Handa 已提交
509
	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
K
Kentaro Takeda 已提交
510 511 512
	if (is_enforce)
		retval = -EPERM;
	else
T
Tetsuo Handa 已提交
513
		old_domain->transition_failed = true;
K
Kentaro Takeda 已提交
514
 out:
515 516
	if (!domain)
		domain = old_domain;
517 518
	/* Update reference count on "struct tomoyo_domain_info". */
	atomic_inc(&domain->users);
519
	bprm->cred->security = domain;
T
Tetsuo Handa 已提交
520 521
	if (need_kfree)
		kfree(rn.name);
522
	kfree(tmp);
K
Kentaro Takeda 已提交
523 524
	return retval;
}