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