Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
domain.c
Go to the documentation of this file.
1 /*
2  * security/tomoyo/domain.c
3  *
4  * Copyright (C) 2005-2011 NTT DATA CORPORATION
5  */
6 
7 #include "common.h"
8 #include <linux/binfmts.h>
9 #include <linux/slab.h>
10 
11 /* Variables definitions.*/
12 
13 /* The initial domain. */
15 
28 int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
29  struct tomoyo_acl_param *param,
30  bool (*check_duplicate) (const struct tomoyo_acl_head
31  *,
32  const struct tomoyo_acl_head
33  *))
34 {
35  int error = param->is_delete ? -ENOENT : -ENOMEM;
36  struct tomoyo_acl_head *entry;
37  struct list_head *list = param->list;
38 
40  return -ENOMEM;
41  list_for_each_entry_rcu(entry, list, list) {
42  if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
43  continue;
44  if (!check_duplicate(entry, new_entry))
45  continue;
46  entry->is_deleted = param->is_delete;
47  error = 0;
48  break;
49  }
50  if (error && !param->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  }
58  return error;
59 }
60 
69 static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
70  const struct tomoyo_acl_info *b)
71 {
72  return a->type == b->type && a->cond == b->cond;
73 }
74 
88 int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
89  struct tomoyo_acl_param *param,
90  bool (*check_duplicate) (const struct tomoyo_acl_info
91  *,
92  const struct tomoyo_acl_info
93  *),
94  bool (*merge_duplicate) (struct tomoyo_acl_info *,
95  struct tomoyo_acl_info *,
96  const bool))
97 {
98  const bool is_delete = param->is_delete;
99  int error = is_delete ? -ENOENT : -ENOMEM;
100  struct tomoyo_acl_info *entry;
101  struct list_head * const list = param->list;
102 
103  if (param->data[0]) {
104  new_entry->cond = tomoyo_get_condition(param);
105  if (!new_entry->cond)
106  return -EINVAL;
107  /*
108  * Domain transition preference is allowed for only
109  * "file execute" entries.
110  */
111  if (new_entry->cond->transit &&
112  !(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
113  container_of(new_entry, struct tomoyo_path_acl, head)
114  ->perm == 1 << TOMOYO_TYPE_EXECUTE))
115  goto out;
116  }
118  goto out;
119  list_for_each_entry_rcu(entry, list, list) {
120  if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
121  continue;
122  if (!tomoyo_same_acl_head(entry, new_entry) ||
123  !check_duplicate(entry, new_entry))
124  continue;
125  if (merge_duplicate)
126  entry->is_deleted = merge_duplicate(entry, new_entry,
127  is_delete);
128  else
129  entry->is_deleted = is_delete;
130  error = 0;
131  break;
132  }
133  if (error && !is_delete) {
134  entry = tomoyo_commit_ok(new_entry, size);
135  if (entry) {
136  list_add_tail_rcu(&entry->list, list);
137  error = 0;
138  }
139  }
141 out:
142  tomoyo_put_condition(new_entry->cond);
143  return error;
144 }
145 
157  bool (*check_entry) (struct tomoyo_request_info *,
158  const struct tomoyo_acl_info *))
159 {
160  const struct tomoyo_domain_info *domain = r->domain;
161  struct tomoyo_acl_info *ptr;
162  bool retried = false;
163  const struct list_head *list = &domain->acl_info_list;
164 
165 retry:
166  list_for_each_entry_rcu(ptr, list, list) {
167  if (ptr->is_deleted || ptr->type != r->param_type)
168  continue;
169  if (!check_entry(r, ptr))
170  continue;
171  if (!tomoyo_condition(r, ptr->cond))
172  continue;
173  r->matched_acl = ptr;
174  r->granted = true;
175  return;
176  }
177  if (!retried) {
178  retried = true;
179  list = &domain->ns->acl_group[domain->group];
180  goto retry;
181  }
182  r->granted = false;
183 }
184 
185 /* The list for "struct tomoyo_domain_info". */
186 LIST_HEAD(tomoyo_domain_list);
187 
195 static const char *tomoyo_last_word(const char *name)
196 {
197  const char *cp = strrchr(name, ' ');
198  if (cp)
199  return cp + 1;
200  return name;
201 }
202 
211 static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
212  const struct tomoyo_acl_head *b)
213 {
214  const struct tomoyo_transition_control *p1 = container_of(a,
215  typeof(*p1),
216  head);
217  const struct tomoyo_transition_control *p2 = container_of(b,
218  typeof(*p2),
219  head);
220  return p1->type == p2->type && p1->is_last_name == p2->is_last_name
221  && p1->domainname == p2->domainname
222  && p1->program == p2->program;
223 }
224 
234  const u8 type)
235 {
236  struct tomoyo_transition_control e = { .type = type };
237  int error = param->is_delete ? -ENOENT : -ENOMEM;
238  char *program = param->data;
239  char *domainname = strstr(program, " from ");
240  if (domainname) {
241  *domainname = '\0';
242  domainname += 6;
243  } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
245  domainname = program;
246  program = NULL;
247  }
248  if (program && strcmp(program, "any")) {
249  if (!tomoyo_correct_path(program))
250  return -EINVAL;
251  e.program = tomoyo_get_name(program);
252  if (!e.program)
253  goto out;
254  }
255  if (domainname && strcmp(domainname, "any")) {
256  if (!tomoyo_correct_domain(domainname)) {
257  if (!tomoyo_correct_path(domainname))
258  goto out;
259  e.is_last_name = true;
260  }
261  e.domainname = tomoyo_get_name(domainname);
262  if (!e.domainname)
263  goto out;
264  }
265  param->list = &param->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
266  error = tomoyo_update_policy(&e.head, sizeof(e), param,
267  tomoyo_same_transition_control);
268 out:
269  tomoyo_put_name(e.domainname);
270  tomoyo_put_name(e.program);
271  return error;
272 }
273 
287 static inline bool tomoyo_scan_transition
288 (const struct list_head *list, const struct tomoyo_path_info *domainname,
289  const struct tomoyo_path_info *program, const char *last_name,
290  const enum tomoyo_transition_type type)
291 {
292  const struct tomoyo_transition_control *ptr;
293  list_for_each_entry_rcu(ptr, list, head.list) {
294  if (ptr->head.is_deleted || ptr->type != type)
295  continue;
296  if (ptr->domainname) {
297  if (!ptr->is_last_name) {
298  if (ptr->domainname != domainname)
299  continue;
300  } else {
301  /*
302  * Use direct strcmp() since this is
303  * unlikely used.
304  */
305  if (strcmp(ptr->domainname->name, last_name))
306  continue;
307  }
308  }
309  if (ptr->program && tomoyo_pathcmp(ptr->program, program))
310  continue;
311  return true;
312  }
313  return false;
314 }
315 
332 (const struct tomoyo_policy_namespace *ns,
333  const struct tomoyo_path_info *domainname,
334  const struct tomoyo_path_info *program)
335 {
336  const char *last_name = tomoyo_last_word(domainname->name);
338  while (type < TOMOYO_MAX_TRANSITION_TYPE) {
339  const struct list_head * const list =
341  if (!tomoyo_scan_transition(list, domainname, program,
342  last_name, type)) {
343  type++;
344  continue;
345  }
348  break;
349  /*
350  * Do not check for reset_domain if no_reset_domain matched.
351  * Do not check for initialize_domain if no_initialize_domain
352  * matched.
353  */
354  type++;
355  type++;
356  }
357  return type;
358 }
359 
368 static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
369  const struct tomoyo_acl_head *b)
370 {
371  const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
372  head);
373  const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
374  head);
375  return p1->original_name == p2->original_name &&
376  p1->aggregated_name == p2->aggregated_name;
377 }
378 
389 {
390  struct tomoyo_aggregator e = { };
391  int error = param->is_delete ? -ENOENT : -ENOMEM;
392  const char *original_name = tomoyo_read_token(param);
393  const char *aggregated_name = tomoyo_read_token(param);
394  if (!tomoyo_correct_word(original_name) ||
395  !tomoyo_correct_path(aggregated_name))
396  return -EINVAL;
397  e.original_name = tomoyo_get_name(original_name);
398  e.aggregated_name = tomoyo_get_name(aggregated_name);
399  if (!e.original_name || !e.aggregated_name ||
400  e.aggregated_name->is_patterned) /* No patterns allowed. */
401  goto out;
402  param->list = &param->ns->policy_list[TOMOYO_ID_AGGREGATOR];
403  error = tomoyo_update_policy(&e.head, sizeof(e), param,
404  tomoyo_same_aggregator);
405 out:
406  tomoyo_put_name(e.original_name);
407  tomoyo_put_name(e.aggregated_name);
408  return error;
409 }
410 
422 static struct tomoyo_policy_namespace *tomoyo_find_namespace
423 (const char *name, const unsigned int len)
424 {
425  struct tomoyo_policy_namespace *ns;
426  list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
427  if (strncmp(name, ns->name, len) ||
428  (name[len] && name[len] != ' '))
429  continue;
430  return ns;
431  }
432  return NULL;
433 }
434 
445 struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
446 {
449  const char *cp = domainname;
450  unsigned int len = 0;
451  while (*cp && *cp++ != ' ')
452  len++;
453  ptr = tomoyo_find_namespace(domainname, len);
454  if (ptr)
455  return ptr;
456  if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname))
457  return NULL;
458  entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS);
459  if (!entry)
460  return NULL;
462  goto out;
463  ptr = tomoyo_find_namespace(domainname, len);
464  if (!ptr && tomoyo_memory_ok(entry)) {
465  char *name = (char *) (entry + 1);
466  ptr = entry;
467  memmove(name, domainname, len);
468  name[len] = '\0';
469  entry->name = name;
471  entry = NULL;
472  }
474 out:
475  kfree(entry);
476  return ptr;
477 }
478 
486 static bool tomoyo_namespace_jump(const char *domainname)
487 {
488  const char *namespace = tomoyo_current_namespace()->name;
489  const int len = strlen(namespace);
490  return strncmp(domainname, namespace, len) ||
491  (domainname[len] && domainname[len] != ' ');
492 }
493 
504 struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
505  const bool transit)
506 {
507  struct tomoyo_domain_info e = { };
508  struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
509  bool created = false;
510  if (entry) {
511  if (transit) {
512  /*
513  * Since namespace is created at runtime, profiles may
514  * not be created by the moment the process transits to
515  * that domain. Do not perform domain transition if
516  * profile for that domain is not yet created.
517  */
518  if (tomoyo_policy_loaded &&
519  !entry->ns->profile_ptr[entry->profile])
520  return NULL;
521  }
522  return entry;
523  }
524  /* Requested domain does not exist. */
525  /* Don't create requested domain if domainname is invalid. */
526  if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 ||
527  !tomoyo_correct_domain(domainname))
528  return NULL;
529  /*
530  * Since definition of profiles and acl_groups may differ across
531  * namespaces, do not inherit "use_profile" and "use_group" settings
532  * by automatically creating requested domain upon domain transition.
533  */
534  if (transit && tomoyo_namespace_jump(domainname))
535  return NULL;
536  e.ns = tomoyo_assign_namespace(domainname);
537  if (!e.ns)
538  return NULL;
539  /*
540  * "use_profile" and "use_group" settings for automatically created
541  * domains are inherited from current domain. These are 0 for manually
542  * created domains.
543  */
544  if (transit) {
545  const struct tomoyo_domain_info *domain = tomoyo_domain();
546  e.profile = domain->profile;
547  e.group = domain->group;
548  }
549  e.domainname = tomoyo_get_name(domainname);
550  if (!e.domainname)
551  return NULL;
553  goto out;
554  entry = tomoyo_find_domain(domainname);
555  if (!entry) {
556  entry = tomoyo_commit_ok(&e, sizeof(e));
557  if (entry) {
558  INIT_LIST_HEAD(&entry->acl_info_list);
559  list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
560  created = true;
561  }
562  }
564 out:
565  tomoyo_put_name(e.domainname);
566  if (entry && transit) {
567  if (created) {
568  struct tomoyo_request_info r;
569  tomoyo_init_request_info(&r, entry,
571  r.granted = false;
572  tomoyo_write_log(&r, "use_profile %u\n",
573  entry->profile);
574  tomoyo_write_log(&r, "use_group %u\n", entry->group);
576  }
577  }
578  return entry;
579 }
580 
588 static int tomoyo_environ(struct tomoyo_execve *ee)
589 {
590  struct tomoyo_request_info *r = &ee->r;
591  struct linux_binprm *bprm = ee->bprm;
592  /* env_page.data is allocated by tomoyo_dump_page(). */
593  struct tomoyo_page_dump env_page = { };
594  char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
595  int arg_len = 0;
596  unsigned long pos = bprm->p;
597  int offset = pos % PAGE_SIZE;
598  int argv_count = bprm->argc;
599  int envp_count = bprm->envc;
600  int error = -ENOMEM;
601 
602  ee->r.type = TOMOYO_MAC_ENVIRON;
603  ee->r.profile = r->domain->profile;
604  ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
606  if (!r->mode || !envp_count)
607  return 0;
608  arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
609  if (!arg_ptr)
610  goto out;
611  while (error == -ENOMEM) {
612  if (!tomoyo_dump_page(bprm, pos, &env_page))
613  goto out;
614  pos += PAGE_SIZE - offset;
615  /* Read. */
616  while (argv_count && offset < PAGE_SIZE) {
617  if (!env_page.data[offset++])
618  argv_count--;
619  }
620  if (argv_count) {
621  offset = 0;
622  continue;
623  }
624  while (offset < PAGE_SIZE) {
625  const unsigned char c = env_page.data[offset++];
626 
627  if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
628  if (c == '=') {
629  arg_ptr[arg_len++] = '\0';
630  } else if (c == '\\') {
631  arg_ptr[arg_len++] = '\\';
632  arg_ptr[arg_len++] = '\\';
633  } else if (c > ' ' && c < 127) {
634  arg_ptr[arg_len++] = c;
635  } else {
636  arg_ptr[arg_len++] = '\\';
637  arg_ptr[arg_len++] = (c >> 6) + '0';
638  arg_ptr[arg_len++]
639  = ((c >> 3) & 7) + '0';
640  arg_ptr[arg_len++] = (c & 7) + '0';
641  }
642  } else {
643  arg_ptr[arg_len] = '\0';
644  }
645  if (c)
646  continue;
647  if (tomoyo_env_perm(r, arg_ptr)) {
648  error = -EPERM;
649  break;
650  }
651  if (!--envp_count) {
652  error = 0;
653  break;
654  }
655  arg_len = 0;
656  }
657  offset = 0;
658  }
659 out:
660  if (r->mode != TOMOYO_CONFIG_ENFORCING)
661  error = 0;
662  kfree(env_page.data);
663  kfree(arg_ptr);
664  return error;
665 }
666 
677 {
678  struct tomoyo_domain_info *old_domain = tomoyo_domain();
679  struct tomoyo_domain_info *domain = NULL;
680  const char *original_name = bprm->filename;
681  int retval = -ENOMEM;
682  bool reject_on_transition_failure = false;
683  const struct tomoyo_path_info *candidate;
684  struct tomoyo_path_info exename;
685  struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
686 
687  if (!ee)
688  return -ENOMEM;
689  ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
690  if (!ee->tmp) {
691  kfree(ee);
692  return -ENOMEM;
693  }
694  /* ee->dump->data is allocated by tomoyo_dump_page(). */
696  ee->r.ee = ee;
697  ee->bprm = bprm;
698  ee->r.obj = &ee->obj;
699  ee->obj.path1 = bprm->file->f_path;
700  /* Get symlink's pathname of program. */
701  retval = -ENOENT;
702  exename.name = tomoyo_realpath_nofollow(original_name);
703  if (!exename.name)
704  goto out;
705  tomoyo_fill_path_info(&exename);
706 retry:
707  /* Check 'aggregator' directive. */
708  {
709  struct tomoyo_aggregator *ptr;
710  struct list_head *list =
711  &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
712  /* Check 'aggregator' directive. */
713  candidate = &exename;
714  list_for_each_entry_rcu(ptr, list, head.list) {
715  if (ptr->head.is_deleted ||
716  !tomoyo_path_matches_pattern(&exename,
717  ptr->original_name))
718  continue;
719  candidate = ptr->aggregated_name;
720  break;
721  }
722  }
723 
724  /* Check execute permission. */
725  retval = tomoyo_execute_permission(&ee->r, candidate);
726  if (retval == TOMOYO_RETRY_REQUEST)
727  goto retry;
728  if (retval < 0)
729  goto out;
730  /*
731  * To be able to specify domainnames with wildcards, use the
732  * pathname specified in the policy (which may contain
733  * wildcard) rather than the pathname passed to execve()
734  * (which never contains wildcard).
735  */
736  if (ee->r.param.path.matched_path)
737  candidate = ee->r.param.path.matched_path;
738 
739  /*
740  * Check for domain transition preference if "file execute" matched.
741  * If preference is given, make do_execve() fail if domain transition
742  * has failed, for domain transition preference should be used with
743  * destination domain defined.
744  */
745  if (ee->transition) {
746  const char *domainname = ee->transition->name;
747  reject_on_transition_failure = true;
748  if (!strcmp(domainname, "keep"))
749  goto force_keep_domain;
750  if (!strcmp(domainname, "child"))
751  goto force_child_domain;
752  if (!strcmp(domainname, "reset"))
753  goto force_reset_domain;
754  if (!strcmp(domainname, "initialize"))
755  goto force_initialize_domain;
756  if (!strcmp(domainname, "parent")) {
757  char *cp;
758  strncpy(ee->tmp, old_domain->domainname->name,
759  TOMOYO_EXEC_TMPSIZE - 1);
760  cp = strrchr(ee->tmp, ' ');
761  if (cp)
762  *cp = '\0';
763  } else if (*domainname == '<')
764  strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
765  else
766  snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
767  old_domain->domainname->name, domainname);
768  goto force_jump_domain;
769  }
770  /*
771  * No domain transition preference specified.
772  * Calculate domain to transit to.
773  */
774  switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
775  candidate)) {
777 force_reset_domain:
778  /* Transit to the root of specified namespace. */
779  snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
780  candidate->name);
781  /*
782  * Make do_execve() fail if domain transition across namespaces
783  * has failed.
784  */
785  reject_on_transition_failure = true;
786  break;
788 force_initialize_domain:
789  /* Transit to the child of current namespace's root. */
790  snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
791  old_domain->ns->name, candidate->name);
792  break;
794 force_keep_domain:
795  /* Keep current domain. */
796  domain = old_domain;
797  break;
798  default:
799  if (old_domain == &tomoyo_kernel_domain &&
801  /*
802  * Needn't to transit from kernel domain before
803  * starting /sbin/init. But transit from kernel domain
804  * if executing initializers because they might start
805  * before /sbin/init.
806  */
807  domain = old_domain;
808  break;
809  }
810 force_child_domain:
811  /* Normal domain transition. */
812  snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
813  old_domain->domainname->name, candidate->name);
814  break;
815  }
816 force_jump_domain:
817  if (!domain)
818  domain = tomoyo_assign_domain(ee->tmp, true);
819  if (domain)
820  retval = 0;
821  else if (reject_on_transition_failure) {
822  printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
823  ee->tmp);
824  retval = -ENOMEM;
825  } else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
826  retval = -ENOMEM;
827  else {
828  retval = 0;
829  if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
830  old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
831  ee->r.granted = false;
832  tomoyo_write_log(&ee->r, "%s", tomoyo_dif
835  "ERROR: Domain '%s' not defined.\n", ee->tmp);
836  }
837  }
838  out:
839  if (!domain)
840  domain = old_domain;
841  /* Update reference count on "struct tomoyo_domain_info". */
842  atomic_inc(&domain->users);
843  bprm->cred->security = domain;
844  kfree(exename.name);
845  if (!retval) {
846  ee->r.domain = domain;
847  retval = tomoyo_environ(ee);
848  }
849  kfree(ee->tmp);
850  kfree(ee->dump.data);
851  kfree(ee);
852  return retval;
853 }
854 
864 bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
865  struct tomoyo_page_dump *dump)
866 {
867  struct page *page;
868 
869  /* dump->data is released by tomoyo_find_next_domain(). */
870  if (!dump->data) {
871  dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
872  if (!dump->data)
873  return false;
874  }
875  /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
876 #ifdef CONFIG_MMU
877  if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
878  return false;
879 #else
880  page = bprm->page[pos / PAGE_SIZE];
881 #endif
882  if (page != dump->page) {
883  const unsigned int offset = pos % PAGE_SIZE;
884  /*
885  * Maybe kmap()/kunmap() should be used here.
886  * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
887  * So do I.
888  */
889  char *kaddr = kmap_atomic(page);
890 
891  dump->page = page;
892  memcpy(dump->data + offset, kaddr + offset,
893  PAGE_SIZE - offset);
894  kunmap_atomic(kaddr);
895  }
896  /* Same with put_arg_page(page) in fs/exec.c */
897 #ifdef CONFIG_MMU
898  put_page(page);
899 #endif
900  return true;
901 }