オープンソース・ソフトウェアの開発とダウンロード

Subversion リポジトリの参照

Diff of /trunk/1.8.x/ccs-patch/security/ccsecurity/domain.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/1.6.x/ccs-patch/fs/tomoyo_domain.c revision 1700 by kumaneko, Mon Oct 13 04:57:50 2008 UTC trunk/1.8.x/ccs-patch/security/ccsecurity/domain.c revision 3968 by kumaneko, Wed Sep 8 05:42:22 2010 UTC
# Line 1  Line 1 
1  /*  /*
2   * fs/tomoyo_domain.c   * security/ccsecurity/domain.c
3   *   *
4   * Implementation of the Domain-Based Mandatory Access Control.   * Copyright (C) 2005-2010  NTT DATA CORPORATION
5   *   *
6   * Copyright (C) 2005-2008  NTT DATA CORPORATION   * Version: 1.8.0-pre   2010/09/01
  *  
  * Version: 1.6.5-pre   2008/10/11  
7   *   *
8   * This file is applicable to both 2.4.30 and 2.6.11 and later.   * This file is applicable to both 2.4.30 and 2.6.11 and later.
9   * See README.ccs for ChangeLog.   * See README.ccs for ChangeLog.
10   *   *
11   */   */
12    
13  #include <linux/ccs_common.h>  #include <linux/slab.h>
 #include <linux/tomoyo.h>  
 #include <linux/realpath.h>  
14  #include <linux/highmem.h>  #include <linux/highmem.h>
15  #include <linux/binfmts.h>  #include <linux/version.h>
16  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
17  #include <linux/namei.h>  #include <linux/namei.h>
18  #include <linux/mount.h>  #include <linux/mount.h>
19  #endif  #endif
20    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21  /* For compatibility with older kernels. */  #include <linux/fs_struct.h>
 #ifndef for_each_process  
 #define for_each_process for_each_task  
22  #endif  #endif
23    #include "internal.h"
24    
25  /* Variables definitions.*/  /* Variables definitions.*/
26    
27  /* The initial domain. */  /* The global domain. */
28  struct domain_info KERNEL_DOMAIN;  struct ccs_domain_info ccs_acl_group[CCS_MAX_ACL_GROUPS];
   
 /* The list for "struct domain_info". */  
 LIST1_HEAD(domain_list);  
29    
30  #ifdef CONFIG_TOMOYO  /* The initial domain. */
31    struct ccs_domain_info ccs_kernel_domain;
32    
33  /* Domain creation lock. */  /* The list for "struct ccs_domain_info". */
34  static DEFINE_MUTEX(domain_list_lock);  LIST_HEAD(ccs_domain_list);
   
 /* Structure for "initialize_domain" and "no_initialize_domain" keyword. */  
 struct domain_initializer_entry {  
         struct list1_head list;  
         const struct path_info *domainname;    /* This may be NULL */  
         const struct path_info *program;  
         bool is_deleted;  
         bool is_not;       /* True if this entry is "no_initialize_domain".  */  
         bool is_last_name; /* True if the domainname is ccs_get_last_name(). */  
 };  
   
 /* Structure for "keep_domain" and "no_keep_domain" keyword. */  
 struct domain_keeper_entry {  
         struct list1_head list;  
         const struct path_info *domainname;  
         const struct path_info *program;       /* This may be NULL */  
         bool is_deleted;  
         bool is_not;       /* True if this entry is "no_keep_domain".        */  
         bool is_last_name; /* True if the domainname is ccs_get_last_name(). */  
 };  
   
 /* Structure for "aggregator" keyword. */  
 struct aggregator_entry {  
         struct list1_head list;  
         const struct path_info *original_name;  
         const struct path_info *aggregated_name;  
         bool is_deleted;  
 };  
   
 /* Structure for "alias" keyword. */  
 struct alias_entry {  
         struct list1_head list;  
         const struct path_info *original_name;  
         const struct path_info *aliased_name;  
         bool is_deleted;  
 };  
35    
36  /**  struct list_head ccs_policy_list[CCS_MAX_POLICY];
37   * ccs_set_domain_flag - Set or clear domain's attribute flags.  struct list_head ccs_group_list[CCS_MAX_GROUP];
38   *  struct list_head ccs_shared_list[CCS_MAX_LIST];
  * @domain:    Pointer to "struct domain_info".  
  * @is_delete: True if it is a delete request.  
  * @flags:     Flags to set or clear.  
  *  
  * Returns nothing.  
  */  
 void ccs_set_domain_flag(struct domain_info *domain, const bool is_delete,  
                          const u8 flags)  
 {  
         /* We need to serialize because this is bitfield operation. */  
         static DEFINE_SPINLOCK(lock);  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&lock);  
         if (!is_delete)  
                 domain->flags |= flags;  
         else  
                 domain->flags &= ~flags;  
         spin_unlock(&lock);  
         /***** CRITICAL SECTION END *****/  
 }  
39    
40  /**  /**
41   * ccs_get_last_name - Get last component of a domainname.   * ccs_update_policy - Update an entry for exception policy.
  *  
  * @domain: Pointer to "struct domain_info".  
42   *   *
43   * Returns the last component of the domainname.   * @new_entry:       Pointer to "struct ccs_acl_info".
44   */   * @size:            Size of @new_entry in bytes.
45  const char *ccs_get_last_name(const struct domain_info *domain)   * @is_delete:       True if it is a delete request.
46  {   * @list:            Pointer to "struct list_head".
47          const char *cp0 = domain->domainname->name;   * @check_duplicate: Callback function to find duplicated entry.
         const char *cp1 = strrchr(cp0, ' ');  
         if (cp1)  
                 return cp1 + 1;  
         return cp0;  
 }  
   
 /**  
  * ccs_add_domain_acl - Add the given ACL to the given domain.  
48   *   *
49   * @domain: Pointer to "struct domain_info". May be NULL.   * Returns 0 on success, negative value otherwise.
  * @acl:    Pointer to "struct acl_info".  
50   *   *
51   * Returns 0.   * Caller holds ccs_read_lock().
52   */   */
53  int ccs_add_domain_acl(struct domain_info *domain, struct acl_info *acl)  int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
54  {                        bool is_delete, struct list_head *list,
55          if (domain) {                        bool (*check_duplicate) (const struct ccs_acl_head *,
56                  /*                                                 const struct ccs_acl_head *))
57                   * We need to serialize because this function is called by  {
58                   * various update functions.          int error = is_delete ? -ENOENT : -ENOMEM;
59                   */          struct ccs_acl_head *entry;
60                  static DEFINE_SPINLOCK(lock);          if (mutex_lock_interruptible(&ccs_policy_lock))
61                  /***** CRITICAL SECTION START *****/                  return -ENOMEM;
62                  spin_lock(&lock);          list_for_each_entry_rcu(entry, list, list) {
63                  list1_add_tail_mb(&acl->list, &domain->acl_info_list);                  if (!check_duplicate(entry, new_entry))
64                  spin_unlock(&lock);                          continue;
65                  /***** CRITICAL SECTION END *****/                  entry->is_deleted = is_delete;
66          } else {                  error = 0;
67                  acl->type &= ~ACL_DELETED;                  break;
68          }          }
69          ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);          if (error && !is_delete) {
70          return 0;                  entry = ccs_commit_ok(new_entry, size);
71                    if (entry) {
72                            list_add_tail_rcu(&entry->list, list);
73                            error = 0;
74                    }
75            }
76            mutex_unlock(&ccs_policy_lock);
77            return error;
78  }  }
79    
80  /**  static inline bool ccs_same_acl_head(const struct ccs_acl_info *p1,
81   * ccs_del_domain_acl - Delete the given ACL from the domain.                                       const struct ccs_acl_info *p2)
  *  
  * @acl: Pointer to "struct acl_info". May be NULL.  
  *  
  * Returns 0.  
  */  
 int ccs_del_domain_acl(struct acl_info *acl)  
82  {  {
83          if (acl)          return p1->type == p2->type && p1->cond == p2->cond;
                 acl->type |= ACL_DELETED;  
         ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);  
         return 0;  
84  }  }
85    
86  /**  /**
87   * audit_execute_handler_log - Audit execute_handler log.   * ccs_update_domain - Update an entry for domain policy.
88   *   *
89   * @is_default: True if it is "execute_handler" log.   * @new_entry:       Pointer to "struct ccs_acl_info".
90   * @handler:    The realpath of the handler.   * @size:            Size of @new_entry in bytes.
91   * @bprm:       Pointer to "struct linux_binprm".   * @param:           Pointer to "struct ccs_acl_param".
92     * @check_duplicate: Callback function to find duplicated entry.
93     * @merge_duplicate: Callback function to merge duplicated entry. Maybe NULL.
94   *   *
95   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
  */  
 static int audit_execute_handler_log(const bool is_default,  
                                      const char *handler,  
                                      struct linux_binprm *bprm)  
 {  
         struct ccs_request_info r;  
         ccs_init_request_info(&r, NULL, CCS_TOMOYO_MAC_FOR_FILE);  
         r.bprm = bprm;  
         return ccs_write_audit_log(true, &r, "%s %s\n",  
                                    is_default ? KEYWORD_EXECUTE_HANDLER :  
                                    KEYWORD_DENIED_EXECUTE_HANDLER, handler);  
 }  
   
 /**  
  * audit_domain_creation_log - Audit domain creation log.  
96   *   *
97   * @domain:  Pointer to "struct domain_info".   * Caller holds ccs_read_lock().
  *  
  * Returns 0 on success, negative value otherwise.  
98   */   */
99  static int audit_domain_creation_log(struct domain_info *domain)  int ccs_update_domain(struct ccs_acl_info *new_entry, const int size,
100  {                        struct ccs_acl_param *param,
101          struct ccs_request_info r;                        bool (*check_duplicate) (const struct ccs_acl_info *,
102          ccs_init_request_info(&r, domain, CCS_TOMOYO_MAC_FOR_FILE);                                                 const struct ccs_acl_info *),
103          return ccs_write_audit_log(false, &r, "use_profile %u\n", r.profile);                        bool (*merge_duplicate) (struct ccs_acl_info *,
104  }                                                 struct ccs_acl_info *,
105                                                   const bool))
106  /* The list for "struct domain_initializer_entry". */  {
107  static LIST1_HEAD(domain_initializer_list);          int error = param->is_delete ? -ENOENT : -ENOMEM;
108            struct ccs_acl_info *entry;
109  /**          const u8 type = new_entry->type;
110   * update_domain_initializer_entry - Update "struct domain_initializer_entry" list.          const u8 i = type == CCS_TYPE_AUTO_EXECUTE_HANDLER ||
111   *                  type == CCS_TYPE_DENIED_EXECUTE_HANDLER ||
112   * @domainname: The name of domain. May be NULL.                  type == CCS_TYPE_AUTO_TASK_ACL;
113   * @program:    The name of program.          const bool is_delete = param->is_delete;
114   * @is_not:     True if it is "no_initialize_domain" entry.          struct ccs_domain_info * const domain = param->domain;
115   * @is_delete:  True if it is a delete request.          if (param->data[0]) {
116   *                  new_entry->cond = ccs_get_condition(param->data);
117   * Returns 0 on success, negative value otherwise.                  if (!new_entry->cond)
  */  
 static int update_domain_initializer_entry(const char *domainname,  
                                            const char *program,  
                                            const bool is_not,  
                                            const bool is_delete)  
 {  
         struct domain_initializer_entry *new_entry;  
         struct domain_initializer_entry *ptr;  
         static DEFINE_MUTEX(lock);  
         const struct path_info *saved_program;  
         const struct path_info *saved_domainname = NULL;  
         int error = -ENOMEM;  
         bool is_last_name = false;  
         if (!ccs_is_correct_path(program, 1, -1, -1, __func__))  
                 return -EINVAL; /* No patterns allowed. */  
         if (domainname) {  
                 if (!ccs_is_domain_def(domainname) &&  
                     ccs_is_correct_path(domainname, 1, -1, -1, __func__))  
                         is_last_name = true;  
                 else if (!ccs_is_correct_domain(domainname, __func__))  
118                          return -EINVAL;                          return -EINVAL;
                 saved_domainname = ccs_save_name(domainname);  
                 if (!saved_domainname)  
                         return -ENOMEM;  
119          }          }
120          saved_program = ccs_save_name(program);          if (mutex_lock_interruptible(&ccs_policy_lock))
         if (!saved_program)  
                 return -ENOMEM;  
         mutex_lock(&lock);  
         list1_for_each_entry(ptr, &domain_initializer_list, list) {  
                 if (ptr->is_not != is_not ||  
                     ptr->domainname != saved_domainname ||  
                     ptr->program != saved_program)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
121                  goto out;                  goto out;
122            list_for_each_entry_rcu(entry, &domain->acl_info_list[i], list) {
123                    if (!ccs_same_acl_head(entry, new_entry) ||
124                        !check_duplicate(entry, new_entry))
125                            continue;
126                    if (merge_duplicate)
127                            entry->is_deleted = merge_duplicate(entry, new_entry,
128                                                                is_delete);
129                    else
130                            entry->is_deleted = is_delete;
131                    error = 0;
132                    break;
133          }          }
134          if (is_delete) {          if (error && !is_delete) {
135                  error = -ENOENT;                  entry = ccs_commit_ok(new_entry, size);
136                  goto out;                  if (entry) {
137                            list_add_tail_rcu(&entry->list,
138                                              &domain->acl_info_list[i]);
139                            error = 0;
140                    }
141          }          }
142          new_entry = ccs_alloc_element(sizeof(*new_entry));          mutex_unlock(&ccs_policy_lock);
         if (!new_entry)  
                 goto out;  
         new_entry->domainname = saved_domainname;  
         new_entry->program = saved_program;  
         new_entry->is_not = is_not;  
         new_entry->is_last_name = is_last_name;  
         list1_add_tail_mb(&new_entry->list, &domain_initializer_list);  
         error = 0;  
143   out:   out:
144          mutex_unlock(&lock);          ccs_put_condition(new_entry->cond);
         ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);  
