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

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

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

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