ima_policy.c 13.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2008 IBM Corporation
 * Author: Mimi Zohar <zohar@us.ibm.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 2 of the License.
 *
 * ima_policy.c
 * 	- initialize default measure policy rules
 *
 */
#include <linux/module.h>
#include <linux/list.h>
#include <linux/security.h>
#include <linux/magic.h>
M
Mimi Zohar 已提交
17
#include <linux/parser.h>
18
#include <linux/slab.h>
19 20 21 22 23 24 25 26 27

#include "ima.h"

/* flags definitions */
#define IMA_FUNC 	0x0001
#define IMA_MASK 	0x0002
#define IMA_FSMAGIC	0x0004
#define IMA_UID		0x0008

M
Mimi Zohar 已提交
28 29 30 31 32 33 34
#define UNKNOWN			0
#define MEASURE			1	/* same as IMA_MEASURE */
#define DONT_MEASURE		2
#define MEASURE_MASK		3
#define APPRAISE		4	/* same as IMA_APPRAISE */
#define DONT_APPRAISE		8
#define APPRAISE_MASK		12
M
Mimi Zohar 已提交
35 36 37 38 39

#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
	LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
};
40 41 42

struct ima_measure_rule_entry {
	struct list_head list;
M
Mimi Zohar 已提交
43
	int action;
44 45 46 47 48
	unsigned int flags;
	enum ima_hooks func;
	int mask;
	unsigned long fsmagic;
	uid_t uid;
M
Mimi Zohar 已提交
49 50 51 52
	struct {
		void *rule;	/* LSM file metadata specific */
		int type;	/* audit type */
	} lsm[MAX_LSM_RULES];
53 54
};

55 56
/*
 * Without LSM specific knowledge, the default policy can only be
M
Mimi Zohar 已提交
57 58
 * written in terms of .action, .func, .mask, .fsmagic, and .uid
 */
59 60 61 62 63 64 65

/*
 * The minimum rule set to allow for full TCB coverage.  Measures all files
 * opened or mmap for exec and everything read by root.  Dangerous because
 * normal users can easily run the machine out of memory simply building
 * and running executables.
 */
66
static struct ima_measure_rule_entry default_rules[] = {
67
	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
68 69 70
	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
D
Dmitry Kasatkin 已提交
71
	{.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
D
Dmitry Kasatkin 已提交
72 73
	{.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
	{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
74 75
	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
	{.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
76 77 78 79
	{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
	 .flags = IMA_FUNC | IMA_MASK},
	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
	 .flags = IMA_FUNC | IMA_MASK},
80
	{.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = 0,
81
	 .flags = IMA_FUNC | IMA_MASK | IMA_UID},
82 83 84
};

static LIST_HEAD(measure_default_rules);
M
Mimi Zohar 已提交
85
static LIST_HEAD(measure_policy_rules);
86 87
static struct list_head *ima_measure;

M
Mimi Zohar 已提交
88 89
static DEFINE_MUTEX(ima_measure_mutex);

90 91 92 93 94 95 96 97
static bool ima_use_tcb __initdata;
static int __init default_policy_setup(char *str)
{
	ima_use_tcb = 1;
	return 1;
}
__setup("ima_tcb", default_policy_setup);

98 99 100 101 102 103 104 105 106 107 108 109 110
/**
 * ima_match_rules - determine whether an inode matches the measure rule.
 * @rule: a pointer to a rule
 * @inode: a pointer to an inode
 * @func: LIM hook identifier
 * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
 *
 * Returns true on rule match, false on failure.
 */
static bool ima_match_rules(struct ima_measure_rule_entry *rule,
			    struct inode *inode, enum ima_hooks func, int mask)
{
	struct task_struct *tsk = current;
M
Mimi Zohar 已提交
111
	const struct cred *cred = current_cred();
M
Mimi Zohar 已提交
112
	int i;
113 114 115 116 117 118 119 120

	if ((rule->flags & IMA_FUNC) && rule->func != func)
		return false;
	if ((rule->flags & IMA_MASK) && rule->mask != mask)
		return false;
	if ((rule->flags & IMA_FSMAGIC)
	    && rule->fsmagic != inode->i_sb->s_magic)
		return false;
M
Mimi Zohar 已提交
121
	if ((rule->flags & IMA_UID) && rule->uid != cred->uid)
122
		return false;
M
Mimi Zohar 已提交
123
	for (i = 0; i < MAX_LSM_RULES; i++) {
124
		int rc = 0;
M
Mimi Zohar 已提交
125 126 127 128 129 130 131 132 133 134 135 136
		u32 osid, sid;

		if (!rule->lsm[i].rule)
			continue;

		switch (i) {
		case LSM_OBJ_USER:
		case LSM_OBJ_ROLE:
		case LSM_OBJ_TYPE:
			security_inode_getsecid(inode, &osid);
			rc = security_filter_rule_match(osid,
							rule->lsm[i].type,
137
							Audit_equal,
M
Mimi Zohar 已提交
138 139 140 141 142 143 144 145 146
							rule->lsm[i].rule,
							NULL);
			break;
		case LSM_SUBJ_USER:
		case LSM_SUBJ_ROLE:
		case LSM_SUBJ_TYPE:
			security_task_getsecid(tsk, &sid);
			rc = security_filter_rule_match(sid,
							rule->lsm[i].type,
147
							Audit_equal,
M
Mimi Zohar 已提交
148 149 150 151 152 153 154 155
							rule->lsm[i].rule,
							NULL);
		default:
			break;
		}
		if (!rc)
			return false;
	}
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	return true;
}

/**
 * ima_match_policy - decision based on LSM and other conditions
 * @inode: pointer to an inode for which the policy decision is being made
 * @func: IMA hook identifier
 * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
 *
 * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
 * conditions.
 *
 * (There is no need for locking when walking the policy list,
 * as elements in the list are never deleted, nor does the list
 * change.)
 */
M
Mimi Zohar 已提交
172 173
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
		     int flags)
174 175
{
	struct ima_measure_rule_entry *entry;
M
Mimi Zohar 已提交
176
	int action = 0, actmask = flags | (flags << 1);
177 178 179

	list_for_each_entry(entry, ima_measure, list) {

M
Mimi Zohar 已提交
180 181 182 183 184 185 186 187 188 189 190
		if (!(entry->action & actmask))
			continue;

		if (!ima_match_rules(entry, inode, func, mask))
			continue;

		action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE));
		actmask &= (entry->action & APPRAISE_MASK) ?
		    ~APPRAISE_MASK : ~MEASURE_MASK;
		if (!actmask)
			break;
191
	}
M
Mimi Zohar 已提交
192 193

