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

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.5.x/ccs-patch/fs/tomoyo_domain.c revision 851 by kumaneko, Wed Jan 2 06:29:56 2008 UTC branches/ccs-patch/security/ccsecurity/domain.c revision 3691 by kumaneko, Sun May 23 03:22:24 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.7.2+   2010/05/05
  *  
  * Version: 1.5.3-pre   2008/01/02  
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   */   */
 /***** TOMOYO Linux start. *****/  
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)
17  #ifndef for_each_process  #include <linux/namei.h>
18  #define for_each_process for_each_task  #include <linux/mount.h>
19    #endif
20    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21    #include <linux/fs_struct.h>
22  #endif  #endif
23    #include "internal.h"
24    
25  /*************************  VARIABLES  *************************/  /* Variables definitions.*/
26    
27  /* The initial domain. */  /* The initial domain. */
28  struct domain_info KERNEL_DOMAIN;  struct ccs_domain_info ccs_kernel_domain;
   
 /* List of domains. */  
 LIST1_HEAD(domain_list);  
   
 /* /sbin/init started? */  
 extern int sbin_init_started;  
   
 #ifdef CONFIG_TOMOYO  
   
 /* Lock for appending domain's ACL. */  
 DEFINE_MUTEX(domain_acl_lock);  
   
 /*************************  UTILITY FUNCTIONS  *************************/  
   
 /***** The structure for program files to force domain reconstruction. *****/  
   
 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;  
         bool is_last_name;  
 };  
   
 /***** The structure for domains to not to transit domains. *****/  
   
 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;  
         bool is_last_name;  
 };  
29    
30  /***** The structure for program files that should be aggregated. *****/  /* The list for "struct ccs_domain_info". */
31    LIST_HEAD(ccs_domain_list);
32    
33  struct aggregator_entry {  struct list_head ccs_policy_list[CCS_MAX_POLICY];
34          struct list1_head list;  struct list_head ccs_group_list[CCS_MAX_GROUP];
35          const struct path_info *original_name;  struct list_head ccs_shared_list[CCS_MAX_LIST];
         const struct path_info *aggregated_name;  
         bool is_deleted;  
 };  
36    
37  /***** The structure for program files that should be aliased. *****/  /**
38     * ccs_audit_execute_handler_log - Audit execute_handler log.
39  struct alias_entry {   *
40          struct list1_head list;   * @ee:         Pointer to "struct ccs_execve_entry".
41          const struct path_info *original_name;   *
42          const struct path_info *aliased_name;   * Returns 0 on success, negative value otherwise.
43          bool is_deleted;   */
44  };  static int ccs_audit_execute_handler_log(struct ccs_execve_entry *ee)
   
 /*************************  VARIABLES  *************************/  
   
 /* Domain creation lock. */  
 static DEFINE_MUTEX(new_domain_assign_lock);  
   
 /*************************  UTILITY FUNCTIONS  *************************/  
   
 const char *GetLastName(const struct domain_info *domain)  
45  {  {
46          const char *cp0 = domain->domainname->name, *cp1;          struct ccs_request_info *r = &ee->r;
47          if ((cp1 = strrchr(cp0, ' ')) != NULL) return cp1 + 1;          const char *handler = ee->handler->name;
48          return cp0;          r->type = CCS_MAC_FILE_EXECUTE;
49            r->mode = ccs_get_mode(r->profile, CCS_MAC_FILE_EXECUTE);
50            return ccs_write_audit_log(true, r, "%s" CCS_KEYWORD_EXECUTE_HANDLER
51                                       " %s\n", ee->handler_type ==
52                                       CCS_TYPE_DENIED_EXECUTE_HANDLER ?
53                                       "denied_" : "", handler);
54  }  }
55    
56  int AddDomainACL(struct domain_info *domain, struct acl_info *acl)  /**
57     * ccs_audit_domain_creation_log - Audit domain creation log.
58     *
59     * Returns 0 on success, negative value otherwise.
60     */
61    static int ccs_audit_domain_creation_log(void)
62  {  {
63          list1_add_tail_mb(&acl->list, &domain->acl_info_list);          struct ccs_request_info r;
64          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);          ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE);
65          return 0;          return ccs_write_audit_log(false, &r, "use_profile %u\n", r.profile);
66    }
67    
68    int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
69                          bool is_delete, const int idx, bool (*check_duplicate)
70                          (const struct ccs_acl_head *,
71                           const struct ccs_acl_head *))
72    {
73            int error = is_delete ? -ENOENT : -ENOMEM;
74            struct ccs_acl_head *entry;
75            if (mutex_lock_interruptible(&ccs_policy_lock))
76                    return -ENOMEM;
77            list_for_each_entry_rcu(entry, &ccs_policy_list[idx], list) {
78                    if (!check_duplicate(entry, new_entry))
79                            continue;
80                    entry->is_deleted = is_delete;
81                    error = 0;
82                    break;
83            }
84            if (error && !is_delete) {
85                    entry = ccs_commit_ok(new_entry, size);
86                    if (entry) {
87                            list_add_tail_rcu(&entry->list, &ccs_policy_list[idx]);
88                            error = 0;
89                    }
90            }
91            mutex_unlock(&ccs_policy_lock);
92            return error;
93  }  }
94    
95  int DelDomainACL(struct acl_info *ptr)  int ccs_update_group(struct ccs_acl_head *new_entry, const int size,
96  {                       bool is_delete, struct ccs_group *group,
97          ptr->is_deleted = 1;                       bool (*check_duplicate) (const struct ccs_acl_head *,
98          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);                                                const struct ccs_acl_head *))
99          return 0;  {
100            int error = is_delete ? -ENOENT : -ENOMEM;
101            struct ccs_acl_head *entry;
102            if (mutex_lock_interruptible(&ccs_policy_lock))
103                    return -ENOMEM;
104            list_for_each_entry_rcu(entry, &group->member_list, list) {
105                    if (!check_duplicate(entry, new_entry))
106                            continue;
107                    entry->is_deleted = is_delete;
108                    error = 0;
109                    break;
110            }
111            if (!is_delete && error) {
112                    entry = ccs_commit_ok(new_entry, size);
113                    if (entry) {
114                            list_add_tail_rcu(&entry->list, &group->member_list);
115                            error = 0;
116                    }
117            }
118            mutex_unlock(&ccs_policy_lock);
119            return error;
120  }  }
121    
122  /*************************  DOMAIN INITIALIZER HANDLER  *************************/  int ccs_update_domain_policy(struct ccs_acl_info *new_entry, const int size,
123                                 bool is_delete, struct ccs_domain_info *domain,
124  static LIST1_HEAD(domain_initializer_list);                               bool (*check_duplicate)
125                                 (const struct ccs_acl_info *,
126  static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)                                const struct ccs_acl_info *),
127  {                               bool (*merge_duplicate) (struct ccs_acl_info *,
128          struct domain_initializer_entry *new_entry, *ptr;                                                        struct ccs_acl_info *,
129          static DEFINE_MUTEX(lock);                                                        const bool))
130          const struct path_info *saved_program, *saved_domainname = NULL;  {
131          int error = -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
132          bool is_last_name = 0;          struct ccs_acl_info *entry;
133          if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */          if (mutex_lock_interruptible(&ccs_policy_lock))
134          if (domainname) {                  return error;
135                  if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {          list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
136                          is_last_name = 1;                  if (!check_duplicate(entry, new_entry))
137                  } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {                          continue;
138                          return -EINVAL;                  if (merge_duplicate)
139                  }                          entry->is_deleted = merge_duplicate(entry, new_entry,
140                  if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;                                                              is_delete);
141                    else
142                            entry->is_deleted = is_delete;
143                    error = 0;
144                    break;
145          }          }
146          if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;          if (error && !is_delete) {
147          mutex_lock(&lock);                  entry = ccs_commit_ok(new_entry, size);
148          list1_for_each_entry(ptr, &domain_initializer_list, list) {                  if (entry) {
149                  if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {                          ccs_add_domain_acl(domain, entry);
                         ptr->is_deleted = is_delete;  
150                          error = 0;                          error = 0;
                         goto out;  
151                  }                  }
152          }          }
153          if (is_delete) {          mutex_unlock(&ccs_policy_lock);
                 error = -ENOENT;  
                 goto out;  
         }  
         if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) 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;  
  out:  
         mutex_unlock(&lock);  
