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

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

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

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