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