145          return error;          return error;
146  }  }
147    
148  /**  void ccs_check_acl(struct ccs_request_info *r,
149   * ccs_read_domain_initializer_policy - Read "struct domain_initializer_entry" list.                     bool (*check_entry) (struct ccs_request_info *,
150   *                                          const struct ccs_acl_info *))
151   * @head: Pointer to "struct ccs_io_buffer".  {
152   *          const struct ccs_domain_info *domain = ccs_current_domain();
153   * Returns true on success, false otherwise.          struct ccs_acl_info *ptr;
154   */          bool retried = false;
155  bool ccs_read_domain_initializer_policy(struct ccs_io_buffer *head)          const u8 i = !check_entry;
156  {   retry:
157          struct list1_head *pos;          list_for_each_entry_rcu(ptr, &domain->acl_info_list[i], list) {
         list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {  
                 const char *no;  
                 const char *from = "";  
                 const char *domain = "";  
                 struct domain_initializer_entry *ptr;  
                 ptr = list1_entry(pos, struct domain_initializer_entry, list);  
158                  if (ptr->is_deleted)                  if (ptr->is_deleted)
159                          continue;                          continue;
160                  no = ptr->is_not ? "no_" : "";                  if (ptr->type != r->param_type)
                 if (ptr->domainname) {  
                         from = " from ";  
                         domain = ptr->domainname->name;  
                 }  
                 if (!ccs_io_printf(head,  
                                    "%s" KEYWORD_INITIALIZE_DOMAIN "%s%s%s\n",  
                                    no, ptr->program->name, from, domain))  
                                 goto out;  
         }  
         return true;  
  out:  
         return false;  
 }  
   
 /**  
  * ccs_write_domain_initializer_policy - Write "struct domain_initializer_entry" list.  
  *  
  * @data:      String to parse.  
  * @is_not:    True if it is "no_initialize_domain" entry.  
  * @is_delete: True if it is a delete request.  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 int ccs_write_domain_initializer_policy(char *data, const bool is_not,  
                                         const bool is_delete)  
 {  
         char *cp = strstr(data, " from ");  
         if (cp) {  
                 *cp = '\0';  
                 return update_domain_initializer_entry(cp + 6, data, is_not,  
                                                        is_delete);  
         }  
         return update_domain_initializer_entry(NULL, data, is_not, is_delete);  
 }  
   
 /**  
  * is_domain_initializer - Check whether the given program causes domainname reinitialization.  
  *  
  * @domainname: The name of domain.  
  * @program:    The name of program.  
  * @last_name:  The last component of @domainname.  
  *  
  * Returns true if executing @program reinitializes domain transition,  
  * false otherwise.  
  */  
 static bool is_domain_initializer(const struct path_info *domainname,  
                                   const struct path_info *program,  
                                   const struct path_info *last_name)  
 {  
         struct domain_initializer_entry *ptr;  
         bool flag = false;  
         list1_for_each_entry(ptr,  &domain_initializer_list, list) {  
                 if (ptr->is_deleted)  
161                          continue;                          continue;
162                  if (ptr->domainname) {                  if (check_entry && !check_entry(r, ptr))
                         if (!ptr->is_last_name) {  
                                 if (ptr->domainname != domainname)  
                                         continue;  
                         } else {  
                                 if (ccs_pathcmp(ptr->domainname, last_name))  
                                         continue;  
                         }  
                 }  
                 if (ccs_pathcmp(ptr->program, program))  
163                          continue;                          continue;
164                  if (ptr->is_not)                  if (!ccs_condition(r, ptr->cond))
165                          return false;                          continue;
166                  flag = true;                  r->matched_acl = ptr;
167                    r->granted = true;
168                    return;
169            }
170            if (!retried) {
171                    retried = true;
172                    domain = &ccs_acl_group[domain->group];
173                    goto retry;
174          }          }
175          return flag;          r->granted = false;
176  }  }
177    
178  /* The list for "struct domain_keeper_entry". */  static bool ccs_same_transition_control(const struct ccs_acl_head *a,
179  static LIST1_HEAD(domain_keeper_list);                                          const struct ccs_acl_head *b)
180    {
181            const struct ccs_transition_control *p1 = container_of(a, typeof(*p1),
182                                                                   head);
183            const struct ccs_transition_control *p2 = container_of(b, typeof(*p2),
184                                                                   head);
185            return p1->type == p2->type && p1->is_last_name == p2->is_last_name
186                    && p1->domainname == p2->domainname
187                    && p1->program == p2->program;
188    }
189    
190  /**  /**
191   * update_domain_keeper_entry - Update "struct domain_keeper_entry" list.   * ccs_update_transition_control_entry - Update "struct ccs_transition_control" list.
192   *   *
193   * @domainname: The name of domain.   * @domainname: The name of domain. Maybe NULL.
194   * @program:    The name of program. May be NULL.   * @program:    The name of program. Maybe NULL.
195   * @is_not:     True if it is "no_keep_domain" entry.   * @type:       Type of transition.
196   * @is_delete:  True if it is a delete request.   * @is_delete:  True if it is a delete request.
197   *   *
198   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
199   */   */
200  static int update_domain_keeper_entry(const char *domainname,  static int ccs_update_transition_control_entry(const char *domainname,
201                                        const char *program,                                                 const char *program,
202                                        const bool is_not, const bool is_delete)                                                 const u8 type,
203  {                                                 const bool is_delete)
204          struct domain_keeper_entry *new_entry;  {
205          struct domain_keeper_entry *ptr;          struct ccs_transition_control e = { .type = type };
206          const struct path_info *saved_domainname;          int error = is_delete ? -ENOENT : -ENOMEM;
207          const struct path_info *saved_program = NULL;          if (program && strcmp(program, "any")) {
208          static DEFINE_MUTEX(lock);                  if (!ccs_correct_path(program))
         int error = -ENOMEM;  
         bool is_last_name = false;  
         if (!ccs_is_domain_def(domainname) &&  
             ccs_is_correct_path(domainname, 1, -1, -1, __func__))  
                 is_last_name = true;  
         else if (!ccs_is_correct_domain(domainname, __func__))  
                 return -EINVAL;  
         if (program) {  
                 if (!ccs_is_correct_path(program, 1, -1, -1, __func__))  
209                          return -EINVAL;                          return -EINVAL;
210                  saved_program = ccs_save_name(program);                  e.program = ccs_get_name(program);
211                  if (!saved_program)                  if (!e.program)
212                          return -ENOMEM;                          goto out;
         }  
         saved_domainname = ccs_save_name(domainname);  
         if (!saved_domainname)  
                 return -ENOMEM;  
         mutex_lock(&lock);  
         list1_for_each_entry(ptr, &domain_keeper_list, list) {  
                 if (ptr->is_not != is_not ||  
                     ptr->domainname != saved_domainname ||  
                     ptr->program != saved_program)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 goto out;  
213          }          }
214          if (is_delete) {          if (domainname && strcmp(domainname, "any")) {
215                  error = -ENOENT;                  if (!ccs_correct_domain(domainname)) {
216                  goto out;                          if (!ccs_correct_path(domainname))
217                                    goto out;
218                            e.is_last_name = true;
219                    }
220                    e.domainname = ccs_get_name(domainname);
221                    if (!e.domainname)
222                            goto out;
223          }          }
224          new_entry = ccs_alloc_element(sizeof(*new_entry));          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
225          if (!new_entry)                                    &ccs_policy_list[CCS_ID_TRANSITION_CONTROL],
226                  goto out;                                    ccs_same_transition_control);
         new_entry->domainname = saved_domainname;  
         new_entry->program = saved_program;  
         new_entry->is_not = is_not;  
         new_entry->is_last_name = is_last_name;  
         list1_add_tail_mb(&new_entry->list, &domain_keeper_list);  
         error = 0;  
227   out:   out:
228          mutex_unlock(&lock);          ccs_put_name(e.domainname);
229          ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);          ccs_put_name(e.program);
230          return error;          return error;
231  }  }
232    
233  /**  /**
234   * ccs_write_domain_keeper_policy - Write "struct domain_keeper_entry" list.   * ccs_write_transition_control - Write "struct ccs_transition_control" list.
235   *   *
236   * @data:      String to parse.   * @data:      String to parse.
  * @is_not:    True if it is "no_keep_domain" entry.  
237   * @is_delete: True if it is a delete request.   * @is_delete: True if it is a delete request.
238     * @type:      Type of this entry.
239   *   *
240     * Returns 0 on success, negative value otherwise.
241   */   */
242  int ccs_write_domain_keeper_policy(char *data, const bool is_not,  int ccs_write_transition_control(char *data, const bool is_delete,
243                                     const bool is_delete)                                   const u8 type)
 {  
         char *cp = strstr(data, " from ");  
         if (cp) {  
                 *cp = '\0';  
                 return update_domain_keeper_entry(cp + 6, data,  
                                                   is_not, is_delete);  
         }  
         return update_domain_keeper_entry(data, NULL, is_not, is_delete);  
 }  
   
 /**  
  * ccs_read_domain_keeper_policy - Read "struct domain_keeper_entry" list.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  */  
 bool ccs_read_domain_keeper_policy(struct ccs_io_buffer *head)  