154          return error;          return error;
155  }  }
156    
157  int ReadDomainInitializerPolicy(struct io_buffer *head)  static bool ccs_is_same_domain_initializer_entry(const struct ccs_acl_head *a,
158                                                     const struct ccs_acl_head *b)
159  {  {
160          struct list1_head *pos;          const struct ccs_domain_initializer_entry *p1 =
161          list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {                  container_of(a, typeof(*p1), head);
162                  struct domain_initializer_entry *ptr;          const struct ccs_domain_initializer_entry *p2 =
163                  ptr = list1_entry(pos, struct domain_initializer_entry, list);                  container_of(b, typeof(*p2), head);
164                  if (ptr->is_deleted) continue;          return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name
165                  if (ptr->domainname) {                  && p1->domainname == p2->domainname
166                          if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;                  && p1->program == p2->program;
167                  } else {  }
168                          if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) return -ENOMEM;  
169                  }  /**
170     * ccs_update_domain_initializer_entry - Update "struct ccs_domain_initializer_entry" list.
171     *
172     * @domainname: The name of domain. May be NULL.
173     * @program:    The name of program.
174     * @is_not:     True if it is "no_initialize_domain" entry.
175     * @is_delete:  True if it is a delete request.
176     *
177     * Returns 0 on success, negative value otherwise.
178     */
179    static int ccs_update_domain_initializer_entry(const char *domainname,
180                                                   const char *program,
181                                                   const bool is_not,
182                                                   const bool is_delete)
183    {
184            struct ccs_domain_initializer_entry e = { .is_not = is_not };
185            int error = is_delete ? -ENOENT : -ENOMEM;
186            if (!ccs_is_correct_path(program, 1, -1, -1))
187                    return -EINVAL; /* No patterns allowed. */
188            if (domainname) {
189                    if (!ccs_is_domain_def(domainname) &&
190                        ccs_is_correct_path(domainname, 1, -1, -1))
191                            e.is_last_name = true;
192                    else if (!ccs_is_correct_domain(domainname))
193                            return -EINVAL;
194                    e.domainname = ccs_get_name(domainname);
195                    if (!e.domainname)
196                            goto out;
197          }          }
198          return 0;          e.program = ccs_get_name(program);
199            if (!e.program)
200                    goto out;
201            error = ccs_update_policy(&e.head, sizeof(e), is_delete,
202                                      CCS_ID_DOMAIN_INITIALIZER,
203                                      ccs_is_same_domain_initializer_entry);
204     out:
205            ccs_put_name(e.domainname);
206            ccs_put_name(e.program);
207            return error;
208  }  }
209    
210  int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete)  /**
211     * ccs_write_domain_initializer_policy - Write "struct ccs_domain_initializer_entry" list.
212     *
213     * @data:      String to parse.
214     * @is_delete: True if it is a delete request.
215     *
216     * Returns 0 on success, negative value otherwise.
217     */
218    int ccs_write_domain_initializer_policy(char *data, const bool is_delete, const u8 flags)
219  {  {
220          char *cp = strstr(data, " from ");          char *domainname = strstr(data, " from ");
221          if (cp) {          if (domainname) {
222                  *cp = '\0';                  *domainname = '\0';
223                  return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete);                  domainname += 6;
         } else {  
                 return AddDomainInitializerEntry(NULL, data, is_not, is_delete);  
224          }          }
225            return ccs_update_domain_initializer_entry(domainname, data, flags,
226                                                       is_delete);
227  }  }
228    
229  static bool IsDomainInitializer(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)  /**
230  {   * ccs_is_domain_initializer - Check whether the given program causes domainname reinitialization.
231          struct domain_initializer_entry *ptr;   *
232          bool flag = 0;   * @domainname: The name of domain.
233          list1_for_each_entry(ptr,  &domain_initializer_list, list) {   * @program:    The name of program.
234                  if (ptr->is_deleted) continue;   * @last_name:  The last component of @domainname.
235     *
236     * Returns true if executing @program reinitializes domain transition,
237     * false otherwise.
238     *
239     * Caller holds ccs_read_lock().
240     */
241    static bool ccs_is_domain_initializer(const struct ccs_path_info *domainname,
242                                          const struct ccs_path_info *program,
243                                          const struct ccs_path_info *last_name)
244    {
245            struct ccs_domain_initializer_entry *ptr;
246            bool flag = false;
247            list_for_each_entry_rcu(ptr, &ccs_policy_list
248                                    [CCS_ID_DOMAIN_INITIALIZER], head.list) {
249                    if (ptr->head.is_deleted)
250                            continue;
251                  if (ptr->domainname) {                  if (ptr->domainname) {
252                          if (!ptr->is_last_name) {                          if (!ptr->is_last_name) {
253                                  if (ptr->domainname != domainname) continue;                                  if (ptr->domainname != domainname)
254                                            continue;
255                          } else {                          } else {
256                                  if (pathcmp(ptr->domainname, last_name)) continue;                                  if (ccs_pathcmp(ptr->domainname, last_name))
257                                            continue;
258                          }                          }
259                  }                  }
260                  if (pathcmp(ptr->program, program)) continue;                  if (ccs_pathcmp(ptr->program, program))
261                  if (ptr->is_not) return 0;                          continue;
262                  flag = 1;                  if (ptr->is_not) {
263                            flag = false;
264                            break;
265                    }
266                    flag = true;
267          }          }
268          return flag;          return flag;
269  }  }
270    
271  /*************************  DOMAIN KEEPER HANDLER  *************************/  static bool ccs_is_same_domain_keeper_entry(const struct ccs_acl_head *a,
272                                                const struct ccs_acl_head *b)
 static LIST1_HEAD(domain_keeper_list);  
   
 static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)  
273  {  {
274          struct domain_keeper_entry *new_entry, *ptr;          const struct ccs_domain_keeper_entry *p1 =
275          const struct path_info *saved_domainname, *saved_program = NULL;                  container_of(a, typeof(*p1), head);
276          static DEFINE_MUTEX(lock);          const struct ccs_domain_keeper_entry *p2 =
277          int error = -ENOMEM;                  container_of(b, typeof(*p2), head);
278          bool is_last_name = 0;          return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name
279          if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {                  && p1->domainname == p2->domainname
280                  is_last_name = 1;                  && p1->program == p2->program;
281          } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {  }
282    
283    /**
284     * ccs_update_domain_keeper_entry - Update "struct ccs_domain_keeper_entry" list.
285     *
286     * @domainname: The name of domain.
287     * @program:    The name of program. May be NULL.
288     * @is_not:     True if it is "no_keep_domain" entry.
289     * @is_delete:  True if it is a delete request.
290     *
291     * Returns 0 on success, negative value otherwise.
292     */
293    static int ccs_update_domain_keeper_entry(const char *domainname,
294                                              const char *program,
295                                              const bool is_not,
296                                              const bool is_delete)
297    {
298            struct ccs_domain_keeper_entry e = { .is_not = is_not };
299            int error = is_delete ? -ENOENT : -ENOMEM;
300            if (!ccs_is_domain_def(domainname) &&
301                ccs_is_correct_path(domainname, 1, -1, -1))
302                    e.is_last_name = true;
303            else if (!ccs_is_correct_domain(domainname))
304                  return -EINVAL;                  return -EINVAL;
         }  
305          if (program) {          if (program) {
306                  if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;                  if (!ccs_is_correct_path(program, 1, -1, -1))
307                  if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;                          return -EINVAL;
308          }                  e.program = ccs_get_name(program);
309          if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;                  if (!e.program)
         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) {  
                         ptr->is_deleted = is_delete;  
                         error = 0;  
310                          goto out;                          goto out;
                 }  
311          }          }
312          if (is_delete) {          e.domainname = ccs_get_name(domainname);
313                  error = -ENOENT;          if (!e.domainname)
314                  goto out;                  goto out;
315          }          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
316          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;                                    CCS_ID_DOMAIN_KEEPER,
317          new_entry->domainname = saved_domainname;                                    ccs_is_same_domain_keeper_entry);
         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;  
