| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* Authors: Karl MacMillan <kmacmillan@tresys.com> | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 2 |  *	    Frank Mayer <mayerf@tresys.com> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 |  * | 
 | 4 |  * Copyright (C) 2003 - 2004 Tresys Technology, LLC | 
 | 5 |  *	This program is free software; you can redistribute it and/or modify | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 6 |  *	it under the terms of the GNU General Public License as published by | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 |  *	the Free Software Foundation, version 2. | 
 | 8 |  */ | 
 | 9 |  | 
 | 10 | #include <linux/kernel.h> | 
 | 11 | #include <linux/errno.h> | 
 | 12 | #include <linux/string.h> | 
 | 13 | #include <linux/spinlock.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/slab.h> | 
 | 15 |  | 
 | 16 | #include "security.h" | 
 | 17 | #include "conditional.h" | 
 | 18 |  | 
 | 19 | /* | 
 | 20 |  * cond_evaluate_expr evaluates a conditional expr | 
 | 21 |  * in reverse polish notation. It returns true (1), false (0), | 
 | 22 |  * or undefined (-1). Undefined occurs when the expression | 
 | 23 |  * exceeds the stack depth of COND_EXPR_MAXDEPTH. | 
 | 24 |  */ | 
 | 25 | static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) | 
 | 26 | { | 
 | 27 |  | 
 | 28 | 	struct cond_expr *cur; | 
 | 29 | 	int s[COND_EXPR_MAXDEPTH]; | 
 | 30 | 	int sp = -1; | 
 | 31 |  | 
 | 32 | 	for (cur = expr; cur != NULL; cur = cur->next) { | 
 | 33 | 		switch (cur->expr_type) { | 
 | 34 | 		case COND_BOOL: | 
 | 35 | 			if (sp == (COND_EXPR_MAXDEPTH - 1)) | 
 | 36 | 				return -1; | 
 | 37 | 			sp++; | 
 | 38 | 			s[sp] = p->bool_val_to_struct[cur->bool - 1]->state; | 
 | 39 | 			break; | 
 | 40 | 		case COND_NOT: | 
 | 41 | 			if (sp < 0) | 
 | 42 | 				return -1; | 
 | 43 | 			s[sp] = !s[sp]; | 
 | 44 | 			break; | 
 | 45 | 		case COND_OR: | 
 | 46 | 			if (sp < 1) | 
 | 47 | 				return -1; | 
 | 48 | 			sp--; | 
 | 49 | 			s[sp] |= s[sp + 1]; | 
 | 50 | 			break; | 
 | 51 | 		case COND_AND: | 
 | 52 | 			if (sp < 1) | 
 | 53 | 				return -1; | 
 | 54 | 			sp--; | 
 | 55 | 			s[sp] &= s[sp + 1]; | 
 | 56 | 			break; | 
 | 57 | 		case COND_XOR: | 
 | 58 | 			if (sp < 1) | 
 | 59 | 				return -1; | 
 | 60 | 			sp--; | 
 | 61 | 			s[sp] ^= s[sp + 1]; | 
 | 62 | 			break; | 
 | 63 | 		case COND_EQ: | 
 | 64 | 			if (sp < 1) | 
 | 65 | 				return -1; | 
 | 66 | 			sp--; | 
 | 67 | 			s[sp] = (s[sp] == s[sp + 1]); | 
 | 68 | 			break; | 
 | 69 | 		case COND_NEQ: | 
 | 70 | 			if (sp < 1) | 
 | 71 | 				return -1; | 
 | 72 | 			sp--; | 
 | 73 | 			s[sp] = (s[sp] != s[sp + 1]); | 
 | 74 | 			break; | 
 | 75 | 		default: | 
 | 76 | 			return -1; | 
 | 77 | 		} | 
 | 78 | 	} | 
 | 79 | 	return s[0]; | 
 | 80 | } | 
 | 81 |  | 
 | 82 | /* | 
 | 83 |  * evaluate_cond_node evaluates the conditional stored in | 
 | 84 |  * a struct cond_node and if the result is different than the | 
 | 85 |  * current state of the node it sets the rules in the true/false | 
 | 86 |  * list appropriately. If the result of the expression is undefined | 
 | 87 |  * all of the rules are disabled for safety. | 
 | 88 |  */ | 
 | 89 | int evaluate_cond_node(struct policydb *p, struct cond_node *node) | 
 | 90 | { | 
 | 91 | 	int new_state; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 92 | 	struct cond_av_list *cur; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 |  | 
 | 94 | 	new_state = cond_evaluate_expr(p, node->expr); | 
 | 95 | 	if (new_state != node->cur_state) { | 
 | 96 | 		node->cur_state = new_state; | 
 | 97 | 		if (new_state == -1) | 
| James Morris | 454d972 | 2008-02-26 20:42:02 +1100 | [diff] [blame] | 98 | 			printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | 		/* turn the rules on or off */ | 
 | 100 | 		for (cur = node->true_list; cur != NULL; cur = cur->next) { | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 101 | 			if (new_state <= 0) | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 102 | 				cur->node->key.specified &= ~AVTAB_ENABLED; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 103 | 			else | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 104 | 				cur->node->key.specified |= AVTAB_ENABLED; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | 		} | 
 | 106 |  | 
 | 107 | 		for (cur = node->false_list; cur != NULL; cur = cur->next) { | 
 | 108 | 			/* -1 or 1 */ | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 109 | 			if (new_state) | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 110 | 				cur->node->key.specified &= ~AVTAB_ENABLED; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 111 | 			else | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 112 | 				cur->node->key.specified |= AVTAB_ENABLED; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 | 		} | 
 | 114 | 	} | 
 | 115 | 	return 0; | 
 | 116 | } | 
 | 117 |  | 
 | 118 | int cond_policydb_init(struct policydb *p) | 
 | 119 | { | 
 | 120 | 	p->bool_val_to_struct = NULL; | 
 | 121 | 	p->cond_list = NULL; | 
 | 122 | 	if (avtab_init(&p->te_cond_avtab)) | 
 | 123 | 		return -1; | 
 | 124 |  | 
 | 125 | 	return 0; | 
 | 126 | } | 
 | 127 |  | 
 | 128 | static void cond_av_list_destroy(struct cond_av_list *list) | 
 | 129 | { | 
 | 130 | 	struct cond_av_list *cur, *next; | 
 | 131 | 	for (cur = list; cur != NULL; cur = next) { | 
 | 132 | 		next = cur->next; | 
 | 133 | 		/* the avtab_ptr_t node is destroy by the avtab */ | 
 | 134 | 		kfree(cur); | 
 | 135 | 	} | 
 | 136 | } | 
 | 137 |  | 
 | 138 | static void cond_node_destroy(struct cond_node *node) | 
 | 139 | { | 
 | 140 | 	struct cond_expr *cur_expr, *next_expr; | 
 | 141 |  | 
 | 142 | 	for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) { | 
 | 143 | 		next_expr = cur_expr->next; | 
 | 144 | 		kfree(cur_expr); | 
 | 145 | 	} | 
 | 146 | 	cond_av_list_destroy(node->true_list); | 
 | 147 | 	cond_av_list_destroy(node->false_list); | 
 | 148 | 	kfree(node); | 
 | 149 | } | 
 | 150 |  | 
 | 151 | static void cond_list_destroy(struct cond_node *list) | 
 | 152 | { | 
 | 153 | 	struct cond_node *next, *cur; | 
 | 154 |  | 
 | 155 | 	if (list == NULL) | 
 | 156 | 		return; | 
 | 157 |  | 
 | 158 | 	for (cur = list; cur != NULL; cur = next) { | 
 | 159 | 		next = cur->next; | 
 | 160 | 		cond_node_destroy(cur); | 
 | 161 | 	} | 
 | 162 | } | 
 | 163 |  | 
 | 164 | void cond_policydb_destroy(struct policydb *p) | 
 | 165 | { | 
| Jesper Juhl | 9a5f04b | 2005-06-25 14:58:51 -0700 | [diff] [blame] | 166 | 	kfree(p->bool_val_to_struct); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | 	avtab_destroy(&p->te_cond_avtab); | 
 | 168 | 	cond_list_destroy(p->cond_list); | 
 | 169 | } | 
 | 170 |  | 
 | 171 | int cond_init_bool_indexes(struct policydb *p) | 
 | 172 | { | 
| Jesper Juhl | 9a5f04b | 2005-06-25 14:58:51 -0700 | [diff] [blame] | 173 | 	kfree(p->bool_val_to_struct); | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 174 | 	p->bool_val_to_struct = (struct cond_bool_datum **) | 
 | 175 | 		kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 | 	if (!p->bool_val_to_struct) | 
 | 177 | 		return -1; | 
 | 178 | 	return 0; | 
 | 179 | } | 
 | 180 |  | 
 | 181 | int cond_destroy_bool(void *key, void *datum, void *p) | 
 | 182 | { | 
| Jesper Juhl | 9a5f04b | 2005-06-25 14:58:51 -0700 | [diff] [blame] | 183 | 	kfree(key); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | 	kfree(datum); | 
 | 185 | 	return 0; | 
 | 186 | } | 
 | 187 |  | 
 | 188 | int cond_index_bool(void *key, void *datum, void *datap) | 
 | 189 | { | 
 | 190 | 	struct policydb *p; | 
 | 191 | 	struct cond_bool_datum *booldatum; | 
 | 192 |  | 
 | 193 | 	booldatum = datum; | 
 | 194 | 	p = datap; | 
 | 195 |  | 
 | 196 | 	if (!booldatum->value || booldatum->value > p->p_bools.nprim) | 
 | 197 | 		return -EINVAL; | 
 | 198 |  | 
 | 199 | 	p->p_bool_val_to_name[booldatum->value - 1] = key; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 200 | 	p->bool_val_to_struct[booldatum->value - 1] = booldatum; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 201 |  | 
 | 202 | 	return 0; | 
 | 203 | } | 
 | 204 |  | 
 | 205 | static int bool_isvalid(struct cond_bool_datum *b) | 
 | 206 | { | 
 | 207 | 	if (!(b->state == 0 || b->state == 1)) | 
 | 208 | 		return 0; | 
 | 209 | 	return 1; | 
 | 210 | } | 
 | 211 |  | 
 | 212 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | 
 | 213 | { | 
 | 214 | 	char *key = NULL; | 
 | 215 | 	struct cond_bool_datum *booldatum; | 
| Alexey Dobriyan | b5bf6c5 | 2005-09-03 15:55:17 -0700 | [diff] [blame] | 216 | 	__le32 buf[3]; | 
 | 217 | 	u32 len; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 | 	int rc; | 
 | 219 |  | 
| James Morris | 89d155e | 2005-10-30 14:59:21 -0800 | [diff] [blame] | 220 | 	booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 221 | 	if (!booldatum) | 
 | 222 | 		return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 223 |  | 
 | 224 | 	rc = next_entry(buf, fp, sizeof buf); | 
 | 225 | 	if (rc < 0) | 
 | 226 | 		goto err; | 
 | 227 |  | 
 | 228 | 	booldatum->value = le32_to_cpu(buf[0]); | 
 | 229 | 	booldatum->state = le32_to_cpu(buf[1]); | 
 | 230 |  | 
 | 231 | 	if (!bool_isvalid(booldatum)) | 
 | 232 | 		goto err; | 
 | 233 |  | 
 | 234 | 	len = le32_to_cpu(buf[2]); | 
 | 235 |  | 
 | 236 | 	key = kmalloc(len + 1, GFP_KERNEL); | 
 | 237 | 	if (!key) | 
 | 238 | 		goto err; | 
 | 239 | 	rc = next_entry(key, fp, len); | 
 | 240 | 	if (rc < 0) | 
 | 241 | 		goto err; | 
 | 242 | 	key[len] = 0; | 
 | 243 | 	if (hashtab_insert(h, key, booldatum)) | 
 | 244 | 		goto err; | 
 | 245 |  | 
 | 246 | 	return 0; | 
 | 247 | err: | 
 | 248 | 	cond_destroy_bool(key, booldatum, NULL); | 
 | 249 | 	return -1; | 
 | 250 | } | 
 | 251 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 252 | struct cond_insertf_data { | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 253 | 	struct policydb *p; | 
 | 254 | 	struct cond_av_list *other; | 
 | 255 | 	struct cond_av_list *head; | 
 | 256 | 	struct cond_av_list *tail; | 
 | 257 | }; | 
 | 258 |  | 
 | 259 | static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr) | 
 | 260 | { | 
 | 261 | 	struct cond_insertf_data *data = ptr; | 
 | 262 | 	struct policydb *p = data->p; | 
 | 263 | 	struct cond_av_list *other = data->other, *list, *cur; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 | 	struct avtab_node *node_ptr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 265 | 	u8 found; | 
 | 266 |  | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 267 |  | 
 | 268 | 	/* | 
 | 269 | 	 * For type rules we have to make certain there aren't any | 
 | 270 | 	 * conflicting rules by searching the te_avtab and the | 
 | 271 | 	 * cond_te_avtab. | 
 | 272 | 	 */ | 
 | 273 | 	if (k->specified & AVTAB_TYPE) { | 
 | 274 | 		if (avtab_search(&p->te_avtab, k)) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 275 | 			printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n"); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 276 | 			goto err; | 
 | 277 | 		} | 
 | 278 | 		/* | 
 | 279 | 		 * If we are reading the false list other will be a pointer to | 
 | 280 | 		 * the true list. We can have duplicate entries if there is only | 
 | 281 | 		 * 1 other entry and it is in our true list. | 
 | 282 | 		 * | 
 | 283 | 		 * If we are reading the true list (other == NULL) there shouldn't | 
 | 284 | 		 * be any other entries. | 
 | 285 | 		 */ | 
 | 286 | 		if (other) { | 
 | 287 | 			node_ptr = avtab_search_node(&p->te_cond_avtab, k); | 
 | 288 | 			if (node_ptr) { | 
 | 289 | 				if (avtab_search_node_next(node_ptr, k->specified)) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 290 | 					printk(KERN_ERR "SELinux: too many conflicting type rules.\n"); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 291 | 					goto err; | 
 | 292 | 				} | 
 | 293 | 				found = 0; | 
 | 294 | 				for (cur = other; cur != NULL; cur = cur->next) { | 
 | 295 | 					if (cur->node == node_ptr) { | 
 | 296 | 						found = 1; | 
 | 297 | 						break; | 
 | 298 | 					} | 
 | 299 | 				} | 
 | 300 | 				if (!found) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 301 | 					printk(KERN_ERR "SELinux: conflicting type rules.\n"); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 302 | 					goto err; | 
 | 303 | 				} | 
 | 304 | 			} | 
 | 305 | 		} else { | 
 | 306 | 			if (avtab_search(&p->te_cond_avtab, k)) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 307 | 				printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n"); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 308 | 				goto err; | 
 | 309 | 			} | 
 | 310 | 		} | 
 | 311 | 	} | 
 | 312 |  | 
 | 313 | 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); | 
 | 314 | 	if (!node_ptr) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 315 | 		printk(KERN_ERR "SELinux: could not insert rule.\n"); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 316 | 		goto err; | 
 | 317 | 	} | 
 | 318 |  | 