244  {  {
245          struct list1_head *pos;          char *domainname = strstr(data, " from ");
246          list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {          if (domainname) {
247                  struct domain_keeper_entry *ptr;                  *domainname = '\0';
248                  const char *no;                  domainname += 6;
249                  const char *from = "";          } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP ||
250                  const char *program = "";                     type == CCS_TRANSITION_CONTROL_KEEP) {
251                  ptr = list1_entry(pos, struct domain_keeper_entry, list);                  domainname = data;
252                  if (ptr->is_deleted)                  data = NULL;
                         continue;  
                 no = ptr->is_not ? "no_" : "";  
                 if (ptr->program) {  
                         from = " from ";  
                         program = ptr->program->name;  
                 }  
                 if (!ccs_io_printf(head,  
                                    "%s" KEYWORD_KEEP_DOMAIN "%s%s%s\n", no,  
                                    program, from, ptr->domainname->name))  
                                 goto out;  
253          }          }
254          return true;          return ccs_update_transition_control_entry(domainname, data, type,
255   out:                                                     is_delete);
         return false;  
256  }  }
257    
258  /**  /**
259   * is_domain_keeper - Check whether the given program causes domain transition suppression.   * ccs_transition_type - Get domain transition type.
260   *   *
261   * @domainname: The name of domain.   * @domainname: The name of domain.
262   * @program:    The name of program.   * @program:    The name of program.
  * @last_name:  The last component of @domainname.  
263   *   *
264   * Returns true if executing @program supresses domain transition,   * Returns CCS_TRANSITION_CONTROL_INITIALIZE if executing @program
265   * false otherwise.   * reinitializes domain transition, CCS_TRANSITION_CONTROL_KEEP if executing
266   */   * @program suppresses domain transition, others otherwise.
267  static bool is_domain_keeper(const struct path_info *domainname,   *
268                               const struct path_info *program,   * Caller holds ccs_read_lock().
269                               const struct path_info *last_name)   */
270  {  static u8 ccs_transition_type(const struct ccs_path_info *domainname,
271          struct domain_keeper_entry *ptr;                                const struct ccs_path_info *program)
272          bool flag = false;  {
273          list1_for_each_entry(ptr, &domain_keeper_list, list) {          const struct ccs_transition_control *ptr;
274                  if (ptr->is_deleted)          const char *last_name = ccs_last_word(domainname->name);
275                          continue;          u8 type;
276                  if (!ptr->is_last_name) {          for (type = 0; type < CCS_MAX_TRANSITION_TYPE; type++) {
277                          if (ptr->domainname != domainname)   next:
278                    list_for_each_entry_rcu(ptr, &ccs_policy_list
279                                            [CCS_ID_TRANSITION_CONTROL],
280                                            head.list) {
281                            if (ptr->head.is_deleted || ptr->type != type)
282                                  continue;                                  continue;
283                  } else {                          if (ptr->domainname) {
284                          if (ccs_pathcmp(ptr->domainname, last_name))                                  if (!ptr->is_last_name) {
285                                            if (ptr->domainname != domainname)
286                                                    continue;
287                                    } else {
288                                            /*
289                                             * Use direct strcmp() since this is
290                                             * unlikely used.
291                                             */
292                                            if (strcmp(ptr->domainname->name,
293                                                       last_name))
294                                                    continue;
295                                    }
296                            }
297                            if (ptr->program && ccs_pathcmp(ptr->program, program))
298                                  continue;                                  continue;
299                            if (type == CCS_TRANSITION_CONTROL_NO_INITIALIZE) {
300                                    /*
301                                     * Do not check for initialize_domain if
302                                     * no_initialize_domain matched.
303                                     */
304                                    type = CCS_TRANSITION_CONTROL_NO_KEEP;
305                                    goto next;
306                            }
307                            goto done;
308                  }                  }
                 if (ptr->program && ccs_pathcmp(ptr->program, program))  
                         continue;  
                 if (ptr->is_not)  
                         return false;  
                 flag = true;  
309          }          }
310          return flag;   done:
311  }          return type;
   
 /* The list for "struct alias_entry". */  
 static LIST1_HEAD(alias_list);  
   
 /**  
  * update_alias_entry - Update "struct alias_entry" list.  
  *  
  * @original_name: The original program's real name.  
  * @aliased_name:  The symbolic program's symbolic link's name.  
  * @is_delete:     True if it is a delete request.  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int update_alias_entry(const char *original_name,  
                               const char *aliased_name,  
                               const bool is_delete)  
 {  
         struct alias_entry *new_entry;  
         struct alias_entry *ptr;  
         static DEFINE_MUTEX(lock);  
         const struct path_info *saved_original_name;  
         const struct path_info *saved_aliased_name;  
         int error = -ENOMEM;  
         if (!ccs_is_correct_path(original_name, 1, -1, -1, __func__) ||  
             !ccs_is_correct_path(aliased_name, 1, -1, -1, __func__))  
                 return -EINVAL; /* No patterns allowed. */  
         saved_original_name = ccs_save_name(original_name);  
         saved_aliased_name = ccs_save_name(aliased_name);  
         if (!saved_original_name || !saved_aliased_name)  
                 return -ENOMEM;  
         mutex_lock(&lock);  
         list1_for_each_entry(ptr, &alias_list, list) {  
                 if (ptr->original_name != saved_original_name ||  
                     ptr->aliased_name != saved_aliased_name)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 goto out;  
         }  
         if (is_delete) {  
                 error = -ENOENT;  
                 goto out;  
         }  
         new_entry = ccs_alloc_element(sizeof(*new_entry));  
         if (!new_entry)  
                 goto out;  
         new_entry->original_name = saved_original_name;  
         new_entry->aliased_name = saved_aliased_name;  
         list1_add_tail_mb(&new_entry->list, &alias_list);  
         error = 0;  
  out:  
         mutex_unlock(&lock);  
         ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);  
         return error;  
312  }  }
313    
314  /**  static bool ccs_same_aggregator(const struct ccs_acl_head *a,
315   * ccs_read_alias_policy - Read "struct alias_entry" list.                                  const struct ccs_acl_head *b)
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  */  
 bool ccs_read_alias_policy(struct ccs_io_buffer *head)  
316  {  {
317          struct list1_head *pos;          const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head);
318          list1_for_each_cookie(pos, head->read_var2, &alias_list) {          const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head);
319                  struct alias_entry *ptr;          return p1->original_name == p2->original_name &&
320                  ptr = list1_entry(pos, struct alias_entry, list);                  p1->aggregated_name == p2->aggregated_name;
                 if (ptr->is_deleted)  
                         continue;  
                 if (!ccs_io_printf(head, KEYWORD_ALIAS "%s %s\n",  
                                    ptr->original_name->name,  
                                    ptr->aliased_name->name))  
                         goto out;  
         }  
         return true;  
  out:  
         return false;  
321  }  }
322    
323  /**  /**
324   * ccs_write_alias_policy - Write "struct alias_entry" list.   * ccs_update_aggregator_entry - Update "struct ccs_aggregator" list.
  *  
  * @data:      String to parse.  
  * @is_delete: True if it is a delete request.  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 int ccs_write_alias_policy(char *data, const bool is_delete)  
 {  
         char *cp = strchr(data, ' ');  
         if (!cp)  
                 return -EINVAL;  
         *cp++ = '\0';  
         return update_alias_entry(data, cp, is_delete);  
 }  
   
 /* The list for "struct aggregator_entry". */  
 static LIST1_HEAD(aggregator_list);  
   
 /**  
  * update_aggregator_entry - Update "struct aggregator_entry" list.  
325   *   *
326   * @original_name:   The original program's name.   * @original_name:   The original program's name.
327   * @aggregated_name: The aggregated program's name.   * @aggregated_name: The aggregated program's name.
# Line 619  static LIST1_HEAD(aggregator_list); Line 329  static LIST1_HEAD(aggregator_list);
329   *   *
330   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
331   */   */
332  static int update_aggregator_entry(const char *original_name,  static int ccs_update_aggregator_entry(const char *original_name,
333                                     const char *aggregated_name,                                         const char *aggregated_name,
334                                     const bool is_delete)                                         const bool is_delete)
335  {  {
336          struct aggregator_entry *new_entry;          struct ccs_aggregator e = { };
337          struct aggregator_entry *ptr;          int error = is_delete ? -ENOENT : -ENOMEM;
338          static DEFINE_MUTEX(lock);          if (!ccs_correct_word(original_name) ||
339          const struct path_info *saved_original_name;              !ccs_correct_path(aggregated_name))
         const struct path_info *saved_aggregated_name;  
         int error = -ENOMEM;  
         if (!ccs_is_correct_path(original_name, 1, 0, -1, __func__) ||  
             !ccs_is_correct_path(aggregated_name, 1, -1, -1, __func__))  
340                  return -EINVAL;                  return -EINVAL;
341          saved_original_name = ccs_save_name(original_name);          e.original_name = ccs_get_name(original_name);
342          saved_aggregated_name = ccs_save_name(aggregated_name);          e.aggregated_name = ccs_get_name(aggregated_name);
343          if (!saved_original_name || !saved_aggregated_name)          if (!e.original_name || !e.aggregated_name ||
344                  return -ENOMEM;              e.aggregated_name->is_patterned) /* No patterns allowed. */
345          mutex_lock(&lock);                  goto out;
346          list1_for_each_entry(ptr, &aggregator_list, list) {          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
347                  if (ptr->original_name != saved_original_name ||                                    &ccs_policy_list[CCS_ID_AGGREGATOR],
348                      ptr->aggregated_name != saved_aggregated_name)                                    ccs_same_aggregator);
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 goto out;  
         }  
         if (is_delete) {  
                 error = -ENOENT;  
                 goto out;  
         }  
         new_entry = ccs_alloc_element(sizeof(*new_entry));  
         if (!new_entry)  
                 goto out;  
         new_entry->original_name = saved_original_name;  
         new_entry->aggregated_name = saved_aggregated_name;  
         list1_add_tail_mb(&new_entry->list, &aggregator_list);  
         error = 0;  
349   out:   out:
350          mutex_unlock(&lock);          ccs_put_name(e.original_name);
351          ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);          ccs_put_name(e.aggregated_name);
352          return error;          return error;
353  }  }
354    
355  /**  /**
356   * ccs_read_aggregator_policy - Read "struct aggregator_entry" list.   * ccs_write_aggregator - Write "struct ccs_aggregator" list.
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  */  
 bool ccs_read_aggregator_policy(struct ccs_io_buffer *head)  
 {  
         struct list1_head *pos;  
         list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {  
                 struct aggregator_entry *ptr;  
                 ptr = list1_entry(pos, struct aggregator_entry, list);  
                 if (ptr->is_deleted)  
                         continue;  
                 if (!ccs_io_printf(head, KEYWORD_AGGREGATOR "%s %s\n",  
                                    ptr->original_name->name,  
                                    ptr->aggregated_name->name))  
                         goto out;  
         }  
         return true;  
  out:  
         return false;  
 }  
   
 /**  
  * ccs_write_aggregator_policy - Write "struct aggregator_entry" list.  
357   *   *
358   * @data:      String to parse.   * @data:      String to parse.
359   * @is_delete: True if it is a delete request.   * @is_delete: True if it is a delete request.
360   *   *
361   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
362   */   */
363  int ccs_write_aggregator_policy(char *data, const bool is_delete)  int ccs_write_aggregator(char *data, const bool is_delete)
364  {  {
365          char *cp = strchr(data, ' ');          char *w[2];
366          if (!cp)          if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
367                  return -EINVAL;                  return -EINVAL;
368          *cp++ = '\0';          return ccs_update_aggregator_entry(w[0], w[1], is_delete);
         return update_aggregator_entry(data, cp, is_delete);  
369  }  }
370    
371  /* Domain create/delete/undelete handler. */  /* Domain create/delete handler. */
   
 /* #define DEBUG_DOMAIN_UNDELETE */  