318   out:   out:
319          mutex_unlock(&lock);          ccs_put_name(e.domainname);
320            ccs_put_name(e.program);
321          return error;          return error;
322  }  }
323    
324  int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete)  /**
325     * ccs_write_domain_keeper_policy - Write "struct ccs_domain_keeper_entry" list.
326     *
327     * @data:      String to parse.
328     * @is_delete: True if it is a delete request.
329     *
330     * Returns 0 on success, negative value otherwise.
331     */
332    int ccs_write_domain_keeper_policy(char *data, const bool is_delete,
333                                       const u8 flags)
334  {  {
335          char *cp = strstr(data, " from ");          char *domainname = strstr(data, " from ");
336          if (cp) {          if (domainname) {
337                  *cp = '\0';                  *domainname = '\0';
338                  return AddDomainKeeperEntry(cp + 6, data, is_not, is_delete);                  domainname += 6;
339          } else {          } else {
340                  return AddDomainKeeperEntry(data, NULL, is_not, is_delete);                  domainname = data;
341          }                  data = NULL;
 }  
   
 int ReadDomainKeeperPolicy(struct io_buffer *head)  
 {  
         struct list1_head *pos;  
         list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {  
                 struct domain_keeper_entry *ptr;  
                 ptr = list1_entry(pos, struct domain_keeper_entry, list);  
                 if (ptr->is_deleted) continue;  
                 if (ptr->program) {  
                         if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;  
                 } else {  
                         if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->domainname->name)) return -ENOMEM;  
                 }  
342          }          }
343          return 0;          return ccs_update_domain_keeper_entry(domainname, data, flags,
344                                                  is_delete);
345  }  }
346    
347  static bool IsDomainKeeper(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)  /**
348  {   * ccs_is_domain_keeper - Check whether the given program causes domain transition suppression.
349          struct domain_keeper_entry *ptr;   *
350          bool flag = 0;   * @domainname: The name of domain.
351          list1_for_each_entry(ptr, &domain_keeper_list, list) {   * @program:    The name of program.
352                  if (ptr->is_deleted) continue;   * @last_name:  The last component of @domainname.
353     *
354     * Returns true if executing @program supresses domain transition,
355     * false otherwise.
356     *
357     * Caller holds ccs_read_lock().
358     */
359    static bool ccs_is_domain_keeper(const struct ccs_path_info *domainname,
360                                     const struct ccs_path_info *program,
361                                     const struct ccs_path_info *last_name)
362    {
363            struct ccs_domain_keeper_entry *ptr;
364            bool flag = false;
365            list_for_each_entry_rcu(ptr, &ccs_policy_list[CCS_ID_DOMAIN_KEEPER],
366                                    head.list) {
367                    if (ptr->head.is_deleted)
368                            continue;
369                  if (!ptr->is_last_name) {                  if (!ptr->is_last_name) {
370                          if (ptr->domainname != domainname) continue;                          if (ptr->domainname != domainname)
371                                    continue;
372                  } else {                  } else {
373                          if (pathcmp(ptr->domainname, last_name)) continue;                          if (ccs_pathcmp(ptr->domainname, last_name))
374                                    continue;
375                  }                  }
376                  if (ptr->program && pathcmp(ptr->program, program)) continue;                  if (ptr->program && ccs_pathcmp(ptr->program, program))
377                  if (ptr->is_not) return 0;                          continue;
378                  flag = 1;                  if (ptr->is_not) {
379          }                          flag = false;
380          return flag;                          break;
 }  
   
 /*************************  SYMBOLIC LINKED PROGRAM HANDLER  *************************/  
   
 static LIST1_HEAD(alias_list);  
   
 static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete)  
 {  
         struct alias_entry *new_entry, *ptr;  
         static DEFINE_MUTEX(lock);  
         const struct path_info *saved_original_name, *saved_aliased_name;  
         int error = -ENOMEM;  
         if (!IsCorrectPath(original_name, 1, -1, -1, __FUNCTION__) || !IsCorrectPath(aliased_name, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */  
         if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aliased_name = SaveName(aliased_name)) == NULL) 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) {  
                         ptr->is_deleted = is_delete;  
                         error = 0;  
                         goto out;  
381                  }                  }
382                    flag = true;
383          }          }
384          if (is_delete) {          return flag;
                 error = -ENOENT;  
                 goto out;  
         }  
         if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) 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);  
         return error;  
 }  
   
 int ReadAliasPolicy(struct io_buffer *head)  
 {  
         struct list1_head *pos;  
         list1_for_each_cookie(pos, head->read_var2, &alias_list) {  
                 struct alias_entry *ptr;  
                 ptr = list1_entry(pos, struct alias_entry, list);  
                 if (ptr->is_deleted) continue;  
                 if (io_printf(head, KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, ptr->aliased_name->name)) return -ENOMEM;  
         }  
         return 0;  
385  }  }
386    
387  int AddAliasPolicy(char *data, const bool is_delete)  static bool ccs_is_same_aggregator_entry(const struct ccs_acl_head *a,
388                                             const struct ccs_acl_head *b)
389  {  {
390          char *cp = strchr(data, ' ');          const struct ccs_aggregator_entry *p1 =
391          if (!cp) return -EINVAL;                  container_of(a, typeof(*p1), head);
392          *cp++ = '\0';          const struct ccs_aggregator_entry *p2 =
393          return AddAliasEntry(data, cp, is_delete);                  container_of(b, typeof(*p2), head);
394            return p1->original_name == p2->original_name &&
395                    p1->aggregated_name == p2->aggregated_name;
396  }  }
397    
398  /*************************  DOMAIN AGGREGATOR HANDLER  *************************/  /**
399     * ccs_update_aggregator_entry - Update "struct ccs_aggregator_entry" list.
400  static LIST1_HEAD(aggregator_list);   *
401     * @original_name:   The original program's name.
402  static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete)   * @aggregated_name: The aggregated program's name.
403  {   * @is_delete:       True if it is a delete request.
404          struct aggregator_entry *new_entry, *ptr;   *
405          static DEFINE_MUTEX(lock);   * Returns 0 on success, negative value otherwise.
406          const struct path_info *saved_original_name, *saved_aggregated_name;   */
407          int error = -ENOMEM;  static int ccs_update_aggregator_entry(const char *original_name,
408          if (!IsCorrectPath(original_name, 1, 0, -1, __FUNCTION__) || !IsCorrectPath(aggregated_name, 1, -1, -1, __FUNCTION__)) return -EINVAL;                                         const char *aggregated_name,
409          if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aggregated_name = SaveName(aggregated_name)) == NULL) return -ENOMEM;                                         const bool is_delete)
410          mutex_lock(&lock);  {
411          list1_for_each_entry(ptr, &aggregator_list, list) {          struct ccs_aggregator_entry e = { };
412                  if (ptr->original_name == saved_original_name && ptr->aggregated_name == saved_aggregated_name) {          int error = is_delete ? -ENOENT : -ENOMEM;
413                          ptr->is_deleted = is_delete;          if (!ccs_is_correct_path(original_name, 1, 0, -1) ||
414                          error = 0;              !ccs_is_correct_path(aggregated_name, 1, -1, -1))
415                          goto out;                  return -EINVAL;
416                  }          e.original_name = ccs_get_name(original_name);
417          }          e.aggregated_name = ccs_get_name(aggregated_name);
418          if (is_delete) {          if (!e.original_name || !e.aggregated_name)
                 error = -ENOENT;  
419                  goto out;                  goto out;
420          }          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
421          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;                                    CCS_ID_AGGREGATOR,
422          new_entry->original_name = saved_original_name;                                    ccs_is_same_aggregator_entry);
         new_entry->aggregated_name = saved_aggregated_name;  
         list1_add_tail_mb(&new_entry->list, &aggregator_list);  
         error = 0;  