| James Morris | 89d155e | 2005-10-30 14:59:21 -0800 | [diff] [blame] | 319 | 	list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 320 | 	if (!list) | 
 | 321 | 		goto err; | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 322 |  | 
 | 323 | 	list->node = node_ptr; | 
 | 324 | 	if (!data->head) | 
 | 325 | 		data->head = list; | 
 | 326 | 	else | 
 | 327 | 		data->tail->next = list; | 
 | 328 | 	data->tail = list; | 
 | 329 | 	return 0; | 
 | 330 |  | 
 | 331 | err: | 
 | 332 | 	cond_av_list_destroy(data->head); | 
 | 333 | 	data->head = NULL; | 
 | 334 | 	return -1; | 
 | 335 | } | 
 | 336 |  | 
 | 337 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) | 
 | 338 | { | 
 | 339 | 	int i, rc; | 
| Alexey Dobriyan | b5bf6c5 | 2005-09-03 15:55:17 -0700 | [diff] [blame] | 340 | 	__le32 buf[1]; | 
 | 341 | 	u32 len; | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 342 | 	struct cond_insertf_data data; | 
 | 343 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 344 | 	*ret_list = NULL; | 
 | 345 |  | 
 | 346 | 	len = 0; | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 347 | 	rc = next_entry(buf, fp, sizeof(u32)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 348 | 	if (rc < 0) | 
 | 349 | 		return -1; | 
 | 350 |  | 
 | 351 | 	len = le32_to_cpu(buf[0]); | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 352 | 	if (len == 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 353 | 		return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 354 |  | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 355 | 	data.p = p; | 
 | 356 | 	data.other = other; | 
 | 357 | 	data.head = NULL; | 
 | 358 | 	data.tail = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | 	for (i = 0; i < len; i++) { | 
| Stephen Smalley | 45e5421 | 2007-11-07 10:08:00 -0500 | [diff] [blame] | 360 | 		rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf, | 
 | 361 | 				     &data); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 362 | 		if (rc) | 
 | 363 | 			return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 |  | 
 | 365 | 	} | 
 | 366 |  | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 367 | 	*ret_list = data.head; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 369 | } | 
 | 370 |  | 
 | 371 | static int expr_isvalid(struct policydb *p, struct cond_expr *expr) | 
 | 372 | { | 
 | 373 | 	if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 374 | 		printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 375 | 		return 0; | 
 | 376 | 	} | 
 | 377 |  | 
 | 378 | 	if (expr->bool > p->p_bools.nprim) { | 
| Eric Paris | 744ba35 | 2008-04-17 11:52:44 -0400 | [diff] [blame] | 379 | 		printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 380 | 		return 0; | 
 | 381 | 	} | 
 | 382 | 	return 1; | 
 | 383 | } | 
 | 384 |  | 
 | 385 | static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | 
 | 386 | { | 
| Alexey Dobriyan | b5bf6c5 | 2005-09-03 15:55:17 -0700 | [diff] [blame] | 387 | 	__le32 buf[2]; | 
 | 388 | 	u32 len, i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | 	int rc; | 
 | 390 | 	struct cond_expr *expr = NULL, *last = NULL; | 
 | 391 |  | 
 | 392 | 	rc = next_entry(buf, fp, sizeof(u32)); | 
 | 393 | 	if (rc < 0) | 
 | 394 | 		return -1; | 
 | 395 |  | 
 | 396 | 	node->cur_state = le32_to_cpu(buf[0]); | 
 | 397 |  | 
 | 398 | 	len = 0; | 
 | 399 | 	rc = next_entry(buf, fp, sizeof(u32)); | 
 | 400 | 	if (rc < 0) | 
 | 401 | 		return -1; | 
 | 402 |  | 
 | 403 | 	/* expr */ | 
 | 404 | 	len = le32_to_cpu(buf[0]); | 
 | 405 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 406 | 	for (i = 0; i < len; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 407 | 		rc = next_entry(buf, fp, sizeof(u32) * 2); | 
 | 408 | 		if (rc < 0) | 
 | 409 | 			goto err; | 
 | 410 |  | 
| James Morris | 89d155e | 2005-10-30 14:59:21 -0800 | [diff] [blame] | 411 | 		expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 412 | 		if (!expr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 413 | 			goto err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 414 |  | 
 | 415 | 		expr->expr_type = le32_to_cpu(buf[0]); | 
 | 416 | 		expr->bool = le32_to_cpu(buf[1]); | 
 | 417 |  | 
 | 418 | 		if (!expr_isvalid(p, expr)) { | 
 | 419 | 			kfree(expr); | 
 | 420 | 			goto err; | 
 | 421 | 		} | 
 | 422 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 423 | 		if (i == 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | 			node->expr = expr; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 425 | 		else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 426 | 			last->next = expr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 427 | 		last = expr; | 
 | 428 | 	} | 
 | 429 |  | 
 | 430 | 	if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | 
 | 431 | 		goto err; | 
 | 432 | 	if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | 
 | 433 | 		goto err; | 
 | 434 | 	return 0; | 
 | 435 | err: | 
 | 436 | 	cond_node_destroy(node); | 
 | 437 | 	return -1; | 
 | 438 | } | 
 | 439 |  | 
 | 440 | int cond_read_list(struct policydb *p, void *fp) | 
 | 441 | { | 
 | 442 | 	struct cond_node *node, *last = NULL; | 
| Alexey Dobriyan | b5bf6c5 | 2005-09-03 15:55:17 -0700 | [diff] [blame] | 443 | 	__le32 buf[1]; | 
 | 444 | 	u32 i, len; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 | 	int rc; | 
 | 446 |  | 
 | 447 | 	rc = next_entry(buf, fp, sizeof buf); | 
 | 448 | 	if (rc < 0) | 
 | 449 | 		return -1; | 
 | 450 |  | 
 | 451 | 	len = le32_to_cpu(buf[0]); | 
 | 452 |  | 
| Yuichi Nakamura | 3232c11 | 2007-08-24 11:55:11 +0900 | [diff] [blame] | 453 | 	rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel); | 
 | 454 | 	if (rc) | 
 | 455 | 		goto err; | 
 | 456 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 457 | 	for (i = 0; i < len; i++) { | 
| James Morris | 89d155e | 2005-10-30 14:59:21 -0800 | [diff] [blame] | 458 | 		node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 459 | 		if (!node) | 
 | 460 | 			goto err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 461 |  | 
 | 462 | 		if (cond_read_node(p, node, fp) != 0) | 
 | 463 | 			goto err; | 
 | 464 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 465 | 		if (i == 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 | 			p->cond_list = node; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 467 | 		else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 468 | 			last->next = node; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 469 | 		last = node; | 
 | 470 | 	} | 
 | 471 | 	return 0; | 
 | 472 | err: | 
 | 473 | 	cond_list_destroy(p->cond_list); | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 474 | 	p->cond_list = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 475 | 	return -1; | 
 | 476 | } | 
 | 477 |  | 
 | 478 | /* Determine whether additional permissions are granted by the conditional | 
 | 479 |  * av table, and if so, add them to the result | 
 | 480 |  */ | 
 | 481 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) | 
 | 482 | { | 
 | 483 | 	struct avtab_node *node; | 
 | 484 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 485 | 	if (!ctab || !key || !avd) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 486 | 		return; | 
 | 487 |  | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 488 | 	for (node = avtab_search_node(ctab, key); node != NULL; | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 489 | 				node = avtab_search_node_next(node, key->specified)) { | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 490 | 		if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) == | 
 | 491 | 		    (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 492 | 			avd->allowed |= node->datum.data; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 493 | 		if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) == | 
 | 494 | 		    (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 495 | 			/* Since a '0' in an auditdeny mask represents a | 
 | 496 | 			 * permission we do NOT want to audit (dontaudit), we use | 
 | 497 | 			 * the '&' operand to ensure that all '0's in the mask | 
 | 498 | 			 * are retained (much unlike the allow and auditallow cases). | 
 | 499 | 			 */ | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 500 | 			avd->auditdeny &= node->datum.data; | 
| Eric Paris | 7c2b240 | 2008-04-18 17:38:29 -0400 | [diff] [blame] | 501 | 		if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) == | 
 | 502 | 		    (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) | 
| Stephen Smalley | 782ebb9 | 2005-09-03 15:55:16 -0700 | [diff] [blame] | 503 | 			avd->auditallow |= node->datum.data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 504 | 	} | 
 | 505 | 	return; | 
 | 506 | } |