372    
373  /**  /**
374   * ccs_delete_domain - Delete a domain.   * ccs_delete_domain - Delete a domain.
# Line 717  int ccs_write_aggregator_policy(char *da Line 379  int ccs_write_aggregator_policy(char *da
379   */   */
380  int ccs_delete_domain(char *domainname)  int ccs_delete_domain(char *domainname)
381  {  {
382          struct domain_info *domain;          struct ccs_domain_info *domain;
383          struct path_info name;          struct ccs_path_info name;
384          name.name = domainname;          name.name = domainname;
385          ccs_fill_path_info(&name);          ccs_fill_path_info(&name);
386          mutex_lock(&domain_list_lock);          if (mutex_lock_interruptible(&ccs_policy_lock))
387  #ifdef DEBUG_DOMAIN_UNDELETE                  return 0;
         printk(KERN_DEBUG "ccs_delete_domain %s\n", domainname);  
         list1_for_each_entry(domain, &domain_list, list) {  
                 if (ccs_pathcmp(domain->domainname, &name))  
                         continue;  
                 printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);  
         }  
 #endif  
388          /* Is there an active domain? */          /* Is there an active domain? */
389          list1_for_each_entry(domain, &domain_list, list) {          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
390                  struct domain_info *domain2;                  /* Never delete ccs_kernel_domain */
391                  /* Never delete KERNEL_DOMAIN */                  if (domain == &ccs_kernel_domain)
                 if (domain == &KERNEL_DOMAIN)  
392                          continue;                          continue;
393                  if (domain->is_deleted ||                  if (domain->is_deleted ||
394                      ccs_pathcmp(domain->domainname, &name))                      ccs_pathcmp(domain->domainname, &name))
395                          continue;                          continue;
396                  /* Mark already deleted domains as non undeletable. */                  domain->is_deleted = true;
                 list1_for_each_entry(domain2, &domain_list, list) {  
                         if (!domain2->is_deleted ||  
                             ccs_pathcmp(domain2->domainname, &name))  
                                 continue;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                         if (domain2->is_deleted != 255)  
                                 printk(KERN_DEBUG  
                                        "Marked %p as non undeletable\n",  
                                        domain2);  
 #endif  
                         domain2->is_deleted = 255;  
                 }  
                 /* Delete and mark active domain as undeletable. */  
                 domain->is_deleted = 1;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                 printk(KERN_DEBUG "Marked %p as undeletable\n", domain);  
 #endif  
397                  break;                  break;
398          }          }
399          mutex_unlock(&domain_list_lock);          mutex_unlock(&ccs_policy_lock);
400          return 0;          return 0;
401  }  }
402    
403  /**  /**
404   * ccs_undelete_domain - Undelete a domain.   * ccs_assign_domain - Create a domain.
  *  
  * @domainname: The name of domain.  
  *  
  * Returns pointer to "struct domain_info" on success, NULL otherwise.  
  */  
 struct domain_info *ccs_undelete_domain(const char *domainname)  
 {  
         struct domain_info *domain;  
         struct domain_info *candidate_domain = NULL;  
         struct path_info name;  
         name.name = domainname;  
         ccs_fill_path_info(&name);  
         mutex_lock(&domain_list_lock);  
 #ifdef DEBUG_DOMAIN_UNDELETE  
         printk(KERN_DEBUG "ccs_undelete_domain %s\n", domainname);  
         list1_for_each_entry(domain, &domain_list, list) {  
                 if (ccs_pathcmp(domain->domainname, &name))  
                         continue;  
                 printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);  
         }  
 #endif  
         list1_for_each_entry(domain, &domain_list, list) {  
                 if (ccs_pathcmp(&name, domain->domainname))  
                         continue;  
                 if (!domain->is_deleted) {  
                         /* This domain is active. I can't undelete. */  
                         candidate_domain = NULL;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                         printk(KERN_DEBUG "%p is active. I can't undelete.\n",  
                                domain);  
 #endif  
                         break;  
                 }  
                 /* Is this domain undeletable? */  
                 if (domain->is_deleted == 1)  
                         candidate_domain = domain;  
         }  
         if (candidate_domain) {  
                 candidate_domain->is_deleted = 0;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                 printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);  
 #endif  
         }  
         mutex_unlock(&domain_list_lock);  
         return candidate_domain;  
 }  
   
 /**  
  * ccs_find_or_assign_new_domain - Create a domain.  
405   *   *
406   * @domainname: The name of domain.   * @domainname: The name of domain.
407   * @profile:    Profile number to assign if the domain was newly created.   * @profile:    Profile number to assign if the domain was newly created.
408     * @group:      Group number to assign if the domain was newly created.
409     * @transit:    True if transit to domain found or created.
410     *
411     * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
412   *   *
413   * Returns pointer to "struct domain_info" on success, NULL otherwise.   * Caller holds ccs_read_lock().
414   */   */
415  struct domain_info *ccs_find_or_assign_new_domain(const char *domainname,  struct ccs_domain_info *ccs_assign_domain(const char *domainname,
416                                                    const u8 profile)                                            const u8 profile, const u8 group,
417                                              const bool transit)
418  {  {
419          struct domain_info *domain = NULL;          struct ccs_domain_info e = { };
420          const struct path_info *saved_domainname;          struct ccs_domain_info *entry = ccs_find_domain(domainname);
421          mutex_lock(&domain_list_lock);          bool created = false;
422          domain = ccs_find_domain(domainname);          if (entry)
         if (domain)  
                 goto out;  
         if (!ccs_is_correct_domain(domainname, __func__))  
423                  goto out;                  goto out;
424          saved_domainname = ccs_save_name(domainname);          if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 ||
425          if (!saved_domainname)              !ccs_correct_domain(domainname))
426                    return NULL;
427            e.profile = profile;
428            e.group = group;
429            e.domainname = ccs_get_name(domainname);
430            if (!e.domainname)
431                    return NULL;
432            if (mutex_lock_interruptible(&ccs_policy_lock))
433                  goto out;                  goto out;
434          /* Can I reuse memory of deleted domain? */          entry = ccs_find_domain(domainname);
435          list1_for_each_entry(domain, &domain_list, list) {          if (!entry) {
436                  struct task_struct *p;                  entry = ccs_commit_ok(&e, sizeof(e));
437                  struct acl_info *ptr;                  if (entry) {
438                  bool flag;                          INIT_LIST_HEAD(&entry->acl_info_list[0]);
439                  if (!domain->is_deleted ||                          INIT_LIST_HEAD(&entry->acl_info_list[1]);
440                      domain->domainname != saved_domainname)                          list_add_tail_rcu(&entry->list, &ccs_domain_list);
441                          continue;                          created = true;
                 flag = false;  
                 /***** CRITICAL SECTION START *****/  
                 read_lock(&tasklist_lock);  
                 for_each_process(p) {  
                         if (p->domain_info != domain)  
                                 continue;  
                         flag = true;  
                         break;  
                 }  
                 read_unlock(&tasklist_lock);  
                 /***** CRITICAL SECTION END *****/  
                 if (flag)  
                         continue;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                 printk(KERN_DEBUG "Reusing %p %s\n", domain,  
                        domain->domainname->name);  
 #endif  
                 list1_for_each_entry(ptr, &domain->acl_info_list, list) {  
                         ptr->type |= ACL_DELETED;  
442                  }                  }
                 ccs_set_domain_flag(domain, true, domain->flags);  
                 domain->profile = profile;  
                 domain->quota_warned = false;  
                 mb(); /* Avoid out-of-order execution. */  
                 domain->is_deleted = 0;  
                 goto out;  
         }  
         /* No memory reusable. Create using new memory. */  
         domain = ccs_alloc_element(sizeof(*domain));  
         if (domain) {  
                 INIT_LIST1_HEAD(&domain->acl_info_list);  
                 domain->domainname = saved_domainname;  
                 domain->profile = profile;  
                 list1_add_tail_mb(&domain->list, &domain_list);  
443          }          }
444            mutex_unlock(&ccs_policy_lock);
445   out:   out:
446          mutex_unlock(&domain_list_lock);          ccs_put_name(e.domainname);
447          return domain;          if (entry && transit) {
448  }                  current->ccs_domain_info = entry;
449                    if (created) {
450  /**                          struct ccs_request_info r;
451   * get_argv0 - Get argv[0].                          ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE);
452   *                          r.granted = false;
453   * @bprm: Pointer to "struct linux_binprm".                          ccs_write_log(&r, "use_profile %u\n", r.profile);
  * @tmp:  Buffer for temporal use.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool get_argv0(struct linux_binprm *bprm, struct ccs_page_buffer *tmp)  
 {  
         char *arg_ptr = tmp->buffer;  
         int arg_len = 0;  
         unsigned long pos = bprm->p;  
         int i = pos / PAGE_SIZE;  
         int offset = pos % PAGE_SIZE;  
         bool done = false;  
         if (!bprm->argc)  
                 goto out;  
         while (1) {  
                 struct page *page;  
                 const char *kaddr;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page,  
                                    NULL) <= 0)  
                         goto out;  
                 pos += PAGE_SIZE - offset;  
 #else  
                 page = bprm->page[i];  
 #endif  
                 /* Map. */  
                 kaddr = kmap(page);  
                 if (!kaddr) { /* Mapping failed. */  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                         put_page(page);  
 #endif  
                         goto out;  
454                  }                  }
                 /* Read. */  
                 while (offset < PAGE_SIZE) {  
                         const unsigned char c = kaddr[offset++];  
                         if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {  
                                 if (c == '\\') {  
                                         arg_ptr[arg_len++] = '\\';  
                                         arg_ptr[arg_len++] = '\\';  
                                 } else if (c == '/') {  
                                         arg_len = 0;  
                                 } else if (c > ' ' && c < 127) {  
                                         arg_ptr[arg_len++] = c;  
                                 } else {  
                                         arg_ptr[arg_len++] = '\\';  
                                         arg_ptr[arg_len++] = (c >> 6) + '0';  
                                         arg_ptr[arg_len++]  
                                                 = ((c >> 3) & 7) + '0';  
                                         arg_ptr[arg_len++] = (c & 7) + '0';  
                                 }  
                         } else {  
                                 arg_ptr[arg_len] = '\0';  
                                 done = true;  
                                 break;  
                         }  
                 }  
                 /* Unmap. */  
                 kunmap(page);  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                 put_page(page);  
 #endif  
                 i++;  
                 offset = 0;  
                 if (done)  
                         break;  
455          }          }
456          return true;          return entry;
  out:  
         return false;  