423   out:   out:
424          mutex_unlock(&lock);          ccs_put_name(e.original_name);
425            ccs_put_name(e.aggregated_name);
426          return error;          return error;
427  }  }
428    
429  int ReadAggregatorPolicy(struct io_buffer *head)  /**
430     * ccs_write_aggregator_policy - Write "struct ccs_aggregator_entry" list.
431     *
432     * @data:      String to parse.
433     * @is_delete: True if it is a delete request.
434     *
435     * Returns 0 on success, negative value otherwise.
436     */
437    int ccs_write_aggregator_policy(char *data, const bool is_delete, const u8 flags)
438  {  {
439          struct list1_head *pos;          char *w[2];
440          list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {          if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
441                  struct aggregator_entry *ptr;                  return -EINVAL;
442                  ptr = list1_entry(pos, struct aggregator_entry, list);          return ccs_update_aggregator_entry(w[0], w[1], is_delete);
                 if (ptr->is_deleted) continue;  
                 if (io_printf(head, KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, ptr->aggregated_name->name)) return -ENOMEM;  
         }  
         return 0;  
443  }  }
444    
445  int AddAggregatorPolicy(char *data, const bool is_delete)  /* Domain create/delete handler. */
446    
447    /**
448     * ccs_delete_domain - Delete a domain.
449     *
450     * @domainname: The name of domain.
451     *
452     * Returns 0.
453     */
454    int ccs_delete_domain(char *domainname)
455  {  {
456          char *cp = strchr(data, ' ');          struct ccs_domain_info *domain;
457          if (!cp) return -EINVAL;          struct ccs_path_info name;
458          *cp++ = '\0';          name.name = domainname;
459          return AddAggregatorEntry(data, cp, is_delete);          ccs_fill_path_info(&name);
460  }          if (mutex_lock_interruptible(&ccs_policy_lock))
461                    return 0;
 /*************************  DOMAIN DELETION HANDLER  *************************/  
   
 /* #define DEBUG_DOMAIN_UNDELETE */  
   
 int DeleteDomain(char *domainname0)  
 {  
         struct domain_info *domain;  
         struct path_info domainname;  
         domainname.name = domainname0;  
         fill_path_info(&domainname);  
         mutex_lock(&new_domain_assign_lock);  
 #ifdef DEBUG_DOMAIN_UNDELETE  
         printk("DeleteDomain %s\n", domainname0);  
         list1_for_each_entry(domain, &domain_list, list) {  
                 if (pathcmp(domain->domainname, &domainname)) continue;  
                 printk("List: %p %u\n", domain, domain->is_deleted);  
         }  
 #endif  
462          /* Is there an active domain? */          /* Is there an active domain? */
463          list1_for_each_entry(domain, &domain_list, list) {          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
464                  struct domain_info *domain2;                  /* Never delete ccs_kernel_domain */
465                  /* Never delete KERNEL_DOMAIN */                  if (domain == &ccs_kernel_domain)
466                  if (domain == &KERNEL_DOMAIN || domain->is_deleted || pathcmp(domain->domainname, &domainname)) continue;                          continue;
467                  /* Mark already deleted domains as non undeletable. */                  if (domain->is_deleted ||
468                  list1_for_each_entry(domain2, &domain_list, list) {                      ccs_pathcmp(domain->domainname, &name))
469                          if (!domain2->is_deleted || pathcmp(domain2->domainname, &domainname)) continue;                          continue;
470  #ifdef DEBUG_DOMAIN_UNDELETE                  domain->is_deleted = true;
                         if (domain2->is_deleted != 255) printk("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("Marked %p as undeletable\n", domain);  
 #endif  
471                  break;                  break;
472          }          }
473          mutex_unlock(&new_domain_assign_lock);          mutex_unlock(&ccs_policy_lock);
474          return 0;          return 0;
475  }  }
476    
477  struct domain_info *UndeleteDomain(const char *domainname0)  /**
478  {   * ccs_find_or_assign_new_domain - Create a domain.
479          struct domain_info *domain, *candidate_domain = NULL;   *
480          struct path_info domainname;   * @domainname: The name of domain.
481          domainname.name = domainname0;   * @profile:    Profile number to assign if the domain was newly created.
482          fill_path_info(&domainname);   *
483          mutex_lock(&new_domain_assign_lock);   * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
484  #ifdef DEBUG_DOMAIN_UNDELETE   */
485          printk("UndeleteDomain %s\n", domainname0);  struct ccs_domain_info *ccs_find_or_assign_new_domain(const char *domainname,
486          list1_for_each_entry(domain, &domain_list, list) {                                                        const u8 profile)
                 if (pathcmp(domain->domainname, &domainname)) continue;  
                 printk("List: %p %u\n", domain, domain->is_deleted);  
         }  
 #endif  
         list1_for_each_entry(domain, &domain_list, list) {  
                 if (pathcmp(&domainname, domain->domainname)) continue;  
                 if (!domain->is_deleted) {  
                         /* This domain is active. I can't undelete. */  
                         candidate_domain = NULL;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                         printk("%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("%p was undeleted.\n", candidate_domain);  
 #endif  
         }  
         mutex_unlock(&new_domain_assign_lock);  
         return candidate_domain;  
 }  
   
 /*************************  DOMAIN TRANSITION HANDLER  *************************/  
   
 struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile)  
487  {  {
488          struct domain_info *domain = NULL;          struct ccs_domain_info *entry;
489          const struct path_info *saved_domainname;          struct ccs_domain_info *domain = NULL;
490          mutex_lock(&new_domain_assign_lock);          const struct ccs_path_info *saved_domainname;
491          if ((domain = FindDomain(domainname)) != NULL) goto out;          bool found = false;
492          if (!IsCorrectDomain(domainname, __FUNCTION__)) goto out;  
493          if ((saved_domainname = SaveName(domainname)) == NULL) goto out;          if (!ccs_is_correct_domain(domainname))
494          /* Can I reuse memory of deleted domain? */                  return NULL;
495          list1_for_each_entry(domain, &domain_list, list) {          saved_domainname = ccs_get_name(domainname);
496                  struct task_struct *p;          if (!saved_domainname)
497                  struct acl_info *ptr;                  return NULL;
498                  int flag;          entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS);
499                  if (!domain->is_deleted || domain->domainname != saved_domainname) continue;          if (mutex_lock_interruptible(&ccs_policy_lock))
                 flag = 0;  
                 /***** CRITICAL SECTION START *****/  
                 read_lock(&tasklist_lock);  
                 for_each_process(p) {  
                         if (p->domain_info == domain) { flag = 1; break; }  
                 }  
                 read_unlock(&tasklist_lock);  
                 /***** CRITICAL SECTION END *****/  
                 if (flag) continue;  
 #ifdef DEBUG_DOMAIN_UNDELETE  
                 printk("Reusing %p %s\n", domain, domain->domainname->name);  
 #endif  
                 list1_for_each_entry(ptr, &domain->acl_info_list, list) ptr->is_deleted = 1;  
                 domain->profile = profile;  
                 domain->quota_warned = 0;  
                 mb(); /* Avoid out-of-order execution. */  
                 domain->is_deleted = 0;  
500                  goto out;                  goto out;
501            list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
502                    if (domain->is_deleted ||
503                        ccs_pathcmp(saved_domainname, domain->domainname))
504                            continue;
505                    found = true;
506                    break;
507          }          }
508          /* No memory reusable. Create using new memory. */          if (!found && ccs_memory_ok(entry, sizeof(*entry))) {
509          if ((domain = alloc_element(sizeof(*domain))) != NULL) {                  INIT_LIST_HEAD(&entry->acl_info_list);
510                  INIT_LIST1_HEAD(&domain->acl_info_list);                  entry->domainname = saved_domainname;
511                  domain->domainname = saved_domainname;                  saved_domainname = NULL;
512                  domain->profile = profile;                  entry->profile = profile;
513                  list1_add_tail_mb(&domain->list, &domain_list);                  list_add_tail_rcu(&entry->list, &ccs_domain_list);
514          }                  domain = entry;
515   out: ;                  entry = NULL;
516          mutex_unlock(&new_domain_assign_lock);                  found = true;
         return domain;  
 }  
   
 static int Escape(char *dest, const char *src, int dest_len)  
 {  
         while (*src) {  
                 const unsigned char c = * (const unsigned char *) src;  
                 if (c == '\\') {  
                         dest_len -= 2;  
                         if (dest_len <= 0) goto out;  
                         *dest++ = '\\';  
                         *dest++ = '\\';  
                 } else if (c > ' ' && c < 127) {  
                         if (--dest_len <= 0) goto out;  
                         *dest++ = c;  
                 } else {  
                         dest_len -= 4;  
                         if (dest_len <= 0) goto out;  
                         *dest++ = '\\';  
                         *dest++ = (c >> 6) + '0';  
                         *dest++ = ((c >> 3) & 7) + '0';  
                         *dest++ = (c & 7) + '0';  
                 }  
                 src++;  
517          }          }
518          if (--dest_len <= 0) goto out;          mutex_unlock(&ccs_policy_lock);
         *dest = '\0';  
         return 0;  
519   out:   out:
520          return -ENOMEM;          ccs_put_name(saved_domainname);
521  }          kfree(entry);
522            return found ? domain : NULL;
 static char *get_argv0(struct linux_binprm *bprm)  
 {  
         char *arg_ptr = ccs_alloc(PAGE_SIZE); /* Initial buffer. */  
         int arg_len = 0;  
         unsigned long pos = bprm->p;  
         int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;  
         if (!bprm->argc || !arg_ptr) goto out;  
         while (1) {  
                 struct page *page;  
 #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;  
 #else  
                 page = bprm->page[i];  
 #endif  
                 { /* Map and copy to kernel buffer and unmap. */  
                         const char *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;  
                         }  
                         memmove(arg_ptr + arg_len, kaddr + offset, PAGE_SIZE - offset);  
                         kunmap(page);  
                 }  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  
                 put_page(page);  
                 pos += PAGE_SIZE - offset;  
 #endif  
                 arg_len += PAGE_SIZE - offset;  
                 if (memchr(arg_ptr, '\0', arg_len)) break;  
                 { /* Initial buffer was too small for argv[0]. Retry after expanding buffer. */  
                         char *tmp_arg_ptr = ccs_alloc(arg_len + PAGE_SIZE);  
                         if (!tmp_arg_ptr) goto out;  
                         memmove(tmp_arg_ptr, arg_ptr, arg_len);  
                         ccs_free(arg_ptr);  
                         arg_ptr = tmp_arg_ptr;  
                 }  
                 i++;  
                 offset = 0;  
         }  
         return arg_ptr;  
  out: /* Release initial buffer. */  
         ccs_free(arg_ptr);  
         return NULL;  