	return action;
194 195 196 197 198 199
}

/**
 * ima_init_policy - initialize the default measure rules.
 *
 * ima_measure points to either the measure_default_rules or the
M
Mimi Zohar 已提交
200
 * the new measure_policy_rules.
201
 */
202
void __init ima_init_policy(void)
203
{
204 205 206 207 208 209 210
	int i, entries;

	/* if !ima_use_tcb set entries = 0 so we load NO default rules */
	if (ima_use_tcb)
		entries = ARRAY_SIZE(default_rules);
	else
		entries = 0;
211

212
	for (i = 0; i < entries; i++)
213 214 215
		list_add_tail(&default_rules[i].list, &measure_default_rules);
	ima_measure = &measure_default_rules;
}
M
Mimi Zohar 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

/**
 * ima_update_policy - update default_rules with new measure rules
 *
 * Called on file .release to update the default rules with a complete new
 * policy.  Once updated, the policy is locked, no additional rules can be
 * added to the policy.
 */
void ima_update_policy(void)
{
	const char *op = "policy_update";
	const char *cause = "already exists";
	int result = 1;
	int audit_info = 0;

	if (ima_measure == &measure_default_rules) {
		ima_measure = &measure_policy_rules;
		cause = "complete";
		result = 0;
	}
	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
			    NULL, op, cause, result, audit_info);
}

enum {
	Opt_err = -1,
	Opt_measure = 1, Opt_dont_measure,
	Opt_obj_user, Opt_obj_role, Opt_obj_type,
	Opt_subj_user, Opt_subj_role, Opt_subj_type,
	Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
};

static match_table_t policy_tokens = {
	{Opt_measure, "measure"},
	{Opt_dont_measure, "dont_measure"},
	{Opt_obj_user, "obj_user=%s"},
	{Opt_obj_role, "obj_role=%s"},
	{Opt_obj_type, "obj_type=%s"},
	{Opt_subj_user, "subj_user=%s"},
	{Opt_subj_role, "subj_role=%s"},
	{Opt_subj_type, "subj_type=%s"},
	{Opt_func, "func=%s"},
	{Opt_mask, "mask=%s"},
	{Opt_fsmagic, "fsmagic=%s"},
	{Opt_uid, "uid=%s"},
	{Opt_err, NULL}
};

