domain.c revision 32997144fd9925fc4d506a16990a0c405f766526
1/* 2 * security/tomoyo/domain.c 3 * 4 * Domain transition functions for TOMOYO. 5 * 6 * Copyright (C) 2005-2010 NTT DATA CORPORATION 7 */ 8 9#include "common.h" 10#include <linux/binfmts.h> 11#include <linux/slab.h> 12 13/* Variables definitions.*/ 14 15/* The global ACL referred by "use_group" keyword. */ 16struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS]; 17 18/* The initial domain. */ 19struct tomoyo_domain_info tomoyo_kernel_domain; 20 21/** 22 * tomoyo_update_policy - Update an entry for exception policy. 23 * 24 * @new_entry: Pointer to "struct tomoyo_acl_info". 25 * @size: Size of @new_entry in bytes. 26 * @param: Pointer to "struct tomoyo_acl_param". 27 * @check_duplicate: Callback function to find duplicated entry. 28 * 29 * Returns 0 on success, negative value otherwise. 30 * 31 * Caller holds tomoyo_read_lock(). 32 */ 33int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, 34 struct tomoyo_acl_param *param, 35 bool (*check_duplicate) (const struct tomoyo_acl_head 36 *, 37 const struct tomoyo_acl_head 38 *)) 39{ 40 int error = param->is_delete ? -ENOENT : -ENOMEM; 41 struct tomoyo_acl_head *entry; 42 struct list_head *list = param->list; 43 44 if (mutex_lock_interruptible(&tomoyo_policy_lock)) 45 return -ENOMEM; 46 list_for_each_entry_rcu(entry, list, list) { 47 if (!check_duplicate(entry, new_entry)) 48 continue; 49 entry->is_deleted = param->is_delete; 50 error = 0; 51 break; 52 } 53 if (error && !param->is_delete) { 54 entry = tomoyo_commit_ok(new_entry, size); 55 if (entry) { 56 list_add_tail_rcu(&entry->list, list); 57 error = 0; 58 } 59 } 60 mutex_unlock(&tomoyo_policy_lock); 61 return error; 62} 63 64/** 65 * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry. 66 * 67 * @a: Pointer to "struct tomoyo_acl_info". 68 * @b: Pointer to "struct tomoyo_acl_info". 69 * 70 * Returns true if @a == @b, false otherwise. 71 */ 72static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a, 73 const struct tomoyo_acl_info *b) 74{ 75 return a->type == b->type; 76} 77 78/** 79 * tomoyo_update_domain - Update an entry for domain policy. 80 * 81 * @new_entry: Pointer to "struct tomoyo_acl_info". 82 * @size: Size of @new_entry in bytes. 83 * @param: Pointer to "struct tomoyo_acl_param". 84 * @check_duplicate: Callback function to find duplicated entry. 85 * @merge_duplicate: Callback function to merge duplicated entry. 86 * 87 * Returns 0 on success, negative value otherwise. 88 * 89 * Caller holds tomoyo_read_lock(). 90 */ 91int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, 92 struct tomoyo_acl_param *param, 93 bool (*check_duplicate) (const struct tomoyo_acl_info 94 *, 95 const struct tomoyo_acl_info 96 *), 97 bool (*merge_duplicate) (struct tomoyo_acl_info *, 98 struct tomoyo_acl_info *, 99 const bool)) 100{ 101 const bool is_delete = param->is_delete; 102 int error = is_delete ? -ENOENT : -ENOMEM; 103 struct tomoyo_acl_info *entry; 104 struct list_head * const list = param->list; 105 106 if (mutex_lock_interruptible(&tomoyo_policy_lock)) 107 return error; 108 list_for_each_entry_rcu(entry, list, list) { 109 if (!tomoyo_same_acl_head(entry, new_entry) || 110 !check_duplicate(entry, new_entry)) 111 continue; 112 if (merge_duplicate) 113 entry->is_deleted = merge_duplicate(entry, new_entry, 114 is_delete); 115 else 116 entry->is_deleted = is_delete; 117 error = 0; 118 break; 119 } 120 if (error && !is_delete) { 121 entry = tomoyo_commit_ok(new_entry, size); 122 if (entry) { 123 list_add_tail_rcu(&entry->list, list); 124 error = 0; 125 } 126 } 127 mutex_unlock(&tomoyo_policy_lock); 128 return error; 129} 130 131/** 132 * tomoyo_check_acl - Do permission check. 133 * 134 * @r: Pointer to "struct tomoyo_request_info". 135 * @check_entry: Callback function to check type specific parameters. 136 * 137 * Returns 0 on success, negative value otherwise. 138 * 139 * Caller holds tomoyo_read_lock(). 140 */ 141void tomoyo_check_acl(struct tomoyo_request_info *r, 142 bool (*check_entry) (struct tomoyo_request_info *, 143 const struct tomoyo_acl_info *)) 144{ 145 const struct tomoyo_domain_info *domain = r->domain; 146 struct tomoyo_acl_info *ptr; 147 bool retried = false; 148 const struct list_head *list = &domain->acl_info_list; 149 150retry: 151 list_for_each_entry_rcu(ptr, list, list) { 152 if (ptr->is_deleted || ptr->type != r->param_type) 153 continue; 154 if (check_entry(r, ptr)) { 155 r->granted = true; 156 return; 157 } 158 } 159 if (!retried) { 160 retried = true; 161 list = &tomoyo_acl_group[domain->group]; 162 goto retry; 163 } 164 r->granted = false; 165} 166 167/* The list for "struct tomoyo_domain_info". */ 168LIST_HEAD(tomoyo_domain_list); 169 170struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; 171struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; 172 173/** 174 * tomoyo_last_word - Get last component of a domainname. 175 * 176 * @domainname: Domainname to check. 177 * 178 * Returns the last word of @domainname. 179 */ 180static const char *tomoyo_last_word(const char *name) 181{ 182 const char *cp = strrchr(name, ' '); 183 if (cp) 184 return cp + 1; 185 return name; 186} 187 188/** 189 * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry. 190 * 191 * @a: Pointer to "struct tomoyo_acl_head". 192 * @b: Pointer to "struct tomoyo_acl_head". 193 * 194 * Returns true if @a == @b, false otherwise. 195 */ 196static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, 197 const struct tomoyo_acl_head *b) 198{ 199 const struct tomoyo_transition_control *p1 = container_of(a, 200 typeof(*p1), 201 head); 202 const struct tomoyo_transition_control *p2 = container_of(b, 203 typeof(*p2), 204 head); 205 return p1->type == p2->type && p1->is_last_name == p2->is_last_name 206 && p1->domainname == p2->domainname 207 && p1->program == p2->program; 208} 209 210/** 211 * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. 212 * 213 * @param: Pointer to "struct tomoyo_acl_param". 214 * @type: Type of this entry. 215 * 216 * Returns 0 on success, negative value otherwise. 217 */ 218int tomoyo_write_transition_control(struct tomoyo_acl_param *param, 219 const u8 type) 220{ 221 struct tomoyo_transition_control e = { .type = type }; 222 int error = param->is_delete ? -ENOENT : -ENOMEM; 223 char *program = param->data; 224 char *domainname = strstr(program, " from "); 225 if (domainname) { 226 *domainname = '\0'; 227 domainname += 6; 228 } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || 229 type == TOMOYO_TRANSITION_CONTROL_KEEP) { 230 domainname = program; 231 program = NULL; 232 } 233 if (program && strcmp(program, "any")) { 234 if (!tomoyo_correct_path(program)) 235 return -EINVAL; 236 e.program = tomoyo_get_name(program); 237 if (!e.program) 238 goto out; 239 } 240 if (domainname && strcmp(domainname, "any")) { 241 if (!tomoyo_correct_domain(domainname)) { 242 if (!tomoyo_correct_path(domainname)) 243 goto out; 244 e.is_last_name = true; 245 } 246 e.domainname = tomoyo_get_name(domainname); 247 if (!e.domainname) 248 goto out; 249 } 250 param->list = &tomoyo_policy_list[TOMOYO_ID_TRANSITION_CONTROL]; 251 error = tomoyo_update_policy(&e.head, sizeof(e), param, 252 tomoyo_same_transition_control); 253out: 254 tomoyo_put_name(e.domainname); 255 tomoyo_put_name(e.program); 256 return error; 257} 258 259/** 260 * tomoyo_transition_type - Get domain transition type. 261 * 262 * @domainname: The name of domain. 263 * @program: The name of program. 264 * 265 * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program 266 * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing 267 * @program suppresses domain transition, others otherwise. 268 * 269 * Caller holds tomoyo_read_lock(). 270 */ 271static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, 272 const struct tomoyo_path_info *program) 273{ 274 const struct tomoyo_transition_control *ptr; 275 const char *last_name = tomoyo_last_word(domainname->name); 276 u8 type; 277 for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { 278 next: 279 list_for_each_entry_rcu(ptr, &tomoyo_policy_list 280 [TOMOYO_ID_TRANSITION_CONTROL], 281 head.list) { 282 if (ptr->head.is_deleted || ptr->type != type) 283 continue; 284 if (ptr->domainname) { 285 if (!ptr->is_last_name) { 286 if (ptr->domainname != domainname) 287 continue; 288 } else { 289 /* 290 * Use direct strcmp() since this is 291 * unlikely used. 292 */ 293 if (strcmp(ptr->domainname->name, 294 last_name)) 295 continue; 296 } 297 } 298 if (ptr->program && 299 tomoyo_pathcmp(ptr->program, program)) 300 continue; 301 if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { 302 /* 303 * Do not check for initialize_domain if 304 * no_initialize_domain matched. 305 */ 306 type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; 307 goto next; 308 } 309 goto done; 310 } 311 } 312 done: 313 return type; 314} 315 316/** 317 * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry. 318 * 319 * @a: Pointer to "struct tomoyo_acl_head". 320 * @b: Pointer to "struct tomoyo_acl_head". 321 * 322 * Returns true if @a == @b, false otherwise. 323 */ 324static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, 325 const struct tomoyo_acl_head *b) 326{ 327 const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), 328 head); 329 const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), 330 head); 331 return p1->original_name == p2->original_name && 332 p1->aggregated_name == p2->aggregated_name; 333} 334 335/** 336 * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. 337 * 338 * @param: Pointer to "struct tomoyo_acl_param". 339 * 340 * Returns 0 on success, negative value otherwise. 341 * 342 * Caller holds tomoyo_read_lock(). 343 */ 344int tomoyo_write_aggregator(struct tomoyo_acl_param *param) 345{ 346 struct tomoyo_aggregator e = { }; 347 int error = param->is_delete ? -ENOENT : -ENOMEM; 348 const char *original_name = tomoyo_read_token(param); 349 const char *aggregated_name = tomoyo_read_token(param); 350 if (!tomoyo_correct_word(original_name) || 351 !tomoyo_correct_path(aggregated_name)) 352 return -EINVAL; 353 e.original_name = tomoyo_get_name(original_name); 354 e.aggregated_name = tomoyo_get_name(aggregated_name); 355 if (!e.original_name || !e.aggregated_name || 356 e.aggregated_name->is_patterned) /* No patterns allowed. */ 357 goto out; 358 param->list = &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR]; 359 error = tomoyo_update_policy(&e.head, sizeof(e), param, 360 tomoyo_same_aggregator); 361out: 362 tomoyo_put_name(e.original_name); 363 tomoyo_put_name(e.aggregated_name); 364 return error; 365} 366 367/** 368 * tomoyo_assign_domain - Create a domain. 369 * 370 * @domainname: The name of domain. 371 * @profile: Profile number to assign if the domain was newly created. 372 * 373 * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. 374 * 375 * Caller holds tomoyo_read_lock(). 376 */ 377struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, 378 const u8 profile) 379{ 380 struct tomoyo_domain_info *entry; 381 struct tomoyo_domain_info *domain = NULL; 382 const struct tomoyo_path_info *saved_domainname; 383 bool found = false; 384 385 if (!tomoyo_correct_domain(domainname)) 386 return NULL; 387 saved_domainname = tomoyo_get_name(domainname); 388 if (!saved_domainname) 389 return NULL; 390 entry = kzalloc(sizeof(*entry), GFP_NOFS); 391 if (mutex_lock_interruptible(&tomoyo_policy_lock)) 392 goto out; 393 list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { 394 if (domain->is_deleted || 395 tomoyo_pathcmp(saved_domainname, domain->domainname)) 396 continue; 397 found = true; 398 break; 399 } 400 if (!found && tomoyo_memory_ok(entry)) { 401 INIT_LIST_HEAD(&entry->acl_info_list); 402 entry->domainname = saved_domainname; 403 saved_domainname = NULL; 404 entry->profile = profile; 405 list_add_tail_rcu(&entry->list, &tomoyo_domain_list); 406 domain = entry; 407 entry = NULL; 408 found = true; 409 } 410 mutex_unlock(&tomoyo_policy_lock); 411 out: 412 tomoyo_put_name(saved_domainname); 413 kfree(entry); 414 return found ? domain : NULL; 415} 416 417/** 418 * tomoyo_find_next_domain - Find a domain. 419 * 420 * @bprm: Pointer to "struct linux_binprm". 421 * 422 * Returns 0 on success, negative value otherwise. 423 * 424 * Caller holds tomoyo_read_lock(). 425 */ 426int tomoyo_find_next_domain(struct linux_binprm *bprm) 427{ 428 struct tomoyo_request_info r; 429 char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); 430 struct tomoyo_domain_info *old_domain = tomoyo_domain(); 431 struct tomoyo_domain_info *domain = NULL; 432 const char *original_name = bprm->filename; 433 u8 mode; 434 bool is_enforce; 435 int retval = -ENOMEM; 436 bool need_kfree = false; 437 struct tomoyo_path_info rn = { }; /* real name */ 438 439 mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); 440 is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); 441 if (!tmp) 442 goto out; 443 444 retry: 445 if (need_kfree) { 446 kfree(rn.name); 447 need_kfree = false; 448 } 449 /* Get symlink's pathname of program. */ 450 retval = -ENOENT; 451 rn.name = tomoyo_realpath_nofollow(original_name); 452 if (!rn.name) 453 goto out; 454 tomoyo_fill_path_info(&rn); 455 need_kfree = true; 456 457 /* Check 'aggregator' directive. */ 458 { 459 struct tomoyo_aggregator *ptr; 460 list_for_each_entry_rcu(ptr, &tomoyo_policy_list 461 [TOMOYO_ID_AGGREGATOR], head.list) { 462 if (ptr->head.is_deleted || 463 !tomoyo_path_matches_pattern(&rn, 464 ptr->original_name)) 465 continue; 466 kfree(rn.name); 467 need_kfree = false; 468 /* This is OK because it is read only. */ 469 rn = *ptr->aggregated_name; 470 break; 471 } 472 } 473 474 /* Check execute permission. */ 475 retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); 476 if (retval == TOMOYO_RETRY_REQUEST) 477 goto retry; 478 if (retval < 0) 479 goto out; 480 /* 481 * To be able to specify domainnames with wildcards, use the 482 * pathname specified in the policy (which may contain 483 * wildcard) rather than the pathname passed to execve() 484 * (which never contains wildcard). 485 */ 486 if (r.param.path.matched_path) { 487 if (need_kfree) 488 kfree(rn.name); 489 need_kfree = false; 490 /* This is OK because it is read only. */ 491 rn = *r.param.path.matched_path; 492 } 493 494 /* Calculate domain to transit to. */ 495 switch (tomoyo_transition_type(old_domain->domainname, &rn)) { 496 case TOMOYO_TRANSITION_CONTROL_INITIALIZE: 497 /* Transit to the child of tomoyo_kernel_domain domain. */ 498 snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " 499 "%s", rn.name); 500 break; 501 case TOMOYO_TRANSITION_CONTROL_KEEP: 502 /* Keep current domain. */ 503 domain = old_domain; 504 break; 505 default: 506 if (old_domain == &tomoyo_kernel_domain && 507 !tomoyo_policy_loaded) { 508 /* 509 * Needn't to transit from kernel domain before 510 * starting /sbin/init. But transit from kernel domain 511 * if executing initializers because they might start 512 * before /sbin/init. 513 */ 514 domain = old_domain; 515 } else { 516 /* Normal domain transition. */ 517 snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", 518 old_domain->domainname->name, rn.name); 519 } 520 break; 521 } 522 if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) 523 goto done; 524 domain = tomoyo_find_domain(tmp); 525 if (!domain) 526 domain = tomoyo_assign_domain(tmp, old_domain->profile); 527 done: 528 if (domain) 529 goto out; 530 printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); 531 if (is_enforce) 532 retval = -EPERM; 533 else 534 old_domain->transition_failed = true; 535 out: 536 if (!domain) 537 domain = old_domain; 538 /* Update reference count on "struct tomoyo_domain_info". */ 539 atomic_inc(&domain->users); 540 bprm->cred->security = domain; 541 if (need_kfree) 542 kfree(rn.name); 543 kfree(tmp); 544 return retval; 545} 546