523  }  }
524    
525  static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check)  /**
526     * ccs_find_next_domain - Find a domain.
527     *
528     * @ee: Pointer to "struct ccs_execve_entry".
529     *
530     * Returns 0 on success, negative value otherwise.
531     *
532     * Caller holds ccs_read_lock().
533     */
534    static int ccs_find_next_domain(struct ccs_execve_entry *ee)
535  {  {
536          /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */          struct ccs_request_info *r = &ee->r;
537          struct domain_info *old_domain = current->domain_info, *domain = NULL;          const struct ccs_path_info *handler = ee->handler;
538            struct ccs_domain_info *domain = NULL;
539            struct ccs_domain_info * const old_domain = ccs_current_domain();
540          const char *old_domain_name = old_domain->domainname->name;          const char *old_domain_name = old_domain->domainname->name;
541          const char *original_name = bprm->filename;          struct linux_binprm *bprm = ee->bprm;
542          struct file *filp = bprm->file;          struct task_struct *task = current;
543          char *new_domain_name = NULL;          const u32 ccs_flags = task->ccs_flags;
544          char *real_program_name = NULL, *symlink_program_name = NULL;          struct ccs_path_info rn = { }; /* real name */
545          const bool is_enforce = (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_FILE) == 3);          struct ccs_path_info ln; /* last name */
546          int retval;          int retval;
547          struct path_info r, s, l;          bool need_kfree = false;
548            bool domain_created = false;
549          {          ln.name = ccs_last_word(old_domain_name);
550                  /*          ccs_fill_path_info(&ln);
551                   * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .   retry:
552                   */          current->ccs_flags = ccs_flags;
553                  static int first = 1;          r->cond = NULL;
554                  if (first) {          if (need_kfree) {
555                          AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);                  kfree(rn.name);
556                          AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);                  need_kfree = false;
                         first = 0;  
                 }  
557          }          }
558    
559          /* Get realpath of program. */          /* Get symlink's pathname of program. */
560          retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */          retval = ccs_symlink_path(bprm->filename, &rn);
561          if ((real_program_name = realpath(original_name)) == NULL) goto out;          if (retval < 0)
562          /* Get realpath of symbolic link. */                  goto out;
563          if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;          need_kfree = true;
564    
565          r.name = real_program_name;          if (handler) {
566          fill_path_info(&r);                  if (ccs_pathcmp(&rn, handler)) {
567          s.name = symlink_program_name;                          /* Failed to verify execute handler. */
568          fill_path_info(&s);                          static u8 counter = 20;
569          if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;                          if (counter) {
570          else l.name = old_domain_name;                                  counter--;
571          fill_path_info(&l);                                  printk(KERN_WARNING "Failed to verify: %s\n",
572                                           handler->name);
         if (!do_perm_check) goto ok;  
   
         /* Check 'alias' directive. */  
         if (pathcmp(&r, &s)) {  
                 struct alias_entry *ptr;  
                 /* Is this program allowed to be called via symbolic links? */  
                 list1_for_each_entry(ptr, &alias_list, list) {  
                         if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;  
                         memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);  
                         strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);  
                         fill_path_info(&r);  
                         break;  
                 }  
         }  
           
         /* Compare basename of real_program_name and argv[0] */  
         if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {  
                 char *org_argv0 = get_argv0(bprm);  
                 retval = -ENOMEM;  
                 if (org_argv0) {  
                         const int len = strlen(org_argv0);  
                         char *printable_argv0 = ccs_alloc(len * 4 + 8);  
                         if (printable_argv0 && Escape(printable_argv0, org_argv0, len * 4 + 8) == 0) {  
                                 const char *base_argv0, *base_filename;  
                                 if ((base_argv0 = strrchr(printable_argv0, '/')) == NULL) base_argv0 = printable_argv0; else base_argv0++;  
                                 if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;  
                                 if (strcmp(base_argv0, base_filename)) retval = CheckArgv0Perm(&r, base_argv0);  
                                 else retval = 0;  
573                          }                          }
574                          ccs_free(printable_argv0);                          goto out;
                         ccs_free(org_argv0);  
575                  }                  }
576                  if (retval) goto out;          } else {
577          }                  struct ccs_aggregator_entry *ptr;
578                            /* Check 'aggregator' directive. */
579          /* Check 'aggregator' directive. */                  list_for_each_entry_rcu(ptr,
580          {                                          &ccs_policy_list[CCS_ID_AGGREGATOR],
581                  struct aggregator_entry *ptr;                                          head.list) {
582                  /* Is this program allowed to be aggregated? */                          if (ptr->head.is_deleted ||
583                  list1_for_each_entry(ptr, &aggregator_list, list) {                              !ccs_path_matches_pattern(&rn, ptr->original_name))
584                          if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;                                  continue;
585                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);                          kfree(rn.name);
586                          strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);                          need_kfree = false;
587                          fill_path_info(&r);                          /* This is OK because it is read only. */
588                            rn = *ptr->aggregated_name;
589                          break;                          break;
590                  }                  }
         }  
591    
592          /* Check execute permission. */                  /* Check execute permission. */
593          if ((retval = CheckExecPerm(&r, filp)) < 0) goto out;                  retval = ccs_exec_perm(r, &rn);
594                    if (retval == CCS_RETRY_REQUEST)
595                            goto retry;
596                    if (retval < 0)
597                            goto out;
598            }
599    
600   ok: ;          /* Calculate domain to transit to. */
601          /* Allocate memory for calcurating domain name. */          if (ccs_is_domain_initializer(old_domain->domainname, &rn, &ln)) {
602          retval = -ENOMEM;                  /* Transit to the child of ccs_kernel_domain domain. */
603          if ((new_domain_name = ccs_alloc(CCS_MAX_PATHNAME_LEN + 16)) == NULL) goto out;                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, ROOT_NAME " " "%s",
604                                     rn.name);
605          if (IsDomainInitializer(old_domain->domainname, &r, &l)) {          } else if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) {
                 /* 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 (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {  
606                  /*                  /*
607                   * Needn't to transit from kernel domain before starting /sbin/init .                   * Needn't to transit from kernel domain before starting
608                   * But transit from kernel domain if executing initializers, for they might start before /sbin/init .                   * /sbin/init. But transit from kernel domain if executing
609                     * initializers because they might start before /sbin/init.
610                   */                   */
611                  domain = old_domain;                  domain = old_domain;
612          } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {          } else if (ccs_is_domain_keeper(old_domain->domainname, &rn, &ln)) {
613                  /* Keep current domain. */                  /* Keep current domain. */
614                  domain = old_domain;                  domain = old_domain;
615          } else {          } else {
616                  /* Normal domain transition. */                  /* Normal domain transition. */
617                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
618          }                           old_domain_name, rn.name);
         if (!domain && strlen(new_domain_name) < CCS_MAX_PATHNAME_LEN) {  
                 if (is_enforce) {  
                         domain = FindDomain(new_domain_name);  
                         if (!domain) if (CheckSupervisor("#Need to create domain\n%s\n", new_domain_name) == 0) domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);  
                 } else {  
                         domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);  
                 }  