457  }  }
458    
459  /**  /**
460   * find_next_domain - Find a domain.   * ccs_find_next_domain - Find a domain.
461   *   *
462   * @r:       Pointer to "struct ccs_request_info".   * @ee: Pointer to "struct ccs_execve".
  * @handler: Pathname to verify. May be NULL.  
463   *   *
464   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
465     *
466     * Caller holds ccs_read_lock().
467   */   */
468  static int find_next_domain(struct ccs_request_info *r,  static int ccs_find_next_domain(struct ccs_execve *ee)
                             const struct path_info *handler)  
469  {  {
470          /*          struct ccs_request_info *r = &ee->r;
471           * This function assumes that the size of buffer returned by          const struct ccs_path_info *handler = ee->handler;
472           * ccs_realpath() = CCS_MAX_PATHNAME_LEN.          struct ccs_domain_info *domain = NULL;
473           */          struct ccs_domain_info * const old_domain = ccs_current_domain();
474          struct domain_info *domain = NULL;          struct linux_binprm *bprm = ee->bprm;
475          const char *old_domain_name = r->domain->domainname->name;          struct task_struct *task = current;
476          struct linux_binprm *bprm = r->bprm;          struct ccs_path_info rn = { }; /* real name */
         struct ccs_page_buffer *tmp = r->obj->tmp;  
         const char *original_name = bprm->filename;  
         const u8 mode = r->mode;  
         const bool is_enforce = (mode == 3);  
         const u32 tomoyo_flags = r->tomoyo_flags;  
         char *new_domain_name = NULL;  
         char *real_program_name = NULL;  
         char *symlink_program_name = NULL;  
         struct path_info rn; /* real name */  
         struct path_info sn; /* symlink name */  
         struct path_info ln; /* last name */  
477          int retval;          int retval;
478            bool need_kfree = false;
479          {   retry:
480                  /*          r->matched_acl = NULL;
481                   * Built-in initializers. This is needed because policies are          if (need_kfree) {
482                   * not loaded until starting /sbin/init.                  kfree(rn.name);
483                   */                  need_kfree = false;
                 static bool first = true;  
                 if (first) {  
                         update_domain_initializer_entry(NULL, "/sbin/hotplug",  
                                                         false, false);  
                         update_domain_initializer_entry(NULL, "/sbin/modprobe",  
                                                         false, false);  
                         first = false;  
                 }  
484          }          }
485    
486   retry:          /* Get symlink's pathname of program. */
487          current->tomoyo_flags = tomoyo_flags;          retval = ccs_symlink_path(bprm->filename, &rn);
488          r->tomoyo_flags = tomoyo_flags;          if (retval < 0)
         /* Get ccs_realpath of program. */  
         retval = -ENOENT; /* I hope ccs_realpath() won't fail with -ENOMEM. */  
         ccs_free(real_program_name);  
         real_program_name = ccs_realpath(original_name);  
         if (!real_program_name)  
                 goto out;  
         /* Get ccs_realpath of symbolic link. */  
         ccs_free(symlink_program_name);  
         symlink_program_name = ccs_realpath_nofollow(original_name);  
         if (!symlink_program_name)  
489                  goto out;                  goto out;
490            need_kfree = true;
         rn.name = real_program_name;  
         ccs_fill_path_info(&rn);  
         sn.name = symlink_program_name;  
         ccs_fill_path_info(&sn);  
         ln.name = ccs_get_last_name(r->domain);  
         ccs_fill_path_info(&ln);  
491    
492          if (handler) {          if (handler) {
493                  if (ccs_pathcmp(&rn, handler)) {                  if (ccs_pathcmp(&rn, handler)) {
# Line 1037  static int find_next_domain(struct ccs_r Line 500  static int find_next_domain(struct ccs_r
500                          }                          }
501                          goto out;                          goto out;
502                  }                  }
503                  goto calculate_domain;          } else {
504          }                  struct ccs_aggregator *ptr;
505                    /* Check 'aggregator' directive. */
506          /* Check 'alias' directive. */                  list_for_each_entry_rcu(ptr,
507          if (ccs_pathcmp(&rn, &sn)) {                                          &ccs_policy_list[CCS_ID_AGGREGATOR],
508                  struct alias_entry *ptr;                                          head.list) {
509                  /* Is this program allowed to be called via symbolic links? */                          if (ptr->head.is_deleted ||
510                  list1_for_each_entry(ptr, &alias_list, list) {                              !ccs_path_matches_pattern(&rn, ptr->original_name))
                         if (ptr->is_deleted ||  
                             ccs_pathcmp(&rn, ptr->original_name) ||  
                             ccs_pathcmp(&sn, ptr->aliased_name))  
511                                  continue;                                  continue;
512                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);                          kfree(rn.name);
513                          strncpy(real_program_name, ptr->aliased_name->name,                          need_kfree = false;
514                                  CCS_MAX_PATHNAME_LEN - 1);                          /* This is OK because it is read only. */
515                          ccs_fill_path_info(&rn);                          rn = *ptr->aggregated_name;
516                          break;                          break;
517                  }                  }
         }  
518    
519          /* Compare basename of real_program_name and argv[0] */                  /* Check execute permission. */
520          r->mode = ccs_check_flags(r->domain, CCS_TOMOYO_MAC_FOR_ARGV0);                  retval = ccs_path_permission(r, CCS_TYPE_EXECUTE, &rn);
521          if (bprm->argc > 0 && r->mode) {                  if (retval == CCS_RETRY_REQUEST)
522                  char *base_argv0 = tmp->buffer;                          goto retry;
523                  const char *base_filename;                  if (retval < 0)
                 retval = -ENOMEM;  
                 if (!get_argv0(bprm, tmp))  
524                          goto out;                          goto out;
525                  base_filename = strrchr(real_program_name, '/');                  /*
526                  if (!base_filename)                   * To be able to specify domainnames with wildcards, use the
527                          base_filename = real_program_name;                   * pathname specified in the policy (which may contain
528                  else                   * wildcard) rather than the pathname passed to execve()
529                          base_filename++;                   * (which never contains wildcard).
530                  if (strcmp(base_argv0, base_filename)) {                   */
531                          retval = ccs_check_argv0_perm(r, &rn, base_argv0);                  if (r->param.path.matched_path) {
532                          if (retval == 1) {                          if (need_kfree)
533                                  r->retry++;                                  kfree(rn.name);
534                                  goto retry;                          need_kfree = false;
535                          }                          /* This is OK because it is read only. */
536                          r->retry = 0;                          rn = *r->param.path.matched_path;
                         r->tomoyo_flags = current->tomoyo_flags;  
                         if (retval < 0)  
                                 goto out;  
                 }  
         }  
   
         /* Check 'aggregator' directive. */  
         {  
                 struct aggregator_entry *ptr;  
                 /* Is this program allowed to be aggregated? */  
                 list1_for_each_entry(ptr, &aggregator_list, list) {  
                         if (ptr->is_deleted ||  
                             !ccs_path_matches_pattern(&rn, ptr->original_name))  
                                 continue;  
                         memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);  
                         strncpy(real_program_name, ptr->aggregated_name->name,  
                                 CCS_MAX_PATHNAME_LEN - 1);  
                         ccs_fill_path_info(&rn);  
                         break;  
537                  }                  }
538          }          }
539    
540          /* Check execute permission. */          /* Calculate domain to transit to. */
541          r->mode = mode;          switch (ccs_transition_type(old_domain->domainname, &rn)) {
542          retval = ccs_check_exec_perm(r, &rn);          case CCS_TRANSITION_CONTROL_INITIALIZE:
543          if (retval == 1) {                  /* Transit to the child of ccs_kernel_domain domain. */
544                  r->retry++;                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, CCS_ROOT_NAME " " "%s",
545                  goto retry;                           rn.name);
546          }                  break;
547          r->retry = 0;          case CCS_TRANSITION_CONTROL_KEEP:
         r->tomoyo_flags = current->tomoyo_flags;  
         if (retval < 0)  
                 goto out;  
   
  calculate_domain:  
         new_domain_name = tmp->buffer;  
         if (is_domain_initializer(r->domain->domainname, &rn, &ln)) {  
                 /* Transit to the child of KERNEL_DOMAIN domain. */  
                 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1,  
                          ROOT_NAME " " "%s", real_program_name);  
         } else if (r->domain == &KERNEL_DOMAIN && !sbin_init_started) {  
                 /*  
                  * Needn't to transit from kernel domain before starting  
                  * /sbin/init. But transit from kernel domain if executing  
                  * initializers because they might start before /sbin/init.  
                  */  
                 domain = r->domain;  
         } else if (is_domain_keeper(r->domain->domainname, &rn, &ln)) {  
548                  /* Keep current domain. */                  /* Keep current domain. */
549                  domain = r->domain;                  domain = old_domain;
550          } else {                  break;
551                  /* Normal domain transition. */          default:
552                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1,                  if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) {
553                           "%s %s", old_domain_name, real_program_name);                          /*
554          }                           * Needn't to transit from kernel domain before
555          if (domain || strlen(new_domain_name) >= CCS_MAX_PATHNAME_LEN)                           * starting /sbin/init. But transit from kernel domain
556                  goto done;                           * if executing initializers because they might start
557          domain = ccs_find_domain(new_domain_name);                           * before /sbin/init.
558          if (domain)                           */
559                  goto done;                          domain = old_domain;
560          if (is_enforce) {                  } else {
561                  int error = ccs_check_supervisor(r,                          /* Normal domain transition. */
562                                                   "# wants to create domain\n"                          snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
563                                                   "%s\n", new_domain_name);                                   old_domain->domainname->name, rn.name);
                 if (error == 1) {  
                         r->retry++;  
                         goto retry;  
564                  }                  }
565                  r->retry = 0;                  break;
                 if (error < 0)  
                         goto done;  
