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

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

Legend:
Removed from v.120  
changed lines
  Added in v.2828

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