619          }          }
620            if (domain || strlen(ee->tmp) >= CCS_EXEC_TMPSIZE - 10)
621                    goto done;
622            domain = ccs_find_domain(ee->tmp);
623            if (domain)
624                    goto done;
625            if (r->mode == CCS_CONFIG_ENFORCING) {
626                    int error = ccs_supervisor(r, "# wants to create domain\n"
627                                               "%s\n", ee->tmp);
628                    if (error == CCS_RETRY_REQUEST)
629                            goto retry;
630                    if (error < 0)
631                            goto done;
632            }
633            domain = ccs_find_or_assign_new_domain(ee->tmp, r->profile);
634            if (domain)
635                    domain_created = true;
636     done:
637          if (!domain) {          if (!domain) {
638                  printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);                  retval = (r->mode == CCS_CONFIG_ENFORCING) ? -EPERM : 0;
639                  if (is_enforce) retval = -EPERM;                  if (!old_domain->domain_transition_failed) {
640                            old_domain->domain_transition_failed = true;
641                            ccs_write_audit_log(false, r,
642                                                CCS_KEYWORD_TRANSITION_FAILED
643                                                "\n");
644                            printk(KERN_WARNING "ERROR: Domain '%s' not defined.\n",
645                                   ee->tmp);
646                    }
647          } else {          } else {
648                  retval = 0;                  retval = 0;
649          }          }
650   out: ;          if (!retval && handler)
651          ccs_free(new_domain_name);                  ccs_audit_execute_handler_log(ee);
652          ccs_free(real_program_name);          /*
653          ccs_free(symlink_program_name);           * Tell GC that I started execve().
654          *next_domain = domain ? domain : old_domain;           * Also, tell open_exec() to check read permission.
655             */
656            task->ccs_flags |= CCS_TASK_IS_IN_EXECVE;
657            /*
658             * Make task->ccs_flags visible to GC before changing
659             * task->ccs_domain_info .
660             */
661            smp_mb();
662            /*
663             * Proceed to the next domain in order to allow reaching via PID.
664             * It will be reverted if execve() failed. Reverting is not good.
665             * But it is better than being unable to reach via PID in interactive
666             * enforcing mode.
667             */
668            if (domain)
669                    task->ccs_domain_info = domain;
670            if (domain_created)
671                    ccs_audit_domain_creation_log();
672     out:
673            if (need_kfree)
674                    kfree(rn.name);
675          return retval;          return retval;
676  }  }
677    
678  static int CheckEnviron(struct linux_binprm *bprm)  /**
679     * ccs_environ - Check permission for environment variable names.
680     *
681     * @ee: Pointer to "struct ccs_execve_entry".
682     *
683     * Returns 0 on success, negative value otherwise.
684     */
685    static int ccs_environ(struct ccs_execve_entry *ee)
686  {  {
687          const u8 profile = current->domain_info->profile;          struct ccs_request_info *r = &ee->r;
688          const u8 mode = CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV);          struct linux_binprm *bprm = ee->bprm;
689          char *arg_ptr;          /* env_page->data is allocated by ccs_dump_page(). */
690            struct ccs_page_dump env_page = { };
691            char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */
692          int arg_len = 0;          int arg_len = 0;
693          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
694          int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;          int offset = pos % PAGE_SIZE;
695          int argv_count = bprm->argc;          int argv_count = bprm->argc;
696          int envp_count = bprm->envc;          int envp_count = bprm->envc;
697          //printk("start %d %d\n", argv_count, envp_count);          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
698          int error = -ENOMEM;          int error = -ENOMEM;
699          if (!mode || !envp_count) return 0;          ee->r.type = CCS_MAC_ENVIRON;
700          arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN);          ee->r.mode = ccs_get_mode(ccs_current_domain()->profile,
701          if (!arg_ptr) goto out;                                    CCS_MAC_ENVIRON);
702            if (!r->mode || !envp_count)
703                    return 0;
704            arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
705            if (!arg_ptr)
706                    goto out;
707          while (error == -ENOMEM) {          while (error == -ENOMEM) {
708                  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) goto out;  
 #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  
709                          goto out;                          goto out;
710                  }                  pos += PAGE_SIZE - offset;
711                  /* Read. */                  /* Read. */
712                  while (argv_count && offset < PAGE_SIZE) {                  while (argv_count && offset < PAGE_SIZE) {
713                          if (!kaddr[offset++]) argv_count--;                          if (!env_page.data[offset++])
714                                    argv_count--;
715                    }
716                    if (argv_count) {
717                            offset = 0;
718                            continue;
719                  }                  }
                 if (argv_count) goto unmap_page;  
720                  while (offset < PAGE_SIZE) {                  while (offset < PAGE_SIZE) {
721                          const unsigned char c = kaddr[offset++];                          const unsigned char c = env_page.data[offset++];
722                          if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {                          if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
723                                  if (c == '=') {                                  if (c == '=') {
724                                          arg_ptr[arg_len++] = '\0';                                          arg_ptr[arg_len++] = '\0';
725                                  } else if (c == '\\') {                                  } else if (c == '\\') {
# Line 783  static int CheckEnviron(struct linux_bin Line 730  static int CheckEnviron(struct linux_bin
730                                  } else {                                  } else {
731                                          arg_ptr[arg_len++] = '\\';                                          arg_ptr[arg_len++] = '\\';
732                                          arg_ptr[arg_len++] = (c >> 6) + '0';                                          arg_ptr[arg_len++] = (c >> 6) + '0';
733                                          arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';                                          arg_ptr[arg_len++]
734                                                    = ((c >> 3) & 7) + '0';
735                                          arg_ptr[arg_len++] = (c & 7) + '0';                                          arg_ptr[arg_len++] = (c & 7) + '0';
736                                  }                                  }
737                          } else {                          } else {
738                                  arg_ptr[arg_len] = '\0';                                  arg_ptr[arg_len] = '\0';
739                          }                          }
740                          if (c) continue;                          if (c)
741                          if (CheckEnvPerm(arg_ptr, profile, mode)) {                                  continue;
742                            if (ccs_env_perm(r, arg_ptr)) {
743                                  error = -EPERM;                                  error = -EPERM;
744                                  break;                                  break;
745                          }                          }
# Line 800  static int CheckEnviron(struct linux_bin Line 749  static int CheckEnviron(struct linux_bin
749                          }                          }
750                          arg_len = 0;                          arg_len = 0;
751                  }                  }
         unmap_page:  
                 /* Unmap. */  
                 kunmap(page);  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  
                 put_page(page);  
                 pos += PAGE_SIZE - offset;  
 #endif  
                 i++;  