566          }          }
567          domain = ccs_find_or_assign_new_domain(new_domain_name, r->profile);          /*
568             * Tell GC that I started execve().
569             * Also, tell open_exec() to check read permission.
570             */
571            task->ccs_flags |= CCS_TASK_IS_IN_EXECVE;
572            /*
573             * Make task->ccs_flags visible to GC before changing
574             * task->ccs_domain_info .
575             */
576            smp_mb();
577            /*
578             * Proceed to the next domain in order to allow reaching via PID.
579             * It will be reverted if execve() failed. Reverting is not good.
580             * But it is better than being unable to reach via PID in interactive
581             * enforcing mode.
582             */
583            if (!domain)
584                    domain = ccs_assign_domain(ee->tmp, r->profile,
585                                               old_domain->group, true);
586          if (domain)          if (domain)
                 audit_domain_creation_log(domain);  
  done:  
         if (!domain) {  
                 printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",  
                        new_domain_name);  
                 if (is_enforce)  
                         retval = -EPERM;  
                 else {  
                         retval = 0;  
                         ccs_set_domain_flag(r->domain, false,  
                                             DOMAIN_FLAGS_TRANSITION_FAILED);  
                 }  
         } else {  
587                  retval = 0;                  retval = 0;
588            else if (r->mode == CCS_CONFIG_ENFORCING)
589                    retval = -ENOMEM;
590            else {
591                    retval = 0;
592                    if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) {
593                            old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true;
594                            r->granted = false;
595                            ccs_write_log(r, "%s",
596                                          ccs_dif[CCS_DIF_TRANSITION_FAILED]);
597                            printk(KERN_WARNING
598                                   "ERROR: Domain '%s' not defined.\n", ee->tmp);
599                    }
600          }          }
601   out:   out:
602          ccs_free(real_program_name);          if (need_kfree)
603          ccs_free(symlink_program_name);                  kfree(rn.name);
         if (domain)  
                 r->domain = domain;  
