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

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

Legend:
Removed from v.329  
changed lines
  Added in v.3694

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