752                  offset = 0;                  offset = 0;
753          }          }
754   out:   out:
755          ccs_free(arg_ptr);          if (r->mode != 3)
756          if (error && mode != 3) error = 0;                  error = 0;
757            kfree(env_page.data);
758            kfree(arg_ptr);
759          return error;          return error;
760  }  }
761    
762  static void UnEscape(unsigned char *dest)  /**
763     * ccs_unescape - Unescape escaped string.
764     *
765     * @dest: String to unescape.
766     *
767     * Returns nothing.
768     */
769    static void ccs_unescape(unsigned char *dest)
770  {  {
771          unsigned char *src = dest;          unsigned char *src = dest;
772          unsigned char c, d, e;          unsigned char c;
773          while ((c = *src++) != '\0') {          unsigned char d;
774            unsigned char e;
775            while (1) {
776                    c = *src++;
777                    if (!c)
778                            break;
779                  if (c != '\\') {                  if (c != '\\') {
780                          *dest++ = c;                          *dest++ = c;
781                          continue;                          continue;
# Line 828  static void UnEscape(unsigned char *dest Line 783  static void UnEscape(unsigned char *dest
783                  c = *src++;                  c = *src++;
784                  if (c == '\\') {                  if (c == '\\') {
785                          *dest++ = c;                          *dest++ = c;
786                  } else if (c >= '0' && c <= '3' &&                          continue;
                            (d = *src++) >= '0' && d <= '7' &&  
                            (e = *src++) >= '0' && e <= '7') {  
                         *dest++ = ((c - '0') << 6) | ((d - '0') << 3) | (e - '0');  
                 } else {  
                         break;  
787                  }                  }
788                    if (c < '0' || c > '3')
789                            break;
790                    d = *src++;
791                    if (d < '0' || d > '7')
792                            break;
793                    e = *src++;
794                    if (e < '0' || e > '7')
795                            break;
796                    *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
797          }          }
798          *dest = '\0';          *dest = '\0';
799  }  }
800    
801  static int try_alt_exec(struct linux_binprm *bprm, char **alt_exec0)  /**
802     * ccs_root_depth - Get number of directories to strip.
803     *
804     * @dentry: Pointer to "struct dentry".
805     * @vfsmnt: Pointer to "struct vfsmount".
806     *
807     * Returns number of directories to strip.
808     */
809    static int ccs_root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)
810    {
811            int depth = 0;
812            ccs_realpath_lock();
813            for (;;) {
814                    if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
815                            /* Global root? */
816                            if (vfsmnt->mnt_parent == vfsmnt)
817                                    break;
818                            dentry = vfsmnt->mnt_mountpoint;
819                            vfsmnt = vfsmnt->mnt_parent;
820                            continue;
821                    }
822                    dentry = dentry->d_parent;
823                    depth++;
824            }
825            ccs_realpath_unlock();
826            return depth;
827    }
828    
829    /**
830     * ccs_get_root_depth - return the depth of root directory.
831     *
832     * Returns number of directories to strip.
833     */
834    static int ccs_get_root_depth(void)
835    {
836            int depth;
837            struct dentry *dentry;
838            struct vfsmount *vfsmnt;
839    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
840            struct path root;
841    #endif
842            read_lock(&current->fs->lock);
843    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
844            root = current->fs->root;
845            path_get(&current->fs->root);
846            dentry = root.dentry;
847            vfsmnt = root.mnt;
848    #else
849            dentry = dget(current->fs->root);
850            vfsmnt = mntget(current->fs->rootmnt);
851    #endif
852            read_unlock(&current->fs->lock);
853            depth = ccs_root_depth(dentry, vfsmnt);
854    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
855            path_put(&root);
856    #else
857            dput(dentry);
858            mntput(vfsmnt);
859    #endif
860            return depth;
861    }
862    
863    /**
864     * ccs_try_alt_exec - Try to start execute handler.
865     *
866     * @ee: Pointer to "struct ccs_execve_entry".
867     *
868     * Returns 0 on success, negative value otherwise.
869     */
870    static int ccs_try_alt_exec(struct ccs_execve_entry *ee)
871  {  {
872            /*
873             * Contents of modified bprm.
874             * The envp[] in original bprm is moved to argv[] so that
875             * the alternatively executed program won't be affected by
876             * some dangerous environment variables like LD_PRELOAD.
877             *
878             * modified bprm->argc
879             *    = original bprm->argc + original bprm->envc + 7
880             * modified bprm->envc
881             *    = 0
882             *
883             * modified bprm->argv[0]
884             *    = the program's name specified by execute_handler
885             * modified bprm->argv[1]
886             *    = ccs_current_domain()->domainname->name
887             * modified bprm->argv[2]
888             *    = the current process's name
889             * modified bprm->argv[3]
890             *    = the current process's information (e.g. uid/gid).
891             * modified bprm->argv[4]
892             *    = original bprm->filename
893             * modified bprm->argv[5]
894             *    = original bprm->argc in string expression
895             * modified bprm->argv[6]
896             *    = original bprm->envc in string expression
897             * modified bprm->argv[7]
898             *    = original bprm->argv[0]
899             *  ...
900             * modified bprm->argv[bprm->argc + 6]
901             *     = original bprm->argv[bprm->argc - 1]
902             * modified bprm->argv[bprm->argc + 7]
903             *     = original bprm->envp[0]
904             *  ...
905             * modified bprm->argv[bprm->envc + bprm->argc + 6]
906             *     = original bprm->envp[bprm->envc - 1]
907             */
908            struct linux_binprm *bprm = ee->bprm;
909          struct file *filp;          struct file *filp;
910          int retval;          int retval;
911          /* domainname must not be modified. */          const int original_argc = bprm->argc;
912          char *domainname = (char *) current->domain_info->domainname->name;          const int original_envc = bprm->envc;
913          char *alt_exec;          struct task_struct *task = current;
914          const char *alt_exec1 = GetAltExec();  
915          if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL;          /* Close the requested program's dentry. */
916          retval = strlen(alt_exec1) + 1;          ee->obj.path1.dentry = NULL;
917          *alt_exec0 = alt_exec = ccs_alloc(retval);          ee->obj.path1.mnt = NULL;
918          if (!alt_exec) return -ENOMEM;          ee->obj.validate_done = false;
         memmove(alt_exec, alt_exec1, retval);  
         UnEscape(alt_exec);  
919          allow_write_access(bprm->file);          allow_write_access(bprm->file);
920          fput(bprm->file);          fput(bprm->file);
921          bprm->file = NULL;          bprm->file = NULL;
922  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)  
923          retval = remove_arg_zero(bprm);          /* Invalidate page dump cache. */
924          if (retval) return retval;          ee->dump.page = NULL;
925  #else  
926          remove_arg_zero(bprm);          /* Move envp[] to argv[] */
927            bprm->argc += bprm->envc;
928            bprm->envc = 0;
929    
930            /* Set argv[6] */
931            {
932                    char *cp = ee->tmp;
933                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc);
934                    retval = copy_strings_kernel(1, &cp, bprm);
935                    if (retval < 0)
936                            goto out;
937                    bprm->argc++;
938            }
939    
940            /* Set argv[5] */
941            {
942                    char *cp = ee->tmp;
943                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc);
944                    retval = copy_strings_kernel(1, &cp, bprm);
945                    if (retval < 0)
946                            goto out;
947                    bprm->argc++;
948            }
949    
950            /* Set argv[4] */
951            {
952                    retval = copy_strings_kernel(1, &bprm->filename, bprm);
953                    if (retval < 0)
954                            goto out;
955                    bprm->argc++;
956            }
957    
958            /* Set argv[3] */
959            {
960                    char *cp = ee->tmp;
961                    const u32 ccs_flags = task->ccs_flags;
962                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1,
963                             "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
964                             "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
965                             "state[1]=%u state[2]=%u",
966                             (pid_t) ccsecurity_exports.sys_getpid(),
967                             current_uid(), current_gid(), current_euid(),
968                             current_egid(), current_suid(), current_sgid(),
969                             current_fsuid(), current_fsgid(),
970                             (u8) (ccs_flags >> 24), (u8) (ccs_flags >> 16),
971                             (u8) (ccs_flags >> 8));
972                    retval = copy_strings_kernel(1, &cp, bprm);
973                    if (retval < 0)
974                            goto out;
975                    bprm->argc++;
976            }
977    
978            /* Set argv[2] */
979            {
980                    char *exe = (char *) ccs_get_exe();
981                    if (exe) {
982                            retval = copy_strings_kernel(1, &exe, bprm);
983                            kfree(exe);
984                    } else {
985                            exe = ee->tmp;
986                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
987                            retval = copy_strings_kernel(1, &exe, bprm);
988                    }
989                    if (retval < 0)
990                            goto out;
991                    bprm->argc++;
992            }
993    
994            /* Set argv[1] */
995            {
996                    char *cp = ee->tmp;
997                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
998                             ccs_current_domain()->domainname->name);
999                    retval = copy_strings_kernel(1, &cp, bprm);
1000                    if (retval < 0)
1001                            goto out;
1002                    bprm->argc++;
1003            }
1004    
1005            /* Set argv[0] */
1006            {
1007                    int depth = ccs_get_root_depth();
1008                    int len = ee->handler->total_len + 1;
1009                    char *cp = kmalloc(len, CCS_GFP_FLAGS);
1010                    if (!cp) {
1011                            retval = -ENOMEM;
1012                            goto out;
1013                    }
1014                    ee->handler_path = cp;
1015                    memmove(cp, ee->handler->name, len);
1016                    ccs_unescape(cp);
1017                    retval = -ENOENT;
1018                    if (!*cp || *cp != '/')
1019                            goto out;
1020                    /* Adjust root directory for open_exec(). */
1021                    while (depth) {
1022                            cp = strchr(cp + 1, '/');
1023                            if (!cp)
1024                                    goto out;
1025                            depth--;
1026                    }
1027                    memmove(ee->handler_path, cp, strlen(cp) + 1);
1028                    cp = ee->handler_path;
1029                    retval = copy_strings_kernel(1, &cp, bprm);
1030                    if (retval < 0)
1031                            goto out;
1032                    bprm->argc++;
1033            }
1034    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1035    #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
1036            bprm->argv_len = bprm->exec - bprm->p;
1037  #endif  #endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)  
         retval = copy_strings_kernel(1, &bprm->interp, bprm);  
 #else  
         retval = copy_strings_kernel(1, &bprm->filename, bprm);  
1038  #endif  #endif
1039          if (retval < 0) return retval;  
1040          bprm->argc++;          /*
1041          retval = copy_strings_kernel(1, &domainname, bprm);           * OK, now restart the process with execute handler program's dentry.
1042          if (retval < 0) return retval;           */
1043          bprm->argc++;          filp = open_exec(ee->handler_path);
1044          retval = copy_strings_kernel(1, &alt_exec, bprm);          if (IS_ERR(filp)) {
1045          if (retval < 0) return retval;                  retval = PTR_ERR(filp);
1046          bprm->argc++;                  goto out;
1047  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)          }
1048          bprm->interp = alt_exec;          ee->obj.path1.dentry = filp->f_dentry;
1049            ee->obj.path1.mnt = filp->f_vfsmnt;
1050            bprm->file = filp;
1051            bprm->filename = ee->handler_path;
1052    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
1053            bprm->interp = bprm->filename;
1054  #endif  #endif
1055          filp = open_exec(alt_exec);          retval = prepare_binprm(bprm);
1056          if (IS_ERR(filp)) return PTR_ERR(filp);          if (retval < 0)
1057          bprm->file= filp;                  goto out;
1058          bprm->filename = alt_exec;          task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1059          return prepare_binprm(bprm);          retval = ccs_find_next_domain(ee);
1060            task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1061     out:
1062            return retval;
1063  }  }
1064    
1065  #endif  /**
1066     * ccs_find_execute_handler - Find an execute handler.
1067     *
1068     * @ee:   Pointer to "struct ccs_execve_entry".
1069     * @type: Type of execute handler.
1070     *
1071     * Returns true if found, false otherwise.
1072     *
1073     * Caller holds ccs_read_lock().
1074     */
1075    static bool ccs_find_execute_handler(struct ccs_execve_entry *ee,
1076                                         const u8 type)
1077    {
1078            struct task_struct *task = current;
1079            const struct ccs_domain_info * const domain = ccs_current_domain();
1080            struct ccs_acl_info *ptr;
1081            bool found = false;
1082            /*
1083             * Don't use execute handler if the current process is
1084             * marked as execute handler to avoid infinite execute handler loop.
1085             */
1086            if (task->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
1087                    return false;
1088            list_for_each_entry(ptr, &domain->acl_info_list, list) {
1089                    struct ccs_execute_handler_record *acl;
1090                    if (ptr->type != type)
1091                            continue;
1092                    acl = container_of(ptr, struct ccs_execute_handler_record,
1093                                       head);
1094                    ee->handler = acl->handler;
1095                    ee->handler_type = type;
1096                    found = true;
1097                    break;
1098            }
1099            return found;
1100    }
1101    
1102  int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)  /**
1103     * ccs_dump_page - Dump a page to buffer.
1104     *
1105     * @bprm: Pointer to "struct linux_binprm".
1106     * @pos:  Location to dump.
1107     * @dump: Poiner to "struct ccs_page_dump".
1108     *
1109     * Returns true on success, false otherwise.
1110     */
1111    bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos,
1112                       struct ccs_page_dump *dump)
1113  {  {
1114          struct domain_info *next_domain = NULL, *prev_domain = current->domain_info;          struct page *page;
1115          int retval;          /* dump->data is released by ccs_finish_execve(). */
1116          char *alt_exec = NULL; /* Keep valid until search_binary_handler() finishes. */          if (!dump->data) {
1117  #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)                  dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS);
1118          extern void CCS_LoadPolicy(const char *filename);                  if (!dump->data)
1119          CCS_LoadPolicy(bprm->filename);                          return false;
1120            }
1121            /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
1122    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1123            if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1124                    return false;
1125    #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3 && defined(CONFIG_MMU)
1126            if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1127                    return false;
1128    #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2 && defined(CONFIG_MMU)
1129            if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1130                    return false;
1131    #else
1132            page = bprm->page[pos / PAGE_SIZE];
1133    #endif
1134            if (page != dump->page) {
1135                    const unsigned int offset = pos % PAGE_SIZE;
1136                    /*
1137                     * Maybe kmap()/kunmap() should be used here.
1138                     * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
1139                     * So do I.
1140                     */
1141                    char *kaddr = kmap_atomic(page, KM_USER0);
1142                    dump->page = page;
1143                    memcpy(dump->data + offset, kaddr + offset,
1144                           PAGE_SIZE - offset);
1145                    kunmap_atomic(kaddr, KM_USER0);
1146            }
1147            /* Same with put_arg_page(page) in fs/exec.c */
1148    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1149            put_page(page);
1150    #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3 && defined(CONFIG_MMU)
1151            put_page(page);
1152    #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2 && defined(CONFIG_MMU)
1153            put_page(page);
1154  #endif  #endif
1155  #if defined(CONFIG_TOMOYO)          return true;
1156          retval = FindNextDomain(bprm, &next_domain, 1);  }
1157          if (retval == -EPERM && try_alt_exec(bprm, &alt_exec) >= 0) {  
1158                  current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;  /**
1159                  retval = FindNextDomain(bprm, &next_domain, 0);   * ccs_start_execve - Prepare for execve() operation.
1160                  current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;   *
1161          }   * @bprm: Pointer to "struct linux_binprm".
1162          if (retval == 0) {   * @eep:  Pointer to "struct ccs_execve_entry *".
1163                  current->domain_info = next_domain;   *
1164                  retval = CheckEnviron(bprm);   * Returns 0 on success, negative value otherwise.
1165                  current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;   */
1166                  if (!retval) retval = search_binary_handler(bprm, regs);  static int ccs_start_execve(struct linux_binprm *bprm,
1167                  current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;                              struct ccs_execve_entry **eep)
1168                  if (retval < 0) current->domain_info = prev_domain;  {
1169            int retval;
1170            struct task_struct *task = current;
1171            struct ccs_execve_entry *ee;
1172            *eep = NULL;
1173            ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS);
1174            if (!ee)
1175                    return -ENOMEM;
1176            ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
1177            if (!ee->tmp) {
1178                    kfree(ee);
1179                    return -ENOMEM;
1180            }
1181            ee->reader_idx = ccs_read_lock();
1182            /* ee->dump->data is allocated by ccs_dump_page(). */
1183            ee->previous_domain = task->ccs_domain_info;
1184            /* Clear manager flag. */
1185            task->ccs_flags &= ~CCS_TASK_IS_POLICY_MANAGER;
1186            *eep = ee;
1187            ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE);
1188            ee->r.ee = ee;
1189            ee->bprm = bprm;
1190            ee->r.obj = &ee->obj;
1191            ee->obj.path1.dentry = bprm->file->f_dentry;
1192            ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1193            /*
1194             * No need to call ccs_environ() for execute handler because envp[] is
1195             * moved to argv[].
1196             */
1197            if (ccs_find_execute_handler(ee, CCS_TYPE_EXECUTE_HANDLER))
1198                    return ccs_try_alt_exec(ee);
1199            retval = ccs_find_next_domain(ee);
1200            if (retval == -EPERM) {
1201                    if (ccs_find_execute_handler(ee,
1202                                                 CCS_TYPE_DENIED_EXECUTE_HANDLER))
1203                            return ccs_try_alt_exec(ee);
1204          }          }
1205          ccs_free(alt_exec);          if (!retval)
1206                    retval = ccs_environ(ee);
1207            return retval;
1208    }
1209    
1210    /**
1211     * ccs_finish_execve - Clean up execve() operation.
1212     *
1213     * @retval: Return code of an execve() operation.
1214     * @ee:     Pointer to "struct ccs_execve_entry".
1215     *
1216     * Caller holds ccs_read_lock().
1217     */
1218    static void ccs_finish_execve(int retval, struct ccs_execve_entry *ee)
1219    {
1220            struct task_struct *task = current;
1221            if (!ee)
1222                    return;
1223            if (retval < 0) {
1224                    task->ccs_domain_info = ee->previous_domain;
1225                    /*
1226                     * Make task->ccs_domain_info visible to GC before changing
1227                     * task->ccs_flags .
1228                     */
1229                    smp_mb();
1230            } else {
1231                    /* Mark the current process as execute handler. */
1232                    if (ee->handler)
1233                            task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1234                    /* Mark the current process as normal process. */
1235                    else
1236                            task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1237            }
1238            /* Tell GC that I finished execve(). */
1239            task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE;
1240            ccs_read_unlock(ee->reader_idx);
1241            kfree(ee->handler_path);
1242            kfree(ee->tmp);
1243            kfree(ee->dump.data);
1244            kfree(ee);
1245    }
1246    
1247    /**
1248     * ccs_may_transit - Check permission and do domain transition without execve().
1249     *
1250     * @domainname: Domainname to transit to.
1251     * @pathname: Pathname to check.
1252     *
1253     * Returns 0 on success, negative value otherwise.
1254     *
1255     * Caller holds ccs_read_lock().
1256     */
1257    int ccs_may_transit(const char *domainname, const char *pathname)
1258    {
1259            struct ccs_path_info name;
1260            struct ccs_request_info r;
1261            struct ccs_domain_info *domain;
1262            int error;
1263            bool domain_created = false;
1264            name.name = pathname;
1265            ccs_fill_path_info(&name);
1266            /* Check allow_transit permission. */
1267            ccs_init_request_info(&r, CCS_MAC_FILE_TRANSIT);
1268            error = ccs_path_permission(&r, CCS_TYPE_TRANSIT, &name);
1269            if (error)
1270                    return error;
1271            /* Check destination domain. */
1272            domain = ccs_find_domain(domainname);
1273            if (!domain && r.mode != CCS_CONFIG_ENFORCING &&
1274                strlen(domainname) < CCS_EXEC_TMPSIZE - 10) {
1275                    domain = ccs_find_or_assign_new_domain(domainname, r.profile);
1276                    if (domain)
1277                            domain_created = true;
1278            }
1279            if (domain) {
1280                    error = 0;
1281                    current->ccs_domain_info = domain;
1282                    if (domain_created)
1283                            ccs_audit_domain_creation_log();
1284            } else {
1285                    error = -ENOENT;
1286            }
1287            return error;
1288    }
1289    
1290    static int __ccs_search_binary_handler(struct linux_binprm *bprm,
1291                                           struct pt_regs *regs)
1292    {
1293            struct ccs_execve_entry *ee;
1294            int retval;
1295            if (!ccs_policy_loaded)
1296                    ccsecurity_exports.load_policy(bprm->filename);
1297            retval = ccs_start_execve(bprm, &ee);
1298            if (!retval)
1299                    retval = search_binary_handler(bprm, regs);
1300            ccs_finish_execve(retval, ee);
1301          return retval;          return retval;
 #else  
         return search_binary_handler(bprm, regs);  
 #endif  
1302  }  }
1303    
1304  /***** TOMOYO Linux end. *****/  void __init ccs_domain_init(void)
1305    {
1306            ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler;
1307    }

Legend:
Removed from v.851  
changed lines
  Added in v.3691

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