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 15 16 17

/* Variables definitions.*/

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

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/**
 * tomoyo_update_policy - Update an entry for exception policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
 * @is_delete:       True if it is a delete request.
 * @list:            Pointer to "struct list_head".
 * @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,
			 bool is_delete, struct list_head *list,
			 bool (*check_duplicate) (const struct tomoyo_acl_head
						  *,
						  const struct tomoyo_acl_head
						  *))
{
	int error = is_delete ? -ENOENT : -ENOMEM;
	struct tomoyo_acl_head *entry;

	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		return -ENOMEM;
	list_for_each_entry_rcu(entry, list, list) {
		if (!check_duplicate(entry, new_entry))
			continue;
		entry->is_deleted = is_delete;
		error = 0;
		break;
	}
	if (error && !is_delete) {
		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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
/**
 * tomoyo_update_domain - Update an entry for domain policy.
 *
 * @new_entry:       Pointer to "struct tomoyo_acl_info".
 * @size:            Size of @new_entry in bytes.
 * @is_delete:       True if it is a delete request.
 * @domain:          Pointer to "struct tomoyo_domain_info".
 * @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,
			 bool is_delete, struct tomoyo_domain_info *domain,
			 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))
{
	int error = is_delete ? -ENOENT : -ENOMEM;
	struct tomoyo_acl_info *entry;

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

127
void tomoyo_check_acl(struct tomoyo_request_info *r,
128
		      bool (*check_entry) (struct tomoyo_request_info *,
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
					   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;
}

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

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

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

T
Tetsuo Handa 已提交
166 167
static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
					   const struct tomoyo_acl_head *b)
168
{
169 170 171 172 173 174 175
	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
176 177 178 179
		&& p1->domainname == p2->domainname
		&& p1->program == p2->program;
}

K
Kentaro Takeda 已提交
180
/**
181
 * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
K
Kentaro Takeda 已提交
182
 *
183 184 185
 * @domainname: The name of domain. Maybe NULL.
 * @program:    The name of program. Maybe NULL.
 * @type:       Type of transition.
K
Kentaro Takeda 已提交
186 187 188 189
 * @is_delete:  True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 */
190
static int tomoyo_update_transition_control_entry(const char *domainname,
K
Kentaro Takeda 已提交
191
						  const char *program,
192
						  const u8 type,
K
Kentaro Takeda 已提交
193 194
						  const bool is_delete)
{
195
	struct tomoyo_transition_control e = { .type = type };
196
	int error = is_delete ? -ENOENT : -ENOMEM;
197 198 199 200 201 202 203
	if (program) {
		if (!tomoyo_correct_path(program))
			return -EINVAL;
		e.program = tomoyo_get_name(program);
		if (!e.program)
			goto out;
	}
K
Kentaro Takeda 已提交
204
	if (domainname) {
205 206 207
		if (!tomoyo_correct_domain(domainname)) {
			if (!tomoyo_correct_path(domainname))
				goto out;
208
			e.is_last_name = true;
209
		}
210 211
		e.domainname = tomoyo_get_name(domainname);
		if (!e.domainname)
212
			goto out;
K
Kentaro Takeda 已提交
213
	}
214
	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
215
				     &tomoyo_policy_list
216
				     [TOMOYO_ID_TRANSITION_CONTROL],
T
Tetsuo Handa 已提交
217
				     tomoyo_same_transition_control);
218
 out:
219 220
	tomoyo_put_name(e.domainname);
	tomoyo_put_name(e.program);
K
Kentaro Takeda 已提交
221 222 223 224
	return error;
}

/**
225
 * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
K
Kentaro Takeda 已提交
226 227 228
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
229
 * @type:      Type of this entry.
K
Kentaro Takeda 已提交
230 231 232
 *
 * Returns 0 on success, negative value otherwise.
 */
233 234
int tomoyo_write_transition_control(char *data, const bool is_delete,
				    const u8 type)
K
Kentaro Takeda 已提交
235
{
236 237 238 239 240 241 242 243
	char *domainname = strstr(data, " from ");
	if (domainname) {
		*domainname = '\0';
		domainname += 6;
	} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
		   type == TOMOYO_TRANSITION_CONTROL_KEEP) {
		domainname = data;
		data = NULL;
K
Kentaro Takeda 已提交
244
	}
245
	return tomoyo_update_transition_control_entry(domainname, data, type,
K
Kentaro Takeda 已提交
246 247 248 249
						      is_delete);
}

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

T
Tetsuo Handa 已提交
306 307
static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
				   const struct tomoyo_acl_head *b)
308
{
T
Tetsuo Handa 已提交
309 310
	const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
	const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
311 312 313 314
	return p1->original_name == p2->original_name &&
		p1->aggregated_name == p2->aggregated_name;
}

315
/**
T
Tetsuo Handa 已提交
316
 * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
317 318 319 320 321 322 323 324 325 326 327 328 329
 *
 * @original_name:   The original program's name.
 * @aggregated_name: The program name to use.
 * @is_delete:       True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
static int tomoyo_update_aggregator_entry(const char *original_name,
					  const char *aggregated_name,
					  const bool is_delete)
{
T
Tetsuo Handa 已提交
330
	struct tomoyo_aggregator e = { };
331 332
	int error = is_delete ? -ENOENT : -ENOMEM;

T
Tetsuo Handa 已提交
333 334
	if (!tomoyo_correct_path(original_name) ||
	    !tomoyo_correct_path(aggregated_name))
335 336 337 338 339 340
		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;
341
	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
342
				     &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
T
Tetsuo Handa 已提交
343
				     tomoyo_same_aggregator);
344 345 346 347 348 349 350
 out:
	tomoyo_put_name(e.original_name);
	tomoyo_put_name(e.aggregated_name);
	return error;
}

/**
T
Tetsuo Handa 已提交
351
 * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
352 353 354 355 356 357 358 359
 *
 * @data:      String to parse.
 * @is_delete: True if it is a delete request.
 *
 * Returns 0 on success, negative value otherwise.
 *
 * Caller holds tomoyo_read_lock().
 */
T
Tetsuo Handa 已提交
360
int tomoyo_write_aggregator(char *data, const bool is_delete)
361 362 363 364 365 366 367 368 369
{
	char *cp = strchr(data, ' ');

	if (!cp)
		return -EINVAL;
	*cp++ = '\0';
	return tomoyo_update_aggregator_entry(data, cp, is_delete);
}

K
Kentaro Takeda 已提交
370
/**
T
Tetsuo Handa 已提交
371
 * tomoyo_assign_domain - Create a domain.
K
Kentaro Takeda 已提交
372 373 374 375 376
 *
 * @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.
377 378
 *
 * Caller holds tomoyo_read_lock().
K
Kentaro Takeda 已提交
379
 */
T
Tetsuo Handa 已提交
380 381
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
						const u8 profile)
K
Kentaro Takeda 已提交
382
{
383
	struct tomoyo_domain_info *entry;
384
	struct tomoyo_domain_info *domain = NULL;
K
Kentaro Takeda 已提交
385
	const struct tomoyo_path_info *saved_domainname;
386
	bool found = false;
K
Kentaro Takeda 已提交
387

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

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

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

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

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

K
Kentaro Takeda 已提交
477
	/* Check execute permission. */
478
	retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
479 480
	if (retval == TOMOYO_RETRY_REQUEST)
		goto retry;
K
Kentaro Takeda 已提交
481 482
	if (retval < 0)
		goto out;
483 484 485 486 487 488 489 490 491 492 493 494 495
	/*
	 * 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 已提交
496

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