static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
			     char *args, int lsm_rule, int audit_type)
{
	int result;

269 270 271
	if (entry->lsm[lsm_rule].rule)
		return -EINVAL;

M
Mimi Zohar 已提交
272 273
	entry->lsm[lsm_rule].type = audit_type;
	result = security_filter_rule_init(entry->lsm[lsm_rule].type,
274
					   Audit_equal, args,
M
Mimi Zohar 已提交
275
					   &entry->lsm[lsm_rule].rule);
M
Mimi Zohar 已提交
276 277
	if (!entry->lsm[lsm_rule].rule)
		return -EINVAL;
M
Mimi Zohar 已提交
278 279 280
	return result;
}

281 282 283 284 285 286 287
static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
{
	audit_log_format(ab, "%s=", key);
	audit_log_untrustedstring(ab, value);
	audit_log_format(ab, " ");
}

M
Mimi Zohar 已提交
288 289 290 291 292 293
static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
{
	struct audit_buffer *ab;
	char *p;
	int result = 0;

M
Mimi Zohar 已提交
294
	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
M
Mimi Zohar 已提交
295

296
	entry->uid = -1;
297
	entry->action = UNKNOWN;
E
Eric Paris 已提交
298
	while ((p = strsep(&rule, " \t")) != NULL) {
M
Mimi Zohar 已提交
299 300 301 302 303 304
		substring_t args[MAX_OPT_ARGS];
		int token;
		unsigned long lnum;

		if (result < 0)
			break;
E
Eric Paris 已提交
305 306
		if ((*p == '\0') || (*p == ' ') || (*p == '\t'))
			continue;
M
Mimi Zohar 已提交
307 308 309
		token = match_token(p, policy_tokens, args);
		switch (token) {
		case Opt_measure:
310
			ima_log_string(ab, "action", "measure");
311 312 313 314

			if (entry->action != UNKNOWN)
				result = -EINVAL;

M
Mimi Zohar 已提交
315 316 317
			entry->action = MEASURE;
			break;
		case Opt_dont_measure:
318
			ima_log_string(ab, "action", "dont_measure");
319 320 321 322

			if (entry->action != UNKNOWN)
				result = -EINVAL;

M
Mimi Zohar 已提交
323 324 325
			entry->action = DONT_MEASURE;
			break;
		case Opt_func:
326
			ima_log_string(ab, "func", args[0].from);
327 328 329 330

			if (entry->func)
				result  = -EINVAL;

331 332 333 334 335
			if (strcmp(args[0].from, "FILE_CHECK") == 0)
				entry->func = FILE_CHECK;
			/* PATH_CHECK is for backwards compat */
			else if (strcmp(args[0].from, "PATH_CHECK") == 0)
				entry->func = FILE_CHECK;
M
Mimi Zohar 已提交
336 337 338 339 340 341 342 343 344 345
			else if (strcmp(args[0].from, "FILE_MMAP") == 0)
				entry->func = FILE_MMAP;
			else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
				entry->func = BPRM_CHECK;
			else
				result = -EINVAL;
			if (!result)
				entry->flags |= IMA_FUNC;
			break;
		case Opt_mask:
346
			ima_log_string(ab, "mask", args[0].from);
347 348 349 350

			if (entry->mask)
				result = -EINVAL;

M
Mimi Zohar 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364
			if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
				entry->mask = MAY_EXEC;
			else if (strcmp(args[0].from, "MAY_WRITE") == 0)
				entry->mask = MAY_WRITE;
			else if (strcmp(args[0].from, "MAY_READ") == 0)
				entry->mask = MAY_READ;
			else if (strcmp(args[0].from, "MAY_APPEND") == 0)
				entry->mask = MAY_APPEND;
			else
				result = -EINVAL;
			if (!result)
				entry->flags |= IMA_MASK;
			break;
		case Opt_fsmagic:
365
			ima_log_string(ab, "fsmagic", args[0].from);
366 367 368 369 370 371

			if (entry->fsmagic) {
				result = -EINVAL;
				break;
			}

M
Mimi Zohar 已提交
372 373 374 375 376 377
			result = strict_strtoul(args[0].from, 16,
						&entry->fsmagic);
			if (!result)
				entry->flags |= IMA_FSMAGIC;
			break;
		case Opt_uid:
378
			ima_log_string(ab, "uid", args[0].from);
379 380 381 382 383 384

			if (entry->uid != -1) {
				result = -EINVAL;
				break;
			}

M
Mimi Zohar 已提交
385 386 387 388 389 390 391 392 393 394
			result = strict_strtoul(args[0].from, 10, &lnum);
			if (!result) {
				entry->uid = (uid_t) lnum;
				if (entry->uid != lnum)
					result = -EINVAL;
				else
					entry->flags |= IMA_UID;
			}
			break;
		case Opt_obj_user:
395
			ima_log_string(ab, "obj_user", args[0].from);
M
Mimi Zohar 已提交
396 397 398 399 400
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_OBJ_USER,
						   AUDIT_OBJ_USER);
			break;
		case Opt_obj_role:
401
			ima_log_string(ab, "obj_role", args[0].from);
M
Mimi Zohar 已提交
402 403 404 405 406
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_OBJ_ROLE,
						   AUDIT_OBJ_ROLE);
			break;
		case Opt_obj_type:
407
			ima_log_string(ab, "obj_type", args[0].from);
M
Mimi Zohar 已提交
408 409 410 411 412
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_OBJ_TYPE,
						   AUDIT_OBJ_TYPE);
			break;
		case Opt_subj_user:
413
			ima_log_string(ab, "subj_user", args[0].from);
M
Mimi Zohar 已提交
414 415 416 417 418
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_SUBJ_USER,
						   AUDIT_SUBJ_USER);
			break;
		case Opt_subj_role:
419
			ima_log_string(ab, "subj_role", args[0].from);
M
Mimi Zohar 已提交
420 421 422 423 424
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_SUBJ_ROLE,
						   AUDIT_SUBJ_ROLE);
			break;
		case Opt_subj_type:
425
			ima_log_string(ab, "subj_type", args[0].from);
M
Mimi Zohar 已提交
426 427 428 429 430
			result = ima_lsm_rule_init(entry, args[0].from,
						   LSM_SUBJ_TYPE,
						   AUDIT_SUBJ_TYPE);
			break;
		case Opt_err:
431
			ima_log_string(ab, "UNKNOWN", p);
432
			result = -EINVAL;
M
Mimi Zohar 已提交
433 434 435
			break;
		}
	}
436
	if (!result && (entry->action == UNKNOWN))
M
Mimi Zohar 已提交
437 438
		result = -EINVAL;

439
	audit_log_format(ab, "res=%d", !result);
M
Mimi Zohar 已提交
440 441 442 443 444 445 446 447 448
	audit_log_end(ab);
	return result;
}

/**
 * ima_parse_add_rule - add a rule to measure_policy_rules
 * @rule - ima measurement policy rule
 *
 * Uses a mutex to protect the policy list from multiple concurrent writers.
449
 * Returns the length of the rule parsed, an error code on failure
M
Mimi Zohar 已提交
450
 */
451
ssize_t ima_parse_add_rule(char *rule)
M
Mimi Zohar 已提交
452
{
M
Mimi Zohar 已提交
453
	const char *op = "update_policy";
454
	char *p;
M
Mimi Zohar 已提交
455
	struct ima_measure_rule_entry *entry;
456
	ssize_t result, len;
M
Mimi Zohar 已提交
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
	int audit_info = 0;

	/* Prevent installed policy from changing */
	if (ima_measure != &measure_default_rules) {
		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
				    NULL, op, "already exists",
				    -EACCES, audit_info);
		return -EACCES;
	}

	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry) {
		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
		return -ENOMEM;
	}

	INIT_LIST_HEAD(&entry->list);

476 477
	p = strsep(&rule, "\n");
	len = strlen(p) + 1;
E
Eric Paris 已提交
478 479 480 481 482 483

	if (*p == '#') {
		kfree(entry);
		return len;
	}

484
	result = ima_parse_rule(p, entry);
E
Eric Paris 已提交
485
	if (result) {
M
Mimi Zohar 已提交
486
		kfree(entry);
M
Mimi Zohar 已提交
487 488 489
		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
				    NULL, op, "invalid policy", result,
				    audit_info);
E
Eric Paris 已提交
490
		return result;
M
Mimi Zohar 已提交
491
	}
E
Eric Paris 已提交
492 493 494 495 496 497

	mutex_lock(&ima_measure_mutex);
	list_add_tail(&entry->list, &measure_policy_rules);
	mutex_unlock(&ima_measure_mutex);

	return len;
M
Mimi Zohar 已提交
498 499 500
}

/* ima_delete_rules called to cleanup invalid policy */
501
void ima_delete_rules(void)
M
Mimi Zohar 已提交
502 503 504 505 506 507 508 509 510 511
{
	struct ima_measure_rule_entry *entry, *tmp;

	mutex_lock(&ima_measure_mutex);
	list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
		list_del(&entry->list);
		kfree(entry);
	}
	mutex_unlock(&ima_measure_mutex);
}