604          return retval;          return retval;
605  }  }
606    
607  /**  /**
608   * check_environ - Check permission for environment variable names.   * ccs_environ - Check permission for environment variable names.
609   *   *
610   * @r: Pointer to "struct ccs_request_info".   * @ee: Pointer to "struct ccs_execve".
611   *   *
612   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
613   */   */
614  static int check_environ(struct ccs_request_info *r)  static int ccs_environ(struct ccs_execve *ee)
615  {  {
616          struct linux_binprm *bprm = r->bprm;          struct ccs_request_info *r = &ee->r;
617          struct ccs_page_buffer *tmp = r->obj->tmp;          struct linux_binprm *bprm = ee->bprm;
618          char *arg_ptr = tmp->buffer;          /* env_page->data is allocated by ccs_dump_page(). */
619            struct ccs_page_dump env_page = { };
620            char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */
621          int arg_len = 0;          int arg_len = 0;
622          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
         int i = pos / PAGE_SIZE;  
623          int offset = pos % PAGE_SIZE;          int offset = pos % PAGE_SIZE;
624          int argv_count = bprm->argc;          int argv_count = bprm->argc;
625          int envp_count = bprm->envc;          int envp_count = bprm->envc;
626          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
627          int error = -ENOMEM;          int error = -ENOMEM;
628            ee->r.type = CCS_MAC_ENVIRON;
629            ee->r.mode = ccs_get_mode(ccs_current_domain()->profile,
630                                      CCS_MAC_ENVIRON);
631          if (!r->mode || !envp_count)          if (!r->mode || !envp_count)
632                  return 0;                  return 0;
633            arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
634            if (!arg_ptr)
635                    goto out;
636          while (error == -ENOMEM) {          while (error == -ENOMEM) {
637                  struct page *page;                  if (!ccs_dump_page(bprm, pos, &env_page))
                 const char *kaddr;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page,  
                                    NULL) <= 0)  
638                          goto out;                          goto out;
639                  pos += PAGE_SIZE - offset;                  pos += PAGE_SIZE - offset;
 #else  
                 page = bprm->page[i];  
 #endif  
                 /* Map. */  
                 kaddr = kmap(page);  
                 if (!kaddr) { /* Mapping failed. */  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                         put_page(page);  
 #endif  
                         goto out;  
                 }  
640                  /* Read. */                  /* Read. */
641                  while (argv_count && offset < PAGE_SIZE) {                  while (argv_count && offset < PAGE_SIZE) {
642                          if (!kaddr[offset++])                          if (!env_page.data[offset++])
643                                  argv_count--;                                  argv_count--;
644                  }                  }
645                  if (argv_count)                  if (argv_count) {
646                          goto unmap_page;                          offset = 0;
647                            continue;
648                    }
649                  while (offset < PAGE_SIZE) {                  while (offset < PAGE_SIZE) {
650                          const unsigned char c = kaddr[offset++];                          const unsigned char c = env_page.data[offset++];
651                          if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {                          if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
652                                  if (c == '=') {                                  if (c == '=') {
653                                          arg_ptr[arg_len++] = '\0';                                          arg_ptr[arg_len++] = '\0';
654                                  } else if (c == '\\') {                                  } else if (c == '\\') {
# Line 1244  static int check_environ(struct ccs_requ Line 668  static int check_environ(struct ccs_requ
668                          }                          }
669                          if (c)                          if (c)
670                                  continue;                                  continue;
671                          if (ccs_check_env_perm(r, arg_ptr)) {                          if (ccs_env_perm(r, arg_ptr)) {
672                                  error = -EPERM;                                  error = -EPERM;
673                                  break;                                  break;
674                          }                          }
# Line 1254  static int check_environ(struct ccs_requ Line 678  static int check_environ(struct ccs_requ
678                          }                          }
679                          arg_len = 0;                          arg_len = 0;
680                  }                  }
  unmap_page:  
                 /* Unmap. */  
                 kunmap(page);  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  
                 put_page(page);  
 #endif  
                 i++;  
681                  offset = 0;                  offset = 0;
682          }          }
683   out:   out:
684          if (r->mode != 3)          if (r->mode != 3)
685                  error = 0;                  error = 0;
686            kfree(env_page.data);
687            kfree(arg_ptr);
688          return error;          return error;
689  }  }
690    
691  /**  /**
692   * unescape - Unescape escaped string.   * ccs_unescape - Unescape escaped string.
693   *   *
694   * @dest: String to unescape.   * @dest: String to unescape.
695   *   *
696   * Returns nothing.   * Returns nothing.
697   */   */
698  static void unescape(unsigned char *dest)  static void ccs_unescape(unsigned char *dest)
699  {  {
700          unsigned char *src = dest;          unsigned char *src = dest;
701          unsigned char c;          unsigned char c;
702          unsigned char d;          unsigned char d;
703          unsigned char e;          unsigned char e;
704          while ((c = *src++) != '\0') {          while (1) {
705                    c = *src++;
706                    if (!c)
707                            break;
708                  if (c != '\\') {                  if (c != '\\') {
709                          *dest++ = c;                          *dest++ = c;
710                          continue;                          continue;
# Line 1305  static void unescape(unsigned char *dest Line 727  static void unescape(unsigned char *dest
727          *dest = '\0';          *dest = '\0';
728  }  }
729    
730  /**  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
731   * root_depth - Get number of directories to strip.  static int ccs_copy_argv(const char *arg, struct linux_binprm *bprm)
  *  
  * @dentry: Pointer to "struct dentry".  
  * @vfsmnt: Pointer to "struct vfsmount".  
  *  
  * Returns number of directories to strip.  
  */  
 static inline int root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)  
732  {  {
733          int depth = 0;          const int ret = copy_strings_kernel(1, &arg, bprm);
734          /***** CRITICAL SECTION START *****/          if (ret >= 0)
735          ccs_realpath_lock();                  bprm->argc++;
736          for (;;) {          return ret;
                 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {  
                         /* Global root? */  
                         if (vfsmnt->mnt_parent == vfsmnt)  
                                 break;  
                         dentry = vfsmnt->mnt_mountpoint;  
                         vfsmnt = vfsmnt->mnt_parent;  
                         continue;  
                 }  
                 dentry = dentry->d_parent;  
                 depth++;  
         }  
         ccs_realpath_unlock();  
         /***** CRITICAL SECTION END *****/  
         return depth;  
737  }  }
   
 /**  
  * get_root_depth - return the depth of root directory.  
  *  
  * Returns number of directories to strip.  
  */  
 static int get_root_depth(void)  
 {  
         int depth;  
         struct dentry *dentry;  
         struct vfsmount *vfsmnt;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
         struct path root;  
 #endif  
         /***** CRITICAL SECTION START *****/  
         read_lock(&current->fs->lock);  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
         root = current->fs->root;  
         path_get(&current->fs->root);  
         dentry = root.dentry;  
         vfsmnt = root.mnt;  
 #else  
         dentry = dget(current->fs->root);  
         vfsmnt = mntget(current->fs->rootmnt);  
 #endif  
         read_unlock(&current->fs->lock);  
         /***** CRITICAL SECTION END *****/  
         depth = root_depth(dentry, vfsmnt);  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
         path_put(&root);  
738  #else  #else
739          dput(dentry);  static int ccs_copy_argv(char *arg, struct linux_binprm *bprm)
740          mntput(vfsmnt);  {
741  #endif          const int ret = copy_strings_kernel(1, &arg, bprm);
742          return depth;          if (ret >= 0)
743                    bprm->argc++;
744            return ret;
745  }  }
746    #endif
747    
748  /**  /**
749   * try_alt_exec - Try to start execute handler.   * ccs_try_alt_exec - Try to start execute handler.
750   *   *
751   * @r:           Pointer to "struct ccs_request_info".   * @ee: Pointer to "struct ccs_execve".
  * @handler:     Pointer to the name of execute handler.  
  * @eh_path:     Pointer to pointer to the name of execute handler.  
752   *   *
753   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
754   */   */
755  static int try_alt_exec(struct ccs_request_info *r,  static int ccs_try_alt_exec(struct ccs_execve *ee)
                         const struct path_info *handler, char **eh_path)  
756  {  {
757          /*          /*
758           * Contents of modified bprm.           * Contents of modified bprm.
# Line 1395  static int try_alt_exec(struct ccs_reque Line 766  static int try_alt_exec(struct ccs_reque
766           *    = 0           *    = 0
767           *           *
768           * modified bprm->argv[0]           * modified bprm->argv[0]
769           *    = the program's name specified by execute_handler           *    = the program's name specified by *_execute_handler
770           * modified bprm->argv[1]           * modified bprm->argv[1]
771           *    = current->domain_info->domainname->name           *    = ccs_current_domain()->domainname->name
772           * modified bprm->argv[2]           * modified bprm->argv[2]
773           *    = the current process's name           *    = the current process's name
774           * modified bprm->argv[3]           * modified bprm->argv[3]
# Line 1419  static int try_alt_exec(struct ccs_reque Line 790  static int try_alt_exec(struct ccs_reque
790           * modified bprm->argv[bprm->envc + bprm->argc + 6]           * modified bprm->argv[bprm->envc + bprm->argc + 6]
791           *     = original bprm->envp[bprm->envc - 1]           *     = original bprm->envp[bprm->envc - 1]
792           */           */
793          struct linux_binprm *bprm = r->bprm;          struct linux_binprm *bprm = ee->bprm;
794          struct file *filp;          struct file *filp;
795          int retval;          int retval;
796          const int original_argc = bprm->argc;          const int original_argc = bprm->argc;
797          const int original_envc = bprm->envc;          const int original_envc = bprm->envc;
798          struct task_struct *task = current;          struct task_struct *task = current;
         char *buffer = r->obj->tmp->buffer;  
         /* Allocate memory for execute handler's pathname. */  
         char *execute_handler = ccs_alloc(sizeof(struct ccs_page_buffer));  
         *eh_path = execute_handler;  
         if (!execute_handler)  
                 return -ENOMEM;  
         strncpy(execute_handler, handler->name,  
                 sizeof(struct ccs_page_buffer) - 1);  
         unescape(execute_handler);  
799    
800          /* Close the requested program's dentry. */          /* Close the requested program's dentry. */
801            ee->obj.path1.dentry = NULL;
802            ee->obj.path1.mnt = NULL;
803            ee->obj.validate_done = false;
804          allow_write_access(bprm->file);          allow_write_access(bprm->file);
805          fput(bprm->file);          fput(bprm->file);
806          bprm->file = NULL;          bprm->file = NULL;
807    
808          { /* Adjust root directory for open_exec(). */          /* Invalidate page dump cache. */
809                  int depth = get_root_depth();          ee->dump.page = NULL;
                 char *cp = execute_handler;  
                 if (!*cp || *cp != '/')  
                         return -ENOENT;  
                 while (depth) {  
                         cp = strchr(cp + 1, '/');  
                         if (!cp)  
                                 return -ENOENT;  
                         depth--;  
                 }  
                 memmove(execute_handler, cp, strlen(cp) + 1);  
         }  
810    
811          /* Move envp[] to argv[] */          /* Move envp[] to argv[] */
812          bprm->argc += bprm->envc;          bprm->argc += bprm->envc;
# Line 1460  static int try_alt_exec(struct ccs_reque Line 814  static int try_alt_exec(struct ccs_reque
814    
815          /* Set argv[6] */          /* Set argv[6] */
816          {          {
817                  snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d",                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc);
818                           original_envc);                  retval = ccs_copy_argv(ee->tmp, bprm);
                 retval = copy_strings_kernel(1, &buffer, bprm);  
819                  if (retval < 0)                  if (retval < 0)
820                          goto out;                          goto out;
                 bprm->argc++;  
821          }          }
822    
823          /* Set argv[5] */          /* Set argv[5] */
824          {          {
825                  snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d",                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc);
826                           original_argc);                  retval = ccs_copy_argv(ee->tmp, bprm);
                 retval = copy_strings_kernel(1, &buffer, bprm);  
827                  if (retval < 0)                  if (retval < 0)
828                          goto out;                          goto out;
                 bprm->argc++;  
829          }          }
830    
831          /* Set argv[4] */          /* Set argv[4] */
832          {          {
833                  retval = copy_strings_kernel(1, &bprm->filename, bprm);                  retval = ccs_copy_argv(bprm->filename, bprm);
834                  if (retval < 0)                  if (retval < 0)
835                          goto out;                          goto out;
                 bprm->argc++;  
836          }          }
837    
838          /* Set argv[3] */          /* Set argv[3] */
839          {          {
840                  const u32 tomoyo_flags = task->tomoyo_flags;                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1,
                 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1,  
841                           "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "                           "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
842                           "sgid=%d fsuid=%d fsgid=%d state[0]=%u "                           "sgid=%d fsuid=%d fsgid=%d",
843                           "state[1]=%u state[2]=%u",                           (pid_t) ccsecurity_exports.sys_getpid(),
844                           task->pid, task->uid, task->gid, task->euid,                           current_uid(), current_gid(), current_euid(),
845                           task->egid, task->suid, task->sgid, task->fsuid,                           current_egid(), current_suid(), current_sgid(),
846                           task->fsgid, (u8) (tomoyo_flags >> 24),                           current_fsuid(), current_fsgid());
847                           (u8) (tomoyo_flags >> 16), (u8) (tomoyo_flags >> 8));                  retval = ccs_copy_argv(ee->tmp, bprm);
                 retval = copy_strings_kernel(1, &buffer, bprm);  
848                  if (retval < 0)                  if (retval < 0)
849                          goto out;                          goto out;
                 bprm->argc++;  
850          }          }
851    
852          /* Set argv[2] */          /* Set argv[2] */
853          {          {
854                  char *exe = (char *) ccs_get_exe();                  char *exe = (char *) ccs_get_exe();
855                  if (exe) {                  if (exe) {
856                          retval = copy_strings_kernel(1, &exe, bprm);                          retval = ccs_copy_argv(exe, bprm);
857                          ccs_free(exe);                          kfree(exe);
858                  } else {                  } else {
859                          snprintf(buffer, sizeof(struct ccs_page_buffer) - 1,  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
860                                   "<unknown>");                          retval = ccs_copy_argv("<unknown>", bprm);
861                          retval = copy_strings_kernel(1, &buffer, bprm);  #else
862                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
863                            retval = ccs_copy_argv(ee->tmp, bprm);
864    #endif
865                  }                  }
866                  if (retval < 0)                  if (retval < 0)
867                          goto out;                          goto out;
                 bprm->argc++;  
868          }          }
869    
870          /* Set argv[1] */          /* Set argv[1] */
871          {          {
872                  strncpy(buffer, task->domain_info->domainname->name,  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
873                          sizeof(struct ccs_page_buffer) - 1);                  retval = ccs_copy_argv(ccs_current_domain()->domainname->name,
874                  retval = copy_strings_kernel(1, &buffer, bprm);                                         bprm);
875    #else
876                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
877                             ccs_current_domain()->domainname->name);
878                    retval = ccs_copy_argv(ee->tmp, bprm);
879    #endif
880                  if (retval < 0)                  if (retval < 0)
881                          goto out;                          goto out;
                 bprm->argc++;  
882          }          }
883    
884          /* Set argv[0] */          /* Set argv[0] */
885          {          {
886                  retval = copy_strings_kernel(1, &execute_handler, bprm);                  struct path root;
887                    char *cp;
888                    int root_len;
889                    int handler_len;
890                    get_fs_root(current->fs, &root);
891                    cp = ccs_realpath_from_path(&root);
892                    path_put(&root);
893                    if (!cp) {
894                            retval = -ENOMEM;
895                            goto out;
896                    }
897                    root_len = strlen(cp);
898                    retval = strncmp(ee->handler->name, cp, root_len);
899                    root_len--;
900                    kfree(cp);
901                    if (retval) {
902                            retval = -ENOENT;
903                            goto out;
904                    }
905                    handler_len = ee->handler->total_len + 1;
906                    cp = kmalloc(handler_len, CCS_GFP_FLAGS);
907                    if (!cp) {
908                            retval = -ENOMEM;
909                            goto out;
910                    }
911                    /* ee->handler_path is released by ccs_finish_execve(). */
912                    ee->handler_path = cp;
913                    /* Adjust root directory for open_exec(). */
914                    memmove(cp, ee->handler->name + root_len,
915                            handler_len - root_len);
916                    ccs_unescape(cp);
917                    retval = -ENOENT;
918                    if (!*cp || *cp != '/')
919                            goto out;
920                    retval = ccs_copy_argv(cp, bprm);
921                  if (retval < 0)                  if (retval < 0)
922                          goto out;                          goto out;
                 bprm->argc++;  
923          }          }
924  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
925  #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)  #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
# Line 1542  static int try_alt_exec(struct ccs_reque Line 927  static int try_alt_exec(struct ccs_reque
927  #endif  #endif
928  #endif  #endif
929    
930          /* OK, now restart the process with execute handler program's dentry. */          /*
931          filp = open_exec(execute_handler);           * OK, now restart the process with execute handler program's dentry.
932             */
933            filp = open_exec(ee->handler_path);
934          if (IS_ERR(filp)) {          if (IS_ERR(filp)) {
935                  retval = PTR_ERR(filp);                  retval = PTR_ERR(filp);
936                  goto out;                  goto out;
937          }          }
938            ee->obj.path1.dentry = filp->f_dentry;
939            ee->obj.path1.mnt = filp->f_vfsmnt;
940          bprm->file = filp;          bprm->file = filp;
941          bprm->filename = execute_handler;          bprm->filename = ee->handler_path;
942  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
943          bprm->interp = execute_handler;          bprm->interp = bprm->filename;
944  #endif  #endif
945          retval = prepare_binprm(bprm);          retval = prepare_binprm(bprm);
946          if (retval < 0)          if (retval < 0)
947                  goto out;                  goto out;
948          task->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;          task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
949          retval = find_next_domain(r, handler);          retval = ccs_find_next_domain(ee);
950          task->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;          task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
951   out:   out:
952          return retval;          return retval;
953  }  }
954    
955  /**  /**
956   * find_execute_handler - Find an execute handler.   * ccs_find_execute_handler - Find an execute handler.
957   *   *
958     * @ee:   Pointer to "struct ccs_execve".
959   * @type: Type of execute handler.   * @type: Type of execute handler.
960   *   *
961   * Returns pointer to "struct path_info" if found, NULL otherwise.   * Returns true if found, false otherwise.
962     *
963     * Caller holds ccs_read_lock().
964   */   */
965  static const struct path_info *find_execute_handler(const u8 type)  static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type)
966  {  {
967          struct task_struct *task = current;          struct ccs_request_info *r = &ee->r;
         const struct domain_info *domain = task->domain_info;  
         struct acl_info *ptr;  
968          /*          /*
969           * Don't use execute handler if the current process is           * To avoid infinite execute handler loop, don't use execute handler
970           * marked as execute handler to avoid infinite execute handler loop.           * if the current process is marked as execute handler .
971           */           */
972          if (task->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER)          if (current->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
973                  return NULL;                  return false;
974          list1_for_each_entry(ptr, &domain->acl_info_list, list) {          r->param_type = type;
975                  struct execute_handler_record *acl;          ccs_check_acl(r, NULL);
976                  if (ptr->type != type)          if (!r->granted)
977                          continue;                  return false;
978                  acl = container_of(ptr, struct execute_handler_record, head);          ee->handler = container_of(r->matched_acl, struct ccs_handler_acl,
979                  return acl->handler;                                     head)->handler;
980          }          return true;
         return NULL;  
981  }  }
982    
983  /* List of next_domain which is used for checking interpreter's permissions. */  #ifdef CONFIG_MMU
984  struct execve_entry {  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
985          struct list_head list;  #define CCS_BPRM_MMU
986          struct task_struct *task;  #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3
987          struct domain_info *next_domain;  #define CCS_BPRM_MMU
988  };  #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2
989    #define CCS_BPRM_MMU
990  static LIST_HEAD(execve_list);  #endif
991  static DEFINE_SPINLOCK(execve_list_lock);  #endif
992    
993  /**  /**
994   * ccs_register_next_domain - Remember next_domain.   * ccs_dump_page - Dump a page to buffer.
995   *   *
996   * @next_domain: Pointer to "struct domain_info".   * @bprm: Pointer to "struct linux_binprm".
997     * @pos:  Location to dump.
998     * @dump: Poiner to "struct ccs_page_dump".
999   *   *
1000   * Returns 0 on success, -ENOMEM otherwise.   * Returns true on success, false otherwise.
1001   */   */
1002  static int ccs_register_next_domain(struct domain_info *next_domain)  bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos,
1003                       struct ccs_page_dump *dump)
1004  {  {
1005          struct execve_entry *ee = kmalloc(sizeof(*ee), GFP_KERNEL);          struct page *page;
1006          if (!ee)          /* dump->data is released by ccs_finish_execve(). */
1007                  return -ENOMEM;          if (!dump->data) {
1008          ee->task = current;                  dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS);
1009          ee->next_domain = next_domain;                  if (!dump->data)
1010          /***** CRITICAL SECTION START *****/                          return false;
1011          spin_lock(&execve_list_lock);          }
1012          list_add(&ee->list, &execve_list);          /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
1013          spin_unlock(&execve_list_lock);  #ifdef CCS_BPRM_MMU
1014          /***** CRITICAL SECTION END *****/          if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1015          return 0;                  return false;
1016    #else
1017            page = bprm->page[pos / PAGE_SIZE];
1018    #endif
1019            if (page != dump->page) {
1020                    const unsigned int offset = pos % PAGE_SIZE;
1021                    /*
1022                     * Maybe kmap()/kunmap() should be used here.
1023                     * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
1024                     * So do I.
1025                     */
1026                    char *kaddr = kmap_atomic(page, KM_USER0);
1027                    dump->page = page;
1028                    memcpy(dump->data + offset, kaddr + offset,
1029                           PAGE_SIZE - offset);
1030                    kunmap_atomic(kaddr, KM_USER0);
1031            }
1032            /* Same with put_arg_page(page) in fs/exec.c */
1033    #ifdef CCS_BPRM_MMU
1034            put_page(page);
1035    #endif
1036            return true;
1037  }  }
1038    
1039  /**  /**
1040   * ccs_fetch_next_domain - Fetch next_domain from the list.   * ccs_start_execve - Prepare for execve() operation.
1041   *   *
1042   * Returns pointer to "struct domain_info" which will be used if execve()   * @bprm: Pointer to "struct linux_binprm".
1043   * succeeds. This function does not return NULL.   * @eep:  Pointer to "struct ccs_execve *".
1044     *
1045     * Returns 0 on success, negative value otherwise.
1046   */   */
1047  struct domain_info *ccs_fetch_next_domain(void)  static int ccs_start_execve(struct linux_binprm *bprm,
1048                                struct ccs_execve **eep)
1049  {  {
1050            int retval;
1051          struct task_struct *task = current;          struct task_struct *task = current;
1052          struct domain_info *next_domain = task->domain_info;          struct ccs_execve *ee;
1053          struct execve_entry *p;          *eep = NULL;
1054          /***** CRITICAL SECTION START *****/          ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS);
1055          spin_lock(&execve_list_lock);          if (!ee)
1056          list_for_each_entry(p, &execve_list, list) {                  return -ENOMEM;
1057                  if (p->task != task)          ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
1058                          continue;          if (!ee->tmp) {
1059                  next_domain = p->next_domain;                  kfree(ee);
1060                  break;                  return -ENOMEM;
1061          }          }
1062          spin_unlock(&execve_list_lock);          ee->reader_idx = ccs_read_lock();
1063          /***** CRITICAL SECTION END *****/          /* ee->dump->data is allocated by ccs_dump_page(). */
1064          return next_domain;          ee->previous_domain = task->ccs_domain_info;
1065            /* Clear manager flag. */
1066            task->ccs_flags &= ~CCS_TASK_IS_MANAGER;
1067            *eep = ee;
1068            ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE);
1069            ee->r.ee = ee;
1070            ee->bprm = bprm;
1071            ee->r.obj = &ee->obj;
1072            ee->obj.path1.dentry = bprm->file->f_dentry;
1073            ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1074            /*
1075             * No need to call ccs_environ() for execute handler because envp[] is
1076             * moved to argv[].
1077             */
1078            if (ccs_find_execute_handler(ee, CCS_TYPE_AUTO_EXECUTE_HANDLER))
1079                    return ccs_try_alt_exec(ee);
1080            retval = ccs_find_next_domain(ee);
1081            if (retval == -EPERM) {
1082                    if (ccs_find_execute_handler(ee,
1083                                                 CCS_TYPE_DENIED_EXECUTE_HANDLER))
1084                            return ccs_try_alt_exec(ee);
1085            }
1086            if (!retval)
1087                    retval = ccs_environ(ee);
1088            return retval;
1089  }  }
1090    
1091  /**  /**
1092   * ccs_unregister_next_domain - Forget next_domain.   * ccs_finish_execve - Clean up execve() operation.
1093     *
1094     * @retval: Return code of an execve() operation.
1095     * @ee:     Pointer to "struct ccs_execve".
1096     *
1097     * Caller holds ccs_read_lock().
1098   */   */
1099  static void ccs_unregister_next_domain(void)  static void ccs_finish_execve(int retval, struct ccs_execve *ee)
1100  {  {
1101          struct task_struct *task = current;          struct task_struct *task = current;
1102          struct execve_entry *p;          if (!ee)
1103          struct execve_entry *ee = NULL;                  return;
1104          /***** CRITICAL SECTION START *****/          if (retval < 0) {
1105          spin_lock(&execve_list_lock);                  task->ccs_domain_info = ee->previous_domain;
1106          list_for_each_entry(p, &execve_list, list) {                  /*
1107                  if (p->task != task)                   * Make task->ccs_domain_info visible to GC before changing
1108                          continue;                   * task->ccs_flags .
1109                  list_del(&p->list);                   */
1110                  ee = p;                  smp_mb();
1111                  break;          } else {
1112                    /* Mark the current process as execute handler. */
1113                    if (ee->handler)
1114                            task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1115                    /* Mark the current process as normal process. */
1116                    else
1117                            task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1118          }          }
1119          spin_unlock(&execve_list_lock);          /* Tell GC that I finished execve(). */
1120          /***** CRITICAL SECTION END *****/          task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE;
1121            ccs_read_unlock(ee->reader_idx);
1122            kfree(ee->handler_path);
1123            kfree(ee->tmp);
1124            kfree(ee->dump.data);
1125          kfree(ee);          kfree(ee);
1126  }  }
1127    
1128  /**  static int __ccs_search_binary_handler(struct linux_binprm *bprm,
1129   * search_binary_handler_with_transition - Perform domain transition.                                         struct pt_regs *regs)
  *  
  * @bprm: Pointer to "struct linux_binprm".  
  * @regs: Pointer to "struct pt_regs".  
  *  
  * Returns result of search_binary_handler() on success,  
  * negative value otherwise.  
  */  
 int search_binary_handler_with_transition(struct linux_binprm *bprm,  
                                           struct pt_regs *regs)  
1130  {  {
1131            struct ccs_execve *ee;
1132          int retval;          int retval;
1133          struct task_struct *task = current;          if (!ccs_policy_loaded)
1134          const struct path_info *handler;                  ccsecurity_exports.load_policy(bprm->filename);
1135          struct ccs_request_info r;          retval = ccs_start_execve(bprm, &ee);
1136          struct obj_info obj;          if (!retval)
1137          /*                  retval = search_binary_handler(bprm, regs);
1138           * "eh_path" holds path to execute handler program.          ccs_finish_execve(retval, ee);
          * Thus, keep valid until search_binary_handler() finishes.  
          */  
         char *eh_path = NULL;  
         struct ccs_page_buffer *tmp = ccs_alloc(sizeof(struct ccs_page_buffer));  
         memset(&obj, 0, sizeof(obj));  
         if (!sbin_init_started)  
                 ccs_load_policy(bprm->filename);  
         if (!tmp)  
                 return -ENOMEM;  
   
         ccs_init_request_info(&r, NULL, CCS_TOMOYO_MAC_FOR_FILE);  
         r.bprm = bprm;  
         r.obj = &obj;  
         obj.path1_dentry = bprm->file->f_dentry;  
         obj.path1_vfsmnt = bprm->file->f_vfsmnt;  
         obj.tmp = tmp;  
   
         /* Clear manager flag. */  
         task->tomoyo_flags &= ~CCS_TASK_IS_POLICY_MANAGER;  
         handler = find_execute_handler(TYPE_EXECUTE_HANDLER);  
         if (handler) {  
                 retval = try_alt_exec(&r, handler, &eh_path);  
                 if (!retval)  
                         audit_execute_handler_log(true, handler->name, bprm);  
                 goto ok;  
         }  
         retval = find_next_domain(&r, NULL);  
         if (retval != -EPERM)  
                 goto ok;  
         handler = find_execute_handler(TYPE_DENIED_EXECUTE_HANDLER);  
         if (handler) {  
                 retval = try_alt_exec(&r, handler, &eh_path);  
                 if (!retval)  
                         audit_execute_handler_log(false, handler->name, bprm);  
         }  
  ok:  
         if (retval < 0)  
                 goto out;  
         r.mode = ccs_check_flags(r.domain, CCS_TOMOYO_MAC_FOR_ENV);  
         retval = check_environ(&r);  
         if (retval < 0)  
                 goto out;  
         retval = ccs_register_next_domain(r.domain);  
         if (retval < 0)  
                 goto out;  
         task->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;  
         retval = search_binary_handler(bprm, regs);  
         task->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;  
         if (retval < 0)  
                 goto out;  
         /* Proceed to next domain if execution suceeded. */  
         task->domain_info = r.domain;  
         mb(); /* Make domain transition visible to other CPUs. */  
         /* Mark the current process as execute handler. */  
         if (handler)  
                 task->tomoyo_flags |= TOMOYO_TASK_IS_EXECUTE_HANDLER;  
         /* Mark the current process as normal process. */  
         else  
                 task->tomoyo_flags &= ~TOMOYO_TASK_IS_EXECUTE_HANDLER;  
  out:  
         ccs_unregister_next_domain();  
         ccs_free(eh_path);  
         ccs_free(tmp);  
1139          return retval;          return retval;
1140  }  }
1141    
1142  #else  void __init ccs_domain_init(void)
   
 /**  
  * search_binary_handler_with_transition - Wrapper for search_binary_handler().  
  *  
  * @bprm: Pointer to "struct linux_binprm".  
  * @regs: Pointer to "struct pt_regs".  
  *  
  * Returns the result of search_binary_handler().  
  */  
 int search_binary_handler_with_transition(struct linux_binprm *bprm,  
                                           struct pt_regs *regs)  
1143  {  {
1144  #ifdef CONFIG_SAKURA          ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler;
         /* Clear manager flag. */  
         current->tomoyo_flags &= ~CCS_TASK_IS_POLICY_MANAGER;  
         ccs_load_policy(bprm->filename);  
 #endif  
         return search_binary_handler(bprm, regs);  
1145  }  }
   
 #endif  

Legend:
Removed from v.1700  
changed lines
  Added in v.3968

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26