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

Subversion リポジトリの参照

Diff of /trunk/1.8.x/ccs-patch/security/ccsecurity/domain.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/1.5.x/ccs-patch/fs/tomoyo_domain.c revision 816 by kumaneko, Tue Dec 18 08:01:32 2007 UTC trunk/1.6.x/ccs-patch/fs/tomoyo_domain.c revision 1695 by kumaneko, Sat Oct 11 08:46:59 2008 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-2008  NTT DATA CORPORATION
7   *   *
8   * Version: 1.5.3-pre   2007/12/18   * Version: 1.6.5-pre   2008/10/11
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/ccs_common.h>  #include <linux/ccs_common.h>
16  #include <linux/tomoyo.h>  #include <linux/tomoyo.h>
17  #include <linux/realpath.h>  #include <linux/realpath.h>
18  #include <linux/highmem.h>  #include <linux/highmem.h>
19  #include <linux/binfmts.h>  #include <linux/binfmts.h>
20    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
21    #include <linux/namei.h>
22    #include <linux/mount.h>
23    #endif
24    
25    /* For compatibility with older kernels. */
26  #ifndef for_each_process  #ifndef for_each_process
27  #define for_each_process for_each_task  #define for_each_process for_each_task
28  #endif  #endif
29    
30  /*************************  VARIABLES  *************************/  /* Variables definitions.*/
31    
32  /* The initial domain. */  /* The initial domain. */
33  struct domain_info KERNEL_DOMAIN;  struct domain_info KERNEL_DOMAIN;
34    
35  /* List of domains. */  /* The list for "struct domain_info". */
36  LIST1_HEAD(domain_list);  LIST1_HEAD(domain_list);
37    
 /* /sbin/init started? */  
 extern int sbin_init_started;  
   
38  #ifdef CONFIG_TOMOYO  #ifdef CONFIG_TOMOYO
39    
40  /* Lock for appending domain's ACL. */  /* Domain creation lock. */
41  DEFINE_MUTEX(domain_acl_lock);  static DEFINE_MUTEX(new_domain_assign_lock);
   
 /*************************  UTILITY FUNCTIONS  *************************/  
   
 /***** The structure for program files to force domain reconstruction. *****/  
42    
43    /* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
44  struct domain_initializer_entry {  struct domain_initializer_entry {
45          struct list1_head list;          struct list1_head list;
46          const struct path_info *domainname;    /* This may be NULL */          const struct path_info *domainname;    /* This may be NULL */
47          const struct path_info *program;          const struct path_info *program;
48          bool is_deleted;          bool is_deleted;
49          bool is_not;          bool is_not;       /* True if this entry is "no_initialize_domain".  */
50          bool is_last_name;          bool is_last_name; /* True if the domainname is ccs_get_last_name(). */
51  };  };
52    
53  /***** The structure for domains to not to transit domains. *****/  /* Structure for "keep_domain" and "no_keep_domain" keyword. */
   
54  struct domain_keeper_entry {  struct domain_keeper_entry {
55          struct list1_head list;          struct list1_head list;
56          const struct path_info *domainname;          const struct path_info *domainname;
57          const struct path_info *program;       /* This may be NULL */          const struct path_info *program;       /* This may be NULL */
58          bool is_deleted;          bool is_deleted;
59          bool is_not;          bool is_not;       /* True if this entry is "no_keep_domain".        */
60          bool is_last_name;          bool is_last_name; /* True if the domainname is ccs_get_last_name(). */
61  };  };
62    
63  /***** The structure for program files that should be aggregated. *****/  /* Structure for "aggregator" keyword. */
   
64  struct aggregator_entry {  struct aggregator_entry {
65          struct list1_head list;          struct list1_head list;
66          const struct path_info *original_name;          const struct path_info *original_name;
# Line 72  struct aggregator_entry { Line 68  struct aggregator_entry {
68          bool is_deleted;          bool is_deleted;
69  };  };
70    
71  /***** The structure for program files that should be aliased. *****/  /* Structure for "alias" keyword. */
   
72  struct alias_entry {  struct alias_entry {
73          struct list1_head list;          struct list1_head list;
74          const struct path_info *original_name;          const struct path_info *original_name;
# Line 81  struct alias_entry { Line 76  struct alias_entry {
76          bool is_deleted;          bool is_deleted;
77  };  };
78    
79  /*************************  VARIABLES  *************************/  /**
80     * ccs_set_domain_flag - Set or clear domain's attribute flags.
81  /* Domain creation lock. */   *
82  static DEFINE_MUTEX(new_domain_assign_lock);   * @domain:    Pointer to "struct domain_info".
83     * @is_delete: True if it is a delete request.
84  /*************************  UTILITY FUNCTIONS  *************************/   * @flags:     Flags to set or clear.
85     *
86     * Returns nothing.
87     */
88    void ccs_set_domain_flag(struct domain_info *domain, const bool is_delete,
89                             const u8 flags)
90    {
91            mutex_lock(&new_domain_assign_lock);
92            if (!is_delete)
93                    domain->flags |= flags;
94            else
95                    domain->flags &= ~flags;
96            mutex_unlock(&new_domain_assign_lock);
97    }
98    
99  const char *GetLastName(const struct domain_info *domain)  /**
100     * ccs_get_last_name - Get last component of a domainname.
101     *
102     * @domain: Pointer to "struct domain_info".
103     *
104     * Returns the last component of the domainname.
105     */
106    const char *ccs_get_last_name(const struct domain_info *domain)
107  {  {
108          const char *cp0 = domain->domainname->name, *cp1;          const char *cp0 = domain->domainname->name;
109          if ((cp1 = strrchr(cp0, ' ')) != NULL) return cp1 + 1;          const char *cp1 = strrchr(cp0, ' ');
110            if (cp1)
111                    return cp1 + 1;
112          return cp0;          return cp0;
113  }  }
114    
115  int AddDomainACL(struct domain_info *domain, struct acl_info *acl)  /**
116     * ccs_add_domain_acl - Add the given ACL to the given domain.
117     *
118     * @domain: Pointer to "struct domain_info". May be NULL.
119     * @acl:    Pointer to "struct acl_info".
120     *
121     * Returns 0.
122     */
123    int ccs_add_domain_acl(struct domain_info *domain, struct acl_info *acl)
124  {  {
125          list1_add_tail_mb(&acl->list, &domain->acl_info_list);          if (domain) {
126          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);                  /*
127                     * We need to serialize because this function is called by
128                     * various update functions.
129                     */
130                    static DEFINE_SPINLOCK(lock);
131                    /***** CRITICAL SECTION START *****/
132                    spin_lock(&lock);
133                    list1_add_tail_mb(&acl->list, &domain->acl_info_list);
134                    spin_unlock(&lock);
135                    /***** CRITICAL SECTION END *****/
136            } else {
137                    acl->type &= ~ACL_DELETED;
138            }
139            ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
140          return 0;          return 0;
141  }  }
142    
143  int DelDomainACL(struct acl_info *ptr)  /**
144     * ccs_del_domain_acl - Delete the given ACL from the domain.
145     *
146     * @acl: Pointer to "struct acl_info". May be NULL.
147     *
148     * Returns 0.
149     */
150    int ccs_del_domain_acl(struct acl_info *acl)
151  {  {
152          ptr->is_deleted = 1;          if (acl)
153          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);                  acl->type |= ACL_DELETED;
154            ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
155          return 0;          return 0;
156  }  }
157    
158  /*************************  DOMAIN INITIALIZER HANDLER  *************************/  /**
159     * audit_execute_handler_log - Audit execute_handler log.
160     *
161     * @is_default: True if it is "execute_handler" log.
162     * @handler:    The realpath of the handler.
163     * @bprm:       Pointer to "struct linux_binprm".
164     *
165     * Returns 0 on success, negative value otherwise.
166     */
167    static int audit_execute_handler_log(const bool is_default,
168                                         const char *handler,
169                                         struct linux_binprm *bprm)
170    {
171            struct ccs_request_info r;
172            ccs_init_request_info(&r, NULL, CCS_TOMOYO_MAC_FOR_FILE);
173            r.bprm = bprm;
174            return ccs_write_audit_log(true, &r, "%s %s\n",
175                                       is_default ? KEYWORD_EXECUTE_HANDLER :
176                                       KEYWORD_DENIED_EXECUTE_HANDLER, handler);
177    }
178    
179    /**
180     * audit_domain_creation_log - Audit domain creation log.
181     *
182     * @domain:  Pointer to "struct domain_info".
183     *
184     * Returns 0 on success, negative value otherwise.
185     */
186    static int audit_domain_creation_log(struct domain_info *domain)
187    {
188            struct ccs_request_info r;
189            ccs_init_request_info(&r, domain, CCS_TOMOYO_MAC_FOR_FILE);
190            return ccs_write_audit_log(false, &r, "use_profile %u\n", r.profile);
191    }
192    
193    /* The list for "struct domain_initializer_entry". */
194  static LIST1_HEAD(domain_initializer_list);  static LIST1_HEAD(domain_initializer_list);
195    
196  static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)  /**
197     * update_domain_initializer_entry - Update "struct domain_initializer_entry" list.
198     *
199     * @domainname: The name of domain. May be NULL.
200     * @program:    The name of program.
201     * @is_not:     True if it is "no_initialize_domain" entry.
202     * @is_delete:  True if it is a delete request.
203     *
204     * Returns 0 on success, negative value otherwise.
205     */
206    static int update_domain_initializer_entry(const char *domainname,
207                                               const char *program,
208                                               const bool is_not,
209                                               const bool is_delete)
210  {  {
211          struct domain_initializer_entry *new_entry, *ptr;          struct domain_initializer_entry *new_entry;
212            struct domain_initializer_entry *ptr;
213          static DEFINE_MUTEX(lock);          static DEFINE_MUTEX(lock);
214          const struct path_info *saved_program, *saved_domainname = NULL;          const struct path_info *saved_program;
215            const struct path_info *saved_domainname = NULL;
216          int error = -ENOMEM;          int error = -ENOMEM;
217          bool is_last_name = 0;          bool is_last_name = false;
218          if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */          if (!ccs_is_correct_path(program, 1, -1, -1, __func__))
219                    return -EINVAL; /* No patterns allowed. */
220          if (domainname) {          if (domainname) {
221                  if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {                  if (!ccs_is_domain_def(domainname) &&
222                          is_last_name = 1;                      ccs_is_correct_path(domainname, 1, -1, -1, __func__))
223                  } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {                          is_last_name = true;
224                    else if (!ccs_is_correct_domain(domainname, __func__))
225                          return -EINVAL;                          return -EINVAL;
226                  }                  saved_domainname = ccs_save_name(domainname);
227                  if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;                  if (!saved_domainname)
228          }                          return -ENOMEM;
229          if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;          }
230            saved_program = ccs_save_name(program);
231            if (!saved_program)
232                    return -ENOMEM;
233          mutex_lock(&lock);          mutex_lock(&lock);
234          list1_for_each_entry(ptr, &domain_initializer_list, list) {          list1_for_each_entry(ptr, &domain_initializer_list, list) {
235                  if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {                  if (ptr->is_not != is_not ||
236                          ptr->is_deleted = is_delete;                      ptr->domainname != saved_domainname ||
237                          error = 0;                      ptr->program != saved_program)
238                          goto out;                          continue;
239                  }                  ptr->is_deleted = is_delete;
240                    error = 0;
241                    goto out;
242          }          }
243          if (is_delete) {          if (is_delete) {
244                  error = -ENOENT;                  error = -ENOENT;
245                  goto out;                  goto out;
246          }          }
247          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;          new_entry = ccs_alloc_element(sizeof(*new_entry));
248            if (!new_entry)
249                    goto out;
250          new_entry->domainname = saved_domainname;          new_entry->domainname = saved_domainname;
251          new_entry->program = saved_program;          new_entry->program = saved_program;
252          new_entry->is_not = is_not;          new_entry->is_not = is_not;
# Line 151  static int AddDomainInitializerEntry(con Line 255  static int AddDomainInitializerEntry(con
255          error = 0;          error = 0;
256   out:   out:
257          mutex_unlock(&lock);          mutex_unlock(&lock);
258            ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
259          return error;          return error;
260  }  }
261    
262  int ReadDomainInitializerPolicy(struct io_buffer *head)  /**
263     * ccs_read_domain_initializer_policy - Read "struct domain_initializer_entry" list.
264     *
265     * @head: Pointer to "struct ccs_io_buffer".
266     *
267     * Returns true on success, false otherwise.
268     */
269    bool ccs_read_domain_initializer_policy(struct ccs_io_buffer *head)
270  {  {
271          struct list1_head *pos;          struct list1_head *pos;
272          list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {          list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {
273                    const char *no;
274                    const char *from = "";
275                    const char *domain = "";
276                  struct domain_initializer_entry *ptr;                  struct domain_initializer_entry *ptr;
277                  ptr = list1_entry(pos, struct domain_initializer_entry, list);                  ptr = list1_entry(pos, struct domain_initializer_entry, list);
278                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
279                            continue;
280                    no = ptr->is_not ? "no_" : "";
281                  if (ptr->domainname) {                  if (ptr->domainname) {
282                          if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;                          from = " from ";
283                  } else {                          domain = ptr->domainname->name;
                         if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) return -ENOMEM;  
284                  }                  }
285                    if (!ccs_io_printf(head,
286                                       "%s" KEYWORD_INITIALIZE_DOMAIN "%s%s%s\n",
287                                       no, ptr->program->name, from, domain))
288                                    goto out;
289          }          }
290          return 0;          return true;
291     out:
292            return false;
293  }  }
294    
295  int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete)  /**
296     * ccs_write_domain_initializer_policy - Write "struct domain_initializer_entry" list.
297     *
298     * @data:      String to parse.
299     * @is_not:    True if it is "no_initialize_domain" entry.
300     * @is_delete: True if it is a delete request.
301     *
302     * Returns 0 on success, negative value otherwise.
303     */
304    int ccs_write_domain_initializer_policy(char *data, const bool is_not,
305                                            const bool is_delete)
306  {  {
307          char *cp = strstr(data, " from ");          char *cp = strstr(data, " from ");
308          if (cp) {          if (cp) {
309                  *cp = '\0';                  *cp = '\0';
310                  return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete);                  return update_domain_initializer_entry(cp + 6, data, is_not,
311          } else {                                                         is_delete);
                 return AddDomainInitializerEntry(NULL, data, is_not, is_delete);  
312          }          }
313            return update_domain_initializer_entry(NULL, data, is_not, is_delete);
314  }  }
315    
316  static bool IsDomainInitializer(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)  /**
317     * is_domain_initializer - Check whether the given program causes domainname reinitialization.
318     *
319     * @domainname: The name of domain.
320     * @program:    The name of program.
321     * @last_name:  The last component of @domainname.
322     *
323     * Returns true if executing @program reinitializes domain transition,
324     * false otherwise.
325     */
326    static bool is_domain_initializer(const struct path_info *domainname,
327                                      const struct path_info *program,
328                                      const struct path_info *last_name)
329  {  {
330          struct domain_initializer_entry *ptr;          struct domain_initializer_entry *ptr;
331          bool flag = 0;          bool flag = false;
332          list1_for_each_entry(ptr,  &domain_initializer_list, list) {          list1_for_each_entry(ptr,  &domain_initializer_list, list) {
333                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
334                            continue;
335                  if (ptr->domainname) {                  if (ptr->domainname) {
336                          if (!ptr->is_last_name) {                          if (!ptr->is_last_name) {
337                                  if (ptr->domainname != domainname) continue;                                  if (ptr->domainname != domainname)
338                                            continue;
339                          } else {                          } else {
340                                  if (pathcmp(ptr->domainname, last_name)) continue;                                  if (ccs_pathcmp(ptr->domainname, last_name))
341                                            continue;
342                          }                          }
343                  }                  }
344                  if (pathcmp(ptr->program, program)) continue;                  if (ccs_pathcmp(ptr->program, program))
345                  if (ptr->is_not) return 0;                          continue;
346                  flag = 1;                  if (ptr->is_not)
347                            return false;
348                    flag = true;
349          }          }
350          return flag;          return flag;
351  }  }
352    
353  /*************************  DOMAIN KEEPER HANDLER  *************************/  /* The list for "struct domain_keeper_entry". */
   
354  static LIST1_HEAD(domain_keeper_list);  static LIST1_HEAD(domain_keeper_list);
355    
356  static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)  /**
357     * update_domain_keeper_entry - Update "struct domain_keeper_entry" list.
358     *
359     * @domainname: The name of domain.
360     * @program:    The name of program. May be NULL.
361     * @is_not:     True if it is "no_keep_domain" entry.
362     * @is_delete:  True if it is a delete request.
363     *
364     * Returns 0 on success, negative value otherwise.
365     */
366    static int update_domain_keeper_entry(const char *domainname,
367                                          const char *program,
368                                          const bool is_not, const bool is_delete)
369  {  {
370          struct domain_keeper_entry *new_entry, *ptr;          struct domain_keeper_entry *new_entry;
371          const struct path_info *saved_domainname, *saved_program = NULL;          struct domain_keeper_entry *ptr;
372            const struct path_info *saved_domainname;
373            const struct path_info *saved_program = NULL;
374          static DEFINE_MUTEX(lock);          static DEFINE_MUTEX(lock);
375          int error = -ENOMEM;          int error = -ENOMEM;
376          bool is_last_name = 0;          bool is_last_name = false;
377          if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {          if (!ccs_is_domain_def(domainname) &&
378                  is_last_name = 1;              ccs_is_correct_path(domainname, 1, -1, -1, __func__))
379          } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {                  is_last_name = true;
380            else if (!ccs_is_correct_domain(domainname, __func__))
381                  return -EINVAL;                  return -EINVAL;
         }  
382          if (program) {          if (program) {
383                  if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;                  if (!ccs_is_correct_path(program, 1, -1, -1, __func__))
384                  if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;                          return -EINVAL;
385          }                  saved_program = ccs_save_name(program);
386          if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;                  if (!saved_program)
387                            return -ENOMEM;
388            }
389            saved_domainname = ccs_save_name(domainname);
390            if (!saved_domainname)
391                    return -ENOMEM;
392          mutex_lock(&lock);          mutex_lock(&lock);
393          list1_for_each_entry(ptr, &domain_keeper_list, list) {          list1_for_each_entry(ptr, &domain_keeper_list, list) {
394                  if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {                  if (ptr->is_not != is_not ||
395                          ptr->is_deleted = is_delete;                      ptr->domainname != saved_domainname ||
396                          error = 0;                      ptr->program != saved_program)
397                          goto out;                          continue;
398                  }                  ptr->is_deleted = is_delete;
399                    error = 0;
400                    goto out;
401          }          }
402          if (is_delete) {          if (is_delete) {
403                  error = -ENOENT;                  error = -ENOENT;
404                  goto out;                  goto out;
405          }          }
406          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;          new_entry = ccs_alloc_element(sizeof(*new_entry));
407            if (!new_entry)
408                    goto out;
409          new_entry->domainname = saved_domainname;          new_entry->domainname = saved_domainname;
410          new_entry->program = saved_program;          new_entry->program = saved_program;
411          new_entry->is_not = is_not;          new_entry->is_not = is_not;
# Line 243  static int AddDomainKeeperEntry(const ch Line 414  static int AddDomainKeeperEntry(const ch
414          error = 0;          error = 0;
415   out:   out:
416          mutex_unlock(&lock);          mutex_unlock(&lock);
417            ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
418          return error;          return error;
419  }  }
420    
421  int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete)  /**
422     * ccs_write_domain_keeper_policy - Write "struct domain_keeper_entry" list.
423     *
424     * @data:      String to parse.
425     * @is_not:    True if it is "no_keep_domain" entry.
426     * @is_delete: True if it is a delete request.
427     *
428     */
429    int ccs_write_domain_keeper_policy(char *data, const bool is_not,
430                                       const bool is_delete)
431  {  {
432          char *cp = strstr(data, " from ");          char *cp = strstr(data, " from ");
433          if (cp) {          if (cp) {
434                  *cp = '\0';                  *cp = '\0';
435                  return AddDomainKeeperEntry(cp + 6, data, is_not, is_delete);                  return update_domain_keeper_entry(cp + 6, data,
436          } else {                                                    is_not, is_delete);
                 return AddDomainKeeperEntry(data, NULL, is_not, is_delete);  
437          }          }
438            return update_domain_keeper_entry(data, NULL, is_not, is_delete);
439  }  }
440    
441  int ReadDomainKeeperPolicy(struct io_buffer *head)  /**
442     * ccs_read_domain_keeper_policy - Read "struct domain_keeper_entry" list.
443     *
444     * @head: Pointer to "struct ccs_io_buffer".
445     *
446     * Returns true on success, false otherwise.
447     */
448    bool ccs_read_domain_keeper_policy(struct ccs_io_buffer *head)
449  {  {
450          struct list1_head *pos;          struct list1_head *pos;
451          list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {          list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {
452                  struct domain_keeper_entry *ptr;                  struct domain_keeper_entry *ptr;
453                    const char *no;
454                    const char *from = "";
455                    const char *program = "";
456                  ptr = list1_entry(pos, struct domain_keeper_entry, list);                  ptr = list1_entry(pos, struct domain_keeper_entry, list);
457                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
458                            continue;
459                    no = ptr->is_not ? "no_" : "";
460                  if (ptr->program) {                  if (ptr->program) {
461                          if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;                          from = " from ";
462                  } else {                          program = ptr->program->name;
                         if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->domainname->name)) return -ENOMEM;  
463                  }                  }
464                    if (!ccs_io_printf(head,
465                                       "%s" KEYWORD_KEEP_DOMAIN "%s%s%s\n", no,
466                                       program, from, ptr->domainname->name))
467                                    goto out;
468          }          }
469          return 0;          return true;
470     out:
471            return false;
472  }  }
473    
474  static bool IsDomainKeeper(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)  /**
475     * is_domain_keeper - Check whether the given program causes domain transition suppression.
476     *
477     * @domainname: The name of domain.
478     * @program:    The name of program.
479     * @last_name:  The last component of @domainname.
480     *
481     * Returns true if executing @program supresses domain transition,
482     * false otherwise.
483     */
484    static bool is_domain_keeper(const struct path_info *domainname,
485                                 const struct path_info *program,
486                                 const struct path_info *last_name)
487  {  {
488          struct domain_keeper_entry *ptr;          struct domain_keeper_entry *ptr;
489          bool flag = 0;          bool flag = false;
490          list1_for_each_entry(ptr, &domain_keeper_list, list) {          list1_for_each_entry(ptr, &domain_keeper_list, list) {
491                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
492                            continue;
493                  if (!ptr->is_last_name) {                  if (!ptr->is_last_name) {
494                          if (ptr->domainname != domainname) continue;                          if (ptr->domainname != domainname)
495                                    continue;
496                  } else {                  } else {
497                          if (pathcmp(ptr->domainname, last_name)) continue;                          if (ccs_pathcmp(ptr->domainname, last_name))
498                                    continue;
499                  }                  }
500                  if (ptr->program && pathcmp(ptr->program, program)) continue;                  if (ptr->program && ccs_pathcmp(ptr->program, program))
501                  if (ptr->is_not) return 0;                          continue;
502                  flag = 1;                  if (ptr->is_not)
503                            return false;
504                    flag = true;
505          }          }
506          return flag;          return flag;
507  }  }
508    
509  /*************************  SYMBOLIC LINKED PROGRAM HANDLER  *************************/  /* The list for "struct alias_entry". */
   
510  static LIST1_HEAD(alias_list);  static LIST1_HEAD(alias_list);
511    
512  static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete)  /**
513     * update_alias_entry - Update "struct alias_entry" list.
514     *
515     * @original_name: The original program's real name.
516     * @aliased_name:  The symbolic program's symbolic link's name.
517     * @is_delete:     True if it is a delete request.
518     *
519     * Returns 0 on success, negative value otherwise.
520     */
521    static int update_alias_entry(const char *original_name,
522                                  const char *aliased_name,
523                                  const bool is_delete)
524  {  {
525          struct alias_entry *new_entry, *ptr;          struct alias_entry *new_entry;
526            struct alias_entry *ptr;
527          static DEFINE_MUTEX(lock);          static DEFINE_MUTEX(lock);
528          const struct path_info *saved_original_name, *saved_aliased_name;          const struct path_info *saved_original_name;
529            const struct path_info *saved_aliased_name;
530          int error = -ENOMEM;          int error = -ENOMEM;
531          if (!IsCorrectPath(original_name, 1, -1, -1, __FUNCTION__) || !IsCorrectPath(aliased_name, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */          if (!ccs_is_correct_path(original_name, 1, -1, -1, __func__) ||
532          if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aliased_name = SaveName(aliased_name)) == NULL) return -ENOMEM;              !ccs_is_correct_path(aliased_name, 1, -1, -1, __func__))
533                    return -EINVAL; /* No patterns allowed. */
534            saved_original_name = ccs_save_name(original_name);
535            saved_aliased_name = ccs_save_name(aliased_name);
536            if (!saved_original_name || !saved_aliased_name)
537                    return -ENOMEM;
538          mutex_lock(&lock);          mutex_lock(&lock);
539          list1_for_each_entry(ptr, &alias_list, list) {          list1_for_each_entry(ptr, &alias_list, list) {
540                  if (ptr->original_name == saved_original_name && ptr->aliased_name == saved_aliased_name) {                  if (ptr->original_name != saved_original_name ||
541                          ptr->is_deleted = is_delete;                      ptr->aliased_name != saved_aliased_name)
542                          error = 0;                          continue;
543                          goto out;                  ptr->is_deleted = is_delete;
544                  }                  error = 0;
545                    goto out;
546          }          }
547          if (is_delete) {          if (is_delete) {
548                  error = -ENOENT;                  error = -ENOENT;
549                  goto out;                  goto out;
550          }          }
551          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;          new_entry = ccs_alloc_element(sizeof(*new_entry));
552            if (!new_entry)
553                    goto out;
554          new_entry->original_name = saved_original_name;          new_entry->original_name = saved_original_name;
555          new_entry->aliased_name = saved_aliased_name;          new_entry->aliased_name = saved_aliased_name;
556          list1_add_tail_mb(&new_entry->list, &alias_list);          list1_add_tail_mb(&new_entry->list, &alias_list);
557          error = 0;          error = 0;
558   out:   out:
559          mutex_unlock(&lock);          mutex_unlock(&lock);
560            ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
561          return error;          return error;
562  }  }
563    
564  int ReadAliasPolicy(struct io_buffer *head)  /**
565     * ccs_read_alias_policy - Read "struct alias_entry" list.
566     *
567     * @head: Pointer to "struct ccs_io_buffer".
568     *
569     * Returns true on success, false otherwise.
570     */
571    bool ccs_read_alias_policy(struct ccs_io_buffer *head)
572  {  {
573          struct list1_head *pos;          struct list1_head *pos;
574          list1_for_each_cookie(pos, head->read_var2, &alias_list) {          list1_for_each_cookie(pos, head->read_var2, &alias_list) {
575                  struct alias_entry *ptr;                  struct alias_entry *ptr;
576                  ptr = list1_entry(pos, struct alias_entry, list);                  ptr = list1_entry(pos, struct alias_entry, list);
577                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
578                  if (io_printf(head, KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, ptr->aliased_name->name)) return -ENOMEM;                          continue;
579                    if (!ccs_io_printf(head, KEYWORD_ALIAS "%s %s\n",
580                                       ptr->original_name->name,
581                                       ptr->aliased_name->name))
582                            goto out;
583          }          }
584          return 0;          return true;
585     out:
586            return false;
587  }  }
588    
589  int AddAliasPolicy(char *data, const bool is_delete)  /**
590     * ccs_write_alias_policy - Write "struct alias_entry" list.
591     *
592     * @data:      String to parse.
593     * @is_delete: True if it is a delete request.
594     *
595     * Returns 0 on success, negative value otherwise.
596     */
597    int ccs_write_alias_policy(char *data, const bool is_delete)
598  {  {
599          char *cp = strchr(data, ' ');          char *cp = strchr(data, ' ');
600          if (!cp) return -EINVAL;          if (!cp)
601                    return -EINVAL;
602          *cp++ = '\0';          *cp++ = '\0';
603          return AddAliasEntry(data, cp, is_delete);          return update_alias_entry(data, cp, is_delete);
604  }  }
605    
606  /*************************  DOMAIN AGGREGATOR HANDLER  *************************/  /* The list for "struct aggregator_entry". */
   
607  static LIST1_HEAD(aggregator_list);  static LIST1_HEAD(aggregator_list);
608    
609  static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete)  /**
610     * update_aggregator_entry - Update "struct aggregator_entry" list.
611     *
612     * @original_name:   The original program's name.
613     * @aggregated_name: The aggregated program's name.
614     * @is_delete:       True if it is a delete request.
615     *
616     * Returns 0 on success, negative value otherwise.
617     */
618    static int update_aggregator_entry(const char *original_name,
619                                       const char *aggregated_name,
620                                       const bool is_delete)
621  {  {
622          struct aggregator_entry *new_entry, *ptr;          struct aggregator_entry *new_entry;
623            struct aggregator_entry *ptr;
624          static DEFINE_MUTEX(lock);          static DEFINE_MUTEX(lock);
625          const struct path_info *saved_original_name, *saved_aggregated_name;          const struct path_info *saved_original_name;
626            const struct path_info *saved_aggregated_name;
627          int error = -ENOMEM;          int error = -ENOMEM;
628          if (!IsCorrectPath(original_name, 1, 0, -1, __FUNCTION__) || !IsCorrectPath(aggregated_name, 1, -1, -1, __FUNCTION__)) return -EINVAL;          if (!ccs_is_correct_path(original_name, 1, 0, -1, __func__) ||
629          if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aggregated_name = SaveName(aggregated_name)) == NULL) return -ENOMEM;              !ccs_is_correct_path(aggregated_name, 1, -1, -1, __func__))
630                    return -EINVAL;
631            saved_original_name = ccs_save_name(original_name);
632            saved_aggregated_name = ccs_save_name(aggregated_name);
633            if (!saved_original_name || !saved_aggregated_name)
634                    return -ENOMEM;
635          mutex_lock(&lock);          mutex_lock(&lock);
636          list1_for_each_entry(ptr, &aggregator_list, list) {          list1_for_each_entry(ptr, &aggregator_list, list) {
637                  if (ptr->original_name == saved_original_name && ptr->aggregated_name == saved_aggregated_name) {                  if (ptr->original_name != saved_original_name ||
638                          ptr->is_deleted = is_delete;                      ptr->aggregated_name != saved_aggregated_name)
639                          error = 0;                          continue;
640                          goto out;                  ptr->is_deleted = is_delete;
641                  }                  error = 0;
642                    goto out;
643          }          }
644          if (is_delete) {          if (is_delete) {
645                  error = -ENOENT;                  error = -ENOENT;
646                  goto out;                  goto out;
647          }          }
648          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;          new_entry = ccs_alloc_element(sizeof(*new_entry));
649            if (!new_entry)
650                    goto out;
651          new_entry->original_name = saved_original_name;          new_entry->original_name = saved_original_name;
652          new_entry->aggregated_name = saved_aggregated_name;          new_entry->aggregated_name = saved_aggregated_name;
653          list1_add_tail_mb(&new_entry->list, &aggregator_list);          list1_add_tail_mb(&new_entry->list, &aggregator_list);
654          error = 0;          error = 0;
655   out:   out:
656          mutex_unlock(&lock);          mutex_unlock(&lock);
657            ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
658          return error;          return error;
659  }  }
660    
661  int ReadAggregatorPolicy(struct io_buffer *head)  /**
662     * ccs_read_aggregator_policy - Read "struct aggregator_entry" list.
663     *
664     * @head: Pointer to "struct ccs_io_buffer".
665     *
666     * Returns true on success, false otherwise.
667     */
668    bool ccs_read_aggregator_policy(struct ccs_io_buffer *head)
669  {  {
670          struct list1_head *pos;          struct list1_head *pos;
671          list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {          list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {
672                  struct aggregator_entry *ptr;                  struct aggregator_entry *ptr;
673                  ptr = list1_entry(pos, struct aggregator_entry, list);                  ptr = list1_entry(pos, struct aggregator_entry, list);
674                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted)
675                  if (io_printf(head, KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, ptr->aggregated_name->name)) return -ENOMEM;                          continue;
676                    if (!ccs_io_printf(head, KEYWORD_AGGREGATOR "%s %s\n",
677                                       ptr->original_name->name,
678                                       ptr->aggregated_name->name))
679                            goto out;
680          }          }
681          return 0;          return true;
682     out:
683            return false;
684  }  }
685    
686  int AddAggregatorPolicy(char *data, const bool is_delete)  /**
687     * ccs_write_aggregator_policy - Write "struct aggregator_entry" list.
688     *
689     * @data:      String to parse.
690     * @is_delete: True if it is a delete request.
691     *
692     * Returns 0 on success, negative value otherwise.
693     */
694    int ccs_write_aggregator_policy(char *data, const bool is_delete)
695  {  {
696          char *cp = strchr(data, ' ');          char *cp = strchr(data, ' ');
697          if (!cp) return -EINVAL;          if (!cp)
698                    return -EINVAL;
699          *cp++ = '\0';          *cp++ = '\0';
700          return AddAggregatorEntry(data, cp, is_delete);          return update_aggregator_entry(data, cp, is_delete);
701  }  }
702    
703  /*************************  DOMAIN DELETION HANDLER  *************************/  /* Domain create/delete/undelete handler. */
704    
705  /* #define DEBUG_DOMAIN_UNDELETE */  /* #define DEBUG_DOMAIN_UNDELETE */
706    
707  int DeleteDomain(char *domainname0)  /**
708     * ccs_delete_domain - Delete a domain.
709     *
710     * @domainname: The name of domain.
711     *
712     * Returns 0.
713     */
714    int ccs_delete_domain(char *domainname)
715  {  {
716          struct domain_info *domain;          struct domain_info *domain;
717          struct path_info domainname;          struct path_info name;
718          domainname.name = domainname0;          name.name = domainname;
719          fill_path_info(&domainname);          ccs_fill_path_info(&name);
720          mutex_lock(&new_domain_assign_lock);          mutex_lock(&new_domain_assign_lock);
721  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
722          printk("DeleteDomain %s\n", domainname0);          printk(KERN_DEBUG "ccs_delete_domain %s\n", domainname);
723          list1_for_each_entry(domain, &domain_list, list) {          list1_for_each_entry(domain, &domain_list, list) {
724                  if (pathcmp(domain->domainname, &domainname)) continue;                  if (ccs_pathcmp(domain->domainname, &name))
725                  printk("List: %p %u\n", domain, domain->is_deleted);                          continue;
726                    printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
727          }          }
728  #endif  #endif
729          /* Is there an active domain? */          /* Is there an active domain? */
730          list1_for_each_entry(domain, &domain_list, list) {          list1_for_each_entry(domain, &domain_list, list) {
731                  struct domain_info *domain2;                  struct domain_info *domain2;
732                  /* Never delete KERNEL_DOMAIN */                  /* Never delete KERNEL_DOMAIN */
733                  if (domain == &KERNEL_DOMAIN || domain->is_deleted || pathcmp(domain->domainname, &domainname)) continue;                  if (domain == &KERNEL_DOMAIN)
734                            continue;
735                    if (domain->is_deleted ||
736                        ccs_pathcmp(domain->domainname, &name))
737                            continue;
738                  /* Mark already deleted domains as non undeletable. */                  /* Mark already deleted domains as non undeletable. */
739                  list1_for_each_entry(domain2, &domain_list, list) {                  list1_for_each_entry(domain2, &domain_list, list) {
740                          if (!domain2->is_deleted || pathcmp(domain2->domainname, &domainname)) continue;                          if (!domain2->is_deleted ||
741                                ccs_pathcmp(domain2->domainname, &name))
742                                    continue;
743  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
744                          if (domain2->is_deleted != 255) printk("Marked %p as non undeletable\n", domain2);                          if (domain2->is_deleted != 255)
745                                    printk(KERN_DEBUG
746                                           "Marked %p as non undeletable\n",
747                                           domain2);
748  #endif  #endif
749                          domain2->is_deleted = 255;                          domain2->is_deleted = 255;
750                  }                  }
751                  /* Delete and mark active domain as undeletable. */                  /* Delete and mark active domain as undeletable. */
752                  domain->is_deleted = 1;                  domain->is_deleted = 1;
753  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
754                  printk("Marked %p as undeletable\n", domain);                  printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
755  #endif  #endif
756                  break;                  break;
757          }          }
# Line 441  int DeleteDomain(char *domainname0) Line 759  int DeleteDomain(char *domainname0)
759          return 0;          return 0;
760  }  }
761    
762  struct domain_info *UndeleteDomain(const char *domainname0)  /**
763     * ccs_undelete_domain - Undelete a domain.
764     *
765     * @domainname: The name of domain.
766     *
767     * Returns pointer to "struct domain_info" on success, NULL otherwise.
768     */
769    struct domain_info *ccs_undelete_domain(const char *domainname)
770  {  {
771          struct domain_info *domain, *candidate_domain = NULL;          struct domain_info *domain;
772          struct path_info domainname;          struct domain_info *candidate_domain = NULL;
773          domainname.name = domainname0;          struct path_info name;
774          fill_path_info(&domainname);          name.name = domainname;
775            ccs_fill_path_info(&name);
776          mutex_lock(&new_domain_assign_lock);          mutex_lock(&new_domain_assign_lock);
777  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
778          printk("UndeleteDomain %s\n", domainname0);          printk(KERN_DEBUG "ccs_undelete_domain %s\n", domainname);
779          list1_for_each_entry(domain, &domain_list, list) {          list1_for_each_entry(domain, &domain_list, list) {
780                  if (pathcmp(domain->domainname, &domainname)) continue;                  if (ccs_pathcmp(domain->domainname, &name))
781                  printk("List: %p %u\n", domain, domain->is_deleted);                          continue;
782                    printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
783          }          }
784  #endif  #endif
785          list1_for_each_entry(domain, &domain_list, list) {          list1_for_each_entry(domain, &domain_list, list) {
786                  if (pathcmp(&domainname, domain->domainname)) continue;                  if (ccs_pathcmp(&name, domain->domainname))
787                            continue;
788                  if (!domain->is_deleted) {                  if (!domain->is_deleted) {
789                          /* This domain is active. I can't undelete. */                          /* This domain is active. I can't undelete. */
790                          candidate_domain = NULL;                          candidate_domain = NULL;
791  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
792                          printk("%p is active. I can't undelete.\n", domain);                          printk(KERN_DEBUG "%p is active. I can't undelete.\n",
793                                   domain);
794  #endif  #endif
795                          break;                          break;
796                  }                  }
797                  /* Is this domain undeletable? */                  /* Is this domain undeletable? */
798                  if (domain->is_deleted == 1) candidate_domain = domain;                  if (domain->is_deleted == 1)
799                            candidate_domain = domain;
800          }          }
801          if (candidate_domain) {          if (candidate_domain) {
802                  candidate_domain->is_deleted = 0;                  candidate_domain->is_deleted = 0;
803  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
804                  printk("%p was undeleted.\n", candidate_domain);                  printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
805  #endif  #endif
806          }          }
807          mutex_unlock(&new_domain_assign_lock);          mutex_unlock(&new_domain_assign_lock);
808          return candidate_domain;          return candidate_domain;
809  }  }
810    
811  /*************************  DOMAIN TRANSITION HANDLER  *************************/  /**
812     * ccs_find_or_assign_new_domain - Create a domain.
813  struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile)   *
814     * @domainname: The name of domain.
815     * @profile:    Profile number to assign if the domain was newly created.
816     *
817     * Returns pointer to "struct domain_info" on success, NULL otherwise.
818     */
819    struct domain_info *ccs_find_or_assign_new_domain(const char *domainname,
820                                                      const u8 profile)
821  {  {
822          struct domain_info *domain = NULL;          struct domain_info *domain = NULL;
823          const struct path_info *saved_domainname;          const struct path_info *saved_domainname;
824          mutex_lock(&new_domain_assign_lock);          mutex_lock(&new_domain_assign_lock);
825          if ((domain = FindDomain(domainname)) != NULL) goto out;          domain = ccs_find_domain(domainname);
826          if (!IsCorrectDomain(domainname, __FUNCTION__)) goto out;          if (domain)
827          if ((saved_domainname = SaveName(domainname)) == NULL) goto out;                  goto out;
828            if (!ccs_is_correct_domain(domainname, __func__))
829                    goto out;
830            saved_domainname = ccs_save_name(domainname);
831            if (!saved_domainname)
832                    goto out;
833          /* Can I reuse memory of deleted domain? */          /* Can I reuse memory of deleted domain? */
834          list1_for_each_entry(domain, &domain_list, list) {          list1_for_each_entry(domain, &domain_list, list) {
835                  struct task_struct *p;                  struct task_struct *p;
836                  struct acl_info *ptr;                  struct acl_info *ptr;
837                  int flag;                  bool flag;
838                  if (!domain->is_deleted || domain->domainname != saved_domainname) continue;                  if (!domain->is_deleted ||
839                  flag = 0;                      domain->domainname != saved_domainname)
840                            continue;
841                    flag = false;
842                  /***** CRITICAL SECTION START *****/                  /***** CRITICAL SECTION START *****/
843                  read_lock(&tasklist_lock);                  read_lock(&tasklist_lock);
844                  for_each_process(p) {                  for_each_process(p) {
845                          if (p->domain_info == domain) { flag = 1; break; }                          if (p->domain_info != domain)
846                                    continue;
847                            flag = true;
848                            break;
849                  }                  }
850                  read_unlock(&tasklist_lock);                  read_unlock(&tasklist_lock);
851                  /***** CRITICAL SECTION END *****/                  /***** CRITICAL SECTION END *****/
852                  if (flag) continue;                  if (flag)
853                            continue;
854  #ifdef DEBUG_DOMAIN_UNDELETE  #ifdef DEBUG_DOMAIN_UNDELETE
855                  printk("Reusing %p %s\n", domain, domain->domainname->name);                  printk(KERN_DEBUG "Reusing %p %s\n", domain,
856                           domain->domainname->name);
857  #endif  #endif
858                  list1_for_each_entry(ptr, &domain->acl_info_list, list) ptr->is_deleted = 1;                  list1_for_each_entry(ptr, &domain->acl_info_list, list) {
859                            ptr->type |= ACL_DELETED;
860                    }
861                    /*
862                     * Don't use ccs_set_domain_flag() because
863                     * new_domain_assign_lock is held.
864                     */
865                    domain->flags = 0;
866                  domain->profile = profile;                  domain->profile = profile;
867                  domain->quota_warned = 0;                  domain->quota_warned = false;
868                  mb(); /* Avoid out-of-order execution. */                  mb(); /* Avoid out-of-order execution. */
869                  domain->is_deleted = 0;                  domain->is_deleted = 0;
870                  goto out;                  goto out;
871          }          }
872          /* No memory reusable. Create using new memory. */          /* No memory reusable. Create using new memory. */
873          if ((domain = alloc_element(sizeof(*domain))) != NULL) {          domain = ccs_alloc_element(sizeof(*domain));
874            if (domain) {
875                  INIT_LIST1_HEAD(&domain->acl_info_list);                  INIT_LIST1_HEAD(&domain->acl_info_list);
876                  domain->domainname = saved_domainname;                  domain->domainname = saved_domainname;
877                  domain->profile = profile;                  domain->profile = profile;
878                  list1_add_tail_mb(&domain->list, &domain_list);                  list1_add_tail_mb(&domain->list, &domain_list);
879          }          }
880   out: ;   out:
881          mutex_unlock(&new_domain_assign_lock);          mutex_unlock(&new_domain_assign_lock);
882          return domain;          return domain;
883  }  }
884    
885  static int Escape(char *dest, const char *src, int dest_len)  /**
886  {   * get_argv0 - Get argv[0].
887          while (*src) {   *
888                  const unsigned char c = * (const unsigned char *) src;   * @bprm: Pointer to "struct linux_binprm".
889                  if (c == '\\') {   * @tmp:  Buffer for temporal use.
890                          dest_len -= 2;   *
891                          if (dest_len <= 0) goto out;   * Returns true on success, false otherwise.
892                          *dest++ = '\\';   */
893                          *dest++ = '\\';  static bool get_argv0(struct linux_binprm *bprm, struct ccs_page_buffer *tmp)
                 } else if (c > ' ' && c < 127) {  
                         if (--dest_len <= 0) goto out;  
                         *dest++ = c;  
                 } else {  
                         dest_len -= 4;  
                         if (dest_len <= 0) goto out;  
                         *dest++ = '\\';  
                         *dest++ = (c >> 6) + '0';  
                         *dest++ = ((c >> 3) & 7) + '0';  
                         *dest++ = (c & 7) + '0';  
                 }  
                 src++;  
         }  
         if (--dest_len <= 0) goto out;  
         *dest = '\0';  
         return 0;  
  out:  
         return -ENOMEM;  
 }  
   
 static char *get_argv0(struct linux_binprm *bprm)  
894  {  {
895          char *arg_ptr = ccs_alloc(PAGE_SIZE); /* Initial buffer. */          char *arg_ptr = tmp->buffer;
896          int arg_len = 0;          int arg_len = 0;
897          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
898          int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;          int i = pos / PAGE_SIZE;
899          if (!bprm->argc || !arg_ptr) goto out;          int offset = pos % PAGE_SIZE;
900            bool done = false;
901            if (!bprm->argc)
902                    goto out;
903          while (1) {          while (1) {
904                  struct page *page;                  struct page *page;
905  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)                  const char *kaddr;
906                  if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
907                    if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page,
908                                       NULL) <= 0)
909                            goto out;
910                    pos += PAGE_SIZE - offset;
911  #else  #else
912                  page = bprm->page[i];                  page = bprm->page[i];
913  #endif  #endif
914                  { /* Map and copy to kernel buffer and unmap. */                  /* Map. */
915                          const char *kaddr = kmap(page);                  kaddr = kmap(page);
916                          if (!kaddr) { /* Mapping failed. */                  if (!kaddr) { /* Mapping failed. */
917  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
918                                  put_page(page);                          put_page(page);
919  #endif  #endif
920                                  goto out;                          goto out;
921                    }
922                    /* Read. */
923                    while (offset < PAGE_SIZE) {
924                            const unsigned char c = kaddr[offset++];
925                            if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
926                                    if (c == '\\') {
927                                            arg_ptr[arg_len++] = '\\';
928                                            arg_ptr[arg_len++] = '\\';
929                                    } else if (c == '/') {
930                                            arg_len = 0;
931                                    } else if (c > ' ' && c < 127) {
932                                            arg_ptr[arg_len++] = c;
933                                    } else {
934                                            arg_ptr[arg_len++] = '\\';
935                                            arg_ptr[arg_len++] = (c >> 6) + '0';
936                                            arg_ptr[arg_len++]
937                                                    = ((c >> 3) & 7) + '0';
938                                            arg_ptr[arg_len++] = (c & 7) + '0';
939                                    }
940                            } else {
941                                    arg_ptr[arg_len] = '\0';
942                                    done = true;
943                                    break;
944                          }                          }
                         memmove(arg_ptr + arg_len, kaddr + offset, PAGE_SIZE - offset);  
                         kunmap(page);  
945                  }                  }
946  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)                  /* Unmap. */
947                    kunmap(page);
948    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
949                  put_page(page);                  put_page(page);
                 pos += PAGE_SIZE - offset;  
950  #endif  #endif
                 arg_len += PAGE_SIZE - offset;  
                 if (memchr(arg_ptr, '\0', arg_len)) break;  
                 { /* Initial buffer was too small for argv[0]. Retry after expanding buffer. */  
                         char *tmp_arg_ptr = ccs_alloc(arg_len + PAGE_SIZE);  
                         if (!tmp_arg_ptr) goto out;  
                         memmove(tmp_arg_ptr, arg_ptr, arg_len);  
                         ccs_free(arg_ptr);  
                         arg_ptr = tmp_arg_ptr;  
                 }  
951                  i++;                  i++;
952                  offset = 0;                  offset = 0;
953                    if (done)
954                            break;
955          }          }
956          return arg_ptr;          return true;
957   out: /* Release initial buffer. */   out:
958          ccs_free(arg_ptr);          return false;
         return NULL;  
959  }  }
960    
961  static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check)  /**
962     * find_next_domain - Find a domain.
963     *
964     * @r:       Pointer to "struct ccs_request_info".
965     * @handler: Pathname to verify. May be NULL.
966     *
967     * Returns 0 on success, negative value otherwise.
968     */
969    static int find_next_domain(struct ccs_request_info *r,
970                                const struct path_info *handler)
971  {  {
972          /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */          /*
973          struct domain_info *old_domain = current->domain_info, *domain = NULL;           * This function assumes that the size of buffer returned by
974          const char *old_domain_name = old_domain->domainname->name;           * ccs_realpath() = CCS_MAX_PATHNAME_LEN.
975             */
976            struct domain_info *domain = NULL;
977            const char *old_domain_name = r->domain->domainname->name;
978            struct linux_binprm *bprm = r->bprm;
979            struct ccs_page_buffer *tmp = r->obj->tmp;
980          const char *original_name = bprm->filename;          const char *original_name = bprm->filename;
981          struct file *filp = bprm->file;          const u8 mode = r->mode;
982            const bool is_enforce = (mode == 3);
983            const u32 tomoyo_flags = r->tomoyo_flags;
984          char *new_domain_name = NULL;          char *new_domain_name = NULL;
985          char *real_program_name = NULL, *symlink_program_name = NULL;          char *real_program_name = NULL;
986          const bool is_enforce = (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_FILE) == 3);          char *symlink_program_name = NULL;
987            struct path_info rn; /* real name */
988            struct path_info sn; /* symlink name */
989            struct path_info ln; /* last name */
990          int retval;          int retval;
         struct path_info r, s, l;  
991    
992          {          {
993                  /*                  /*
994                   * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .                   * Built-in initializers. This is needed because policies are
995                     * not loaded until starting /sbin/init.
996                   */                   */
997                  static int first = 1;                  static bool first = true;
998                  if (first) {                  if (first) {
999                          AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);                          update_domain_initializer_entry(NULL, "/sbin/hotplug",
1000                          AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);                                                          false, false);
1001                          first = 0;                          update_domain_initializer_entry(NULL, "/sbin/modprobe",
1002                                                            false, false);
1003                            first = false;
1004                  }                  }
1005          }          }
1006    
1007          /* Get realpath of program. */   retry:
1008          retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */          current->tomoyo_flags = tomoyo_flags;
1009          if ((real_program_name = realpath(original_name)) == NULL) goto out;          r->tomoyo_flags = tomoyo_flags;
1010          /* Get realpath of symbolic link. */          /* Get ccs_realpath of program. */
1011          if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;          retval = -ENOENT; /* I hope ccs_realpath() won't fail with -ENOMEM. */
1012            ccs_free(real_program_name);
1013          r.name = real_program_name;          real_program_name = ccs_realpath(original_name);
1014          fill_path_info(&r);          if (!real_program_name)
1015          s.name = symlink_program_name;                  goto out;
1016          fill_path_info(&s);          /* Get ccs_realpath of symbolic link. */
1017          if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;          ccs_free(symlink_program_name);
1018          else l.name = old_domain_name;          symlink_program_name = ccs_realpath_nofollow(original_name);
1019          fill_path_info(&l);          if (!symlink_program_name)
1020                    goto out;
1021    
1022          if (!do_perm_check) goto ok;          rn.name = real_program_name;
1023            ccs_fill_path_info(&rn);
1024            sn.name = symlink_program_name;
1025            ccs_fill_path_info(&sn);
1026            ln.name = ccs_get_last_name(r->domain);
1027            ccs_fill_path_info(&ln);
1028    
1029            if (handler) {
1030                    if (ccs_pathcmp(&rn, handler)) {
1031                            /* Failed to verify execute handler. */
1032                            static u8 counter = 20;
1033                            if (counter) {
1034                                    counter--;
1035                                    printk(KERN_WARNING "Failed to verify: %s\n",
1036                                           handler->name);
1037                            }
1038                            goto out;
1039                    }
1040                    goto calculate_domain;
1041            }
1042    
1043          /* Check 'alias' directive. */          /* Check 'alias' directive. */
1044          if (pathcmp(&r, &s)) {          if (ccs_pathcmp(&rn, &sn)) {
1045                  struct alias_entry *ptr;                  struct alias_entry *ptr;
1046                  /* Is this program allowed to be called via symbolic links? */                  /* Is this program allowed to be called via symbolic links? */
1047                  list1_for_each_entry(ptr, &alias_list, list) {                  list1_for_each_entry(ptr, &alias_list, list) {
1048                          if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;                          if (ptr->is_deleted ||
1049                                ccs_pathcmp(&rn, ptr->original_name) ||
1050                                ccs_pathcmp(&sn, ptr->aliased_name))
1051                                    continue;
1052                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
1053                          strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);                          strncpy(real_program_name, ptr->aliased_name->name,
1054                          fill_path_info(&r);                                  CCS_MAX_PATHNAME_LEN - 1);
1055                            ccs_fill_path_info(&rn);
1056                          break;                          break;
1057                  }                  }
1058          }          }
1059            
1060          /* Compare basename of real_program_name and argv[0] */          /* Compare basename of real_program_name and argv[0] */
1061          if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {          r->mode = ccs_check_flags(r->domain, CCS_TOMOYO_MAC_FOR_ARGV0);
1062                  char *org_argv0 = get_argv0(bprm);          if (bprm->argc > 0 && r->mode) {
1063                    char *base_argv0 = tmp->buffer;
1064                    const char *base_filename;
1065                  retval = -ENOMEM;                  retval = -ENOMEM;
1066                  if (org_argv0) {                  if (!get_argv0(bprm, tmp))
1067                          const int len = strlen(org_argv0);                          goto out;
1068                          char *printable_argv0 = ccs_alloc(len * 4 + 8);                  base_filename = strrchr(real_program_name, '/');
1069                          if (printable_argv0 && Escape(printable_argv0, org_argv0, len * 4 + 8) == 0) {                  if (!base_filename)
1070                                  const char *base_argv0, *base_filename;                          base_filename = real_program_name;
1071                                  if ((base_argv0 = strrchr(printable_argv0, '/')) == NULL) base_argv0 = printable_argv0; else base_argv0++;                  else
1072                                  if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;                          base_filename++;
1073                                  if (strcmp(base_argv0, base_filename)) retval = CheckArgv0Perm(&r, base_argv0);                  if (strcmp(base_argv0, base_filename)) {
1074                                  else retval = 0;                          retval = ccs_check_argv0_perm(r, &rn, base_argv0);
1075                            if (retval == 1) {
1076                                    r->retry++;
1077                                    goto retry;
1078                          }                          }
1079                          ccs_free(printable_argv0);                          r->retry = 0;
1080                          ccs_free(org_argv0);                          r->tomoyo_flags = current->tomoyo_flags;
1081                            if (retval < 0)
1082                                    goto out;
1083                  }                  }
                 if (retval) goto out;  
1084          }          }
1085            
1086          /* Check 'aggregator' directive. */          /* Check 'aggregator' directive. */
1087          {          {
1088                  struct aggregator_entry *ptr;                  struct aggregator_entry *ptr;
1089                  /* Is this program allowed to be aggregated? */                  /* Is this program allowed to be aggregated? */
1090                  list1_for_each_entry(ptr, &aggregator_list, list) {                  list1_for_each_entry(ptr, &aggregator_list, list) {
1091                          if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;                          if (ptr->is_deleted ||
1092                                !ccs_path_matches_pattern(&rn, ptr->original_name))
1093                                    continue;
1094                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
1095                          strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);                          strncpy(real_program_name, ptr->aggregated_name->name,
1096                          fill_path_info(&r);                                  CCS_MAX_PATHNAME_LEN - 1);
1097                            ccs_fill_path_info(&rn);
1098                          break;                          break;
1099                  }                  }
1100          }          }
1101    
1102          /* Check execute permission. */          /* Check execute permission. */
1103          if ((retval = CheckExecPerm(&r, filp)) < 0) goto out;          r->mode = mode;
1104            retval = ccs_check_exec_perm(r, &rn);
1105            if (retval == 1) {
1106                    r->retry++;
1107                    goto retry;
1108            }
1109            r->retry = 0;
1110            r->tomoyo_flags = current->tomoyo_flags;
1111            if (retval < 0)
1112                    goto out;
1113    
1114   ok: ;   calculate_domain:
1115          /* Allocate memory for calcurating domain name. */          new_domain_name = tmp->buffer;
1116          retval = -ENOMEM;          if (is_domain_initializer(r->domain->domainname, &rn, &ln)) {
         if ((new_domain_name = ccs_alloc(CCS_MAX_PATHNAME_LEN + 16)) == NULL) goto out;  
           
         if (IsDomainInitializer(old_domain->domainname, &r, &l)) {  
1117                  /* Transit to the child of KERNEL_DOMAIN domain. */                  /* Transit to the child of KERNEL_DOMAIN domain. */
1118                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, ROOT_NAME " " "%s", real_program_name);                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1,
1119          } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {                           ROOT_NAME " " "%s", real_program_name);
1120            } else if (r->domain == &KERNEL_DOMAIN && !sbin_init_started) {
1121                  /*                  /*
1122                   * Needn't to transit from kernel domain before starting /sbin/init .                   * Needn't to transit from kernel domain before starting
1123                   * But transit from kernel domain if executing initializers, for they might start before /sbin/init .                   * /sbin/init. But transit from kernel domain if executing
1124                     * initializers because they might start before /sbin/init.
1125                   */                   */
1126                  domain = old_domain;                  domain = r->domain;
1127          } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {          } else if (is_domain_keeper(r->domain->domainname, &rn, &ln)) {
1128                  /* Keep current domain. */                  /* Keep current domain. */
1129                  domain = old_domain;                  domain = r->domain;
1130          } else {          } else {
1131                  /* Normal domain transition. */                  /* Normal domain transition. */
1132                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1,
1133          }                           "%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);  
                 }  
1134          }          }
1135            if (domain || strlen(new_domain_name) >= CCS_MAX_PATHNAME_LEN)
1136                    goto done;
1137            domain = ccs_find_domain(new_domain_name);
1138            if (domain)
1139                    goto done;
1140            if (is_enforce) {
1141                    int error = ccs_check_supervisor(r,
1142                                                     "# wants to create domain\n"
1143                                                     "%s\n", new_domain_name);
1144                    if (error == 1) {
1145                            r->retry++;
1146                            goto retry;
1147                    }
1148                    r->retry = 0;
1149                    if (error < 0)
1150                            goto done;
1151            }
1152            domain = ccs_find_or_assign_new_domain(new_domain_name, r->profile);
1153            if (domain)
1154                    audit_domain_creation_log(domain);
1155     done:
1156          if (!domain) {          if (!domain) {
1157                  printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);                  printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
1158                  if (is_enforce) retval = -EPERM;                         new_domain_name);
1159                    if (is_enforce)
1160                            retval = -EPERM;
1161                    else {
1162                            retval = 0;
1163                            ccs_set_domain_flag(r->domain, false,
1164                                                DOMAIN_FLAGS_TRANSITION_FAILED);
1165                    }
1166          } else {          } else {
1167                  retval = 0;                  retval = 0;
1168          }          }
1169   out: ;   out:
         ccs_free(new_domain_name);  
1170          ccs_free(real_program_name);          ccs_free(real_program_name);
1171          ccs_free(symlink_program_name);          ccs_free(symlink_program_name);
1172          *next_domain = domain ? domain : old_domain;          if (domain)
1173                    r->domain = domain;
1174          return retval;          return retval;
1175  }  }
1176    
1177  static int CheckEnviron(struct linux_binprm *bprm)  /**
1178     * check_environ - Check permission for environment variable names.
1179     *
1180     * @r: Pointer to "struct ccs_request_info".
1181     *
1182     * Returns 0 on success, negative value otherwise.
1183     */
1184    static int check_environ(struct ccs_request_info *r)
1185  {  {
1186          const u8 profile = current->domain_info->profile;          struct linux_binprm *bprm = r->bprm;
1187          const unsigned int mode = CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV);          struct ccs_page_buffer *tmp = r->obj->tmp;
1188          char *arg_ptr;          char *arg_ptr = tmp->buffer;
1189          int arg_len = 0;          int arg_len = 0;
1190          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
1191          int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;          int i = pos / PAGE_SIZE;
1192            int offset = pos % PAGE_SIZE;
1193          int argv_count = bprm->argc;          int argv_count = bprm->argc;
1194          int envp_count = bprm->envc;          int envp_count = bprm->envc;
1195          //printk("start %d %d\n", argv_count, envp_count);          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
1196          int error = -ENOMEM;          int error = -ENOMEM;
1197          if (!mode || !envp_count) return 0;          if (!r->mode || !envp_count)
1198          arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN);                  return 0;
         if (!arg_ptr) goto out;  
1199          while (error == -ENOMEM) {          while (error == -ENOMEM) {
1200                  struct page *page;                  struct page *page;
1201                  const char *kaddr;                  const char *kaddr;
1202  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1203                  if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;                  if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page,
1204                                       NULL) <= 0)
1205                            goto out;
1206                    pos += PAGE_SIZE - offset;
1207  #else  #else
1208                  page = bprm->page[i];                  page = bprm->page[i];
1209  #endif  #endif
1210                  /* Map */                  /* Map. */
1211                  kaddr = kmap(page);                  kaddr = kmap(page);
1212                  if (!kaddr) { /* Mapping failed. */                  if (!kaddr) { /* Mapping failed. */
1213  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1214                          put_page(page);                          put_page(page);
1215  #endif  #endif
1216                          goto out;                          goto out;
1217                  }                  }
1218                  /* Read. */                  /* Read. */
1219                  while (argv_count && offset < PAGE_SIZE) {                  while (argv_count && offset < PAGE_SIZE) {
1220                          if (!kaddr[offset++]) argv_count--;                          if (!kaddr[offset++])
1221                                    argv_count--;
1222                  }                  }
1223                  if (argv_count) goto unmap_page;                  if (argv_count)
1224                            goto unmap_page;
1225                  while (offset < PAGE_SIZE) {                  while (offset < PAGE_SIZE) {
1226                          const unsigned char c = kaddr[offset++];                          const unsigned char c = kaddr[offset++];
1227                          if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {                          if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
# Line 783  static int CheckEnviron(struct linux_bin Line 1235  static int CheckEnviron(struct linux_bin
1235                                  } else {                                  } else {
1236                                          arg_ptr[arg_len++] = '\\';                                          arg_ptr[arg_len++] = '\\';
1237                                          arg_ptr[arg_len++] = (c >> 6) + '0';                                          arg_ptr[arg_len++] = (c >> 6) + '0';
1238                                          arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';                                          arg_ptr[arg_len++]
1239                                                    = ((c >> 3) & 7) + '0';
1240                                          arg_ptr[arg_len++] = (c & 7) + '0';                                          arg_ptr[arg_len++] = (c & 7) + '0';
1241                                  }                                  }
1242                          } else {                          } else {
1243                                  arg_ptr[arg_len] = '\0';                                  arg_ptr[arg_len] = '\0';
1244                          }                          }
1245                          if (c) continue;                          if (c)
1246                          if (CheckEnvPerm(arg_ptr, profile, mode)) {                                  continue;
1247                            if (ccs_check_env_perm(r, arg_ptr)) {
1248                                  error = -EPERM;                                  error = -EPERM;
1249                                  break;                                  break;
1250                          }                          }
# Line 800  static int CheckEnviron(struct linux_bin Line 1254  static int CheckEnviron(struct linux_bin
1254                          }                          }
1255                          arg_len = 0;                          arg_len = 0;
1256                  }                  }
1257          unmap_page:   unmap_page:
1258                  /* Unmap. */                  /* Unmap. */
1259                  kunmap(page);                  kunmap(page);
1260  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1261                  put_page(page);                  put_page(page);
                 pos += PAGE_SIZE - offset;  
1262  #endif  #endif
1263                  i++;                  i++;
1264                  offset = 0;                  offset = 0;
1265          }          }
1266   out:   out:
1267          ccs_free(arg_ptr);          if (r->mode != 3)
1268          if (error && mode != 3) error = 0;                  error = 0;
1269          return error;          return error;
1270  }  }
1271    
1272  static void UnEscape(unsigned char *dest)  /**
1273     * unescape - Unescape escaped string.
1274     *
1275     * @dest: String to unescape.
1276     *
1277     * Returns nothing.
1278     */
1279    static void unescape(unsigned char *dest)
1280  {  {
1281          unsigned char *src = dest;          unsigned char *src = dest;
1282          unsigned char c, d, e;          unsigned char c;
1283            unsigned char d;
1284            unsigned char e;
1285          while ((c = *src++) != '\0') {          while ((c = *src++) != '\0') {
1286                  if (c != '\\') {                  if (c != '\\') {
1287                          *dest++ = c;                          *dest++ = c;
# Line 828  static void UnEscape(unsigned char *dest Line 1290  static void UnEscape(unsigned char *dest
1290                  c = *src++;                  c = *src++;
1291                  if (c == '\\') {                  if (c == '\\') {
1292                          *dest++ = c;                          *dest++ = c;
1293                  } else if (c >= '0' && c <= '3' &&                          continue;
                            (d = *src++) >= '0' && d <= '7' &&  
                            (e = *src++) >= '0' && e <= '7') {  
                         *dest++ = ((c - '0') << 6) | ((d - '0') << 3) | (e - '0');  
                 } else {  
                         break;  
1294                  }                  }
1295                    if (c < '0' || c > '3')
1296                            break;
1297                    d = *src++;
1298                    if (d < '0' || d > '7')
1299                            break;
1300                    e = *src++;
1301                    if (e < '0' || e > '7')
1302                            break;
1303                    *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
1304          }          }
1305          *dest = '\0';          *dest = '\0';
1306  }  }
1307    
1308  static int try_alt_exec(struct linux_binprm *bprm, char **alt_exec0)  /**
1309     * root_depth - Get number of directories to strip.
1310     *
1311     * @dentry: Pointer to "struct dentry".
1312     * @vfsmnt: Pointer to "struct vfsmount".
1313     *
1314     * Returns number of directories to strip.
1315     */
1316    static inline int root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)
1317    {
1318            int depth = 0;
1319            /***** CRITICAL SECTION START *****/
1320            ccs_realpath_lock();
1321            for (;;) {
1322                    if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
1323                            /* Global root? */
1324                            if (vfsmnt->mnt_parent == vfsmnt)
1325                                    break;
1326                            dentry = vfsmnt->mnt_mountpoint;
1327                            vfsmnt = vfsmnt->mnt_parent;
1328                            continue;
1329                    }
1330                    dentry = dentry->d_parent;
1331                    depth++;
1332            }
1333            ccs_realpath_unlock();
1334            /***** CRITICAL SECTION END *****/
1335            return depth;
1336    }
1337    
1338    /**
1339     * get_root_depth - return the depth of root directory.
1340     *
1341     * Returns number of directories to strip.
1342     */
1343    static int get_root_depth(void)
1344  {  {
1345            int depth;
1346            struct dentry *dentry;
1347            struct vfsmount *vfsmnt;
1348    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
1349            struct path root;
1350    #endif
1351            /***** CRITICAL SECTION START *****/
1352            read_lock(&current->fs->lock);
1353    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
1354            root = current->fs->root;
1355            path_get(&current->fs->root);
1356            dentry = root.dentry;
1357            vfsmnt = root.mnt;
1358    #else
1359            dentry = dget(current->fs->root);
1360            vfsmnt = mntget(current->fs->rootmnt);
1361    #endif
1362            read_unlock(&current->fs->lock);
1363            /***** CRITICAL SECTION END *****/
1364            depth = root_depth(dentry, vfsmnt);
1365    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
1366            path_put(&root);
1367    #else
1368            dput(dentry);
1369            mntput(vfsmnt);
1370    #endif
1371            return depth;
1372    }
1373    
1374    /**
1375     * try_alt_exec - Try to start execute handler.
1376     *
1377     * @r:           Pointer to "struct ccs_request_info".
1378     * @handler:     Pointer to the name of execute handler.
1379     * @eh_path:     Pointer to pointer to the name of execute handler.
1380     *
1381     * Returns 0 on success, negative value otherwise.
1382     */
1383    static int try_alt_exec(struct ccs_request_info *r,
1384                            const struct path_info *handler, char **eh_path)
1385    {
1386            /*
1387             * Contents of modified bprm.
1388             * The envp[] in original bprm is moved to argv[] so that
1389             * the alternatively executed program won't be affected by
1390             * some dangerous environment variables like LD_PRELOAD.
1391             *
1392             * modified bprm->argc
1393             *    = original bprm->argc + original bprm->envc + 7
1394             * modified bprm->envc
1395             *    = 0
1396             *
1397             * modified bprm->argv[0]
1398             *    = the program's name specified by execute_handler
1399             * modified bprm->argv[1]
1400             *    = current->domain_info->domainname->name
1401             * modified bprm->argv[2]
1402             *    = the current process's name
1403             * modified bprm->argv[3]
1404             *    = the current process's information (e.g. uid/gid).
1405             * modified bprm->argv[4]
1406             *    = original bprm->filename
1407             * modified bprm->argv[5]
1408             *    = original bprm->argc in string expression
1409             * modified bprm->argv[6]
1410             *    = original bprm->envc in string expression
1411             * modified bprm->argv[7]
1412             *    = original bprm->argv[0]
1413             *  ...
1414             * modified bprm->argv[bprm->argc + 6]
1415             *     = original bprm->argv[bprm->argc - 1]
1416             * modified bprm->argv[bprm->argc + 7]
1417             *     = original bprm->envp[0]
1418             *  ...
1419             * modified bprm->argv[bprm->envc + bprm->argc + 6]
1420             *     = original bprm->envp[bprm->envc - 1]
1421             */
1422            struct linux_binprm *bprm = r->bprm;
1423          struct file *filp;          struct file *filp;
1424          int retval;          int retval;
1425          /* domainname must not be modified. */          const int original_argc = bprm->argc;
1426          char *domainname = (char *) current->domain_info->domainname->name;          const int original_envc = bprm->envc;
1427          char *alt_exec;          struct task_struct *task = current;
1428          const char *alt_exec1 = GetAltExec();          char *buffer = r->obj->tmp->buffer;
1429          if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL;          /* Allocate memory for execute handler's pathname. */
1430          retval = strlen(alt_exec1) + 1;          char *execute_handler = ccs_alloc(sizeof(struct ccs_page_buffer));
1431          *alt_exec0 = alt_exec = ccs_alloc(retval);          *eh_path = execute_handler;
1432          if (!alt_exec) return -ENOMEM;          if (!execute_handler)
1433          memmove(alt_exec, alt_exec1, retval);                  return -ENOMEM;
1434          UnEscape(alt_exec);          strncpy(execute_handler, handler->name,
1435                    sizeof(struct ccs_page_buffer) - 1);
1436            unescape(execute_handler);
1437    
1438            /* Close the requested program's dentry. */
1439          allow_write_access(bprm->file);          allow_write_access(bprm->file);
1440          fput(bprm->file);          fput(bprm->file);
1441          bprm->file = NULL;          bprm->file = NULL;
1442  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)  
1443          retval = remove_arg_zero(bprm);          { /* Adjust root directory for open_exec(). */
1444          if (retval) return retval;                  int depth = get_root_depth();
1445  #else                  char *cp = execute_handler;
1446          remove_arg_zero(bprm);                  if (!*cp || *cp != '/')
1447                            return -ENOENT;
1448                    while (depth) {
1449                            cp = strchr(cp + 1, '/');
1450                            if (!cp)
1451                                    return -ENOENT;
1452                            depth--;
1453                    }
1454                    memmove(execute_handler, cp, strlen(cp) + 1);
1455            }
1456    
1457            /* Move envp[] to argv[] */
1458            bprm->argc += bprm->envc;
1459            bprm->envc = 0;
1460    
1461            /* Set argv[6] */
1462            {
1463                    snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d",
1464                             original_envc);
1465                    retval = copy_strings_kernel(1, &buffer, bprm);
1466                    if (retval < 0)
1467                            goto out;
1468                    bprm->argc++;
1469            }
1470    
1471            /* Set argv[5] */
1472            {
1473                    snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d",
1474                             original_argc);
1475                    retval = copy_strings_kernel(1, &buffer, bprm);
1476                    if (retval < 0)
1477                            goto out;
1478                    bprm->argc++;
1479            }
1480    
1481            /* Set argv[4] */
1482            {
1483                    retval = copy_strings_kernel(1, &bprm->filename, bprm);
1484                    if (retval < 0)
1485                            goto out;
1486                    bprm->argc++;
1487            }
1488    
1489            /* Set argv[3] */
1490            {
1491                    const u32 tomoyo_flags = task->tomoyo_flags;
1492                    snprintf(buffer, sizeof(struct ccs_page_buffer) - 1,
1493                             "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
1494                             "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
1495                             "state[1]=%u state[2]=%u",
1496                             task->pid, task->uid, task->gid, task->euid,
1497                             task->egid, task->suid, task->sgid, task->fsuid,
1498                             task->fsgid, (u8) (tomoyo_flags >> 24),
1499                             (u8) (tomoyo_flags >> 16), (u8) (tomoyo_flags >> 8));
1500                    retval = copy_strings_kernel(1, &buffer, bprm);
1501                    if (retval < 0)
1502                            goto out;
1503                    bprm->argc++;
1504            }
1505    
1506            /* Set argv[2] */
1507            {
1508                    char *exe = (char *) ccs_get_exe();
1509                    if (exe) {
1510                            retval = copy_strings_kernel(1, &exe, bprm);
1511                            ccs_free(exe);
1512                    } else {
1513                            snprintf(buffer, sizeof(struct ccs_page_buffer) - 1,
1514                                     "<unknown>");
1515                            retval = copy_strings_kernel(1, &buffer, bprm);
1516                    }
1517                    if (retval < 0)
1518                            goto out;
1519                    bprm->argc++;
1520            }
1521    
1522            /* Set argv[1] */
1523            {
1524                    strncpy(buffer, task->domain_info->domainname->name,
1525                            sizeof(struct ccs_page_buffer) - 1);
1526                    retval = copy_strings_kernel(1, &buffer, bprm);
1527                    if (retval < 0)
1528                            goto out;
1529                    bprm->argc++;
1530            }
1531    
1532            /* Set argv[0] */
1533            {
1534                    retval = copy_strings_kernel(1, &execute_handler, bprm);
1535                    if (retval < 0)
1536                            goto out;
1537                    bprm->argc++;
1538            }
1539    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1540    #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
1541            bprm->argv_len = bprm->exec - bprm->p;
1542  #endif  #endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)  
         retval = copy_strings_kernel(1, &bprm->interp, bprm);  
 #else  
         retval = copy_strings_kernel(1, &bprm->filename, bprm);  
1543  #endif  #endif
         if (retval < 0) return retval;  
         bprm->argc++;  
         retval = copy_strings_kernel(1, &domainname, bprm);  
         if (retval < 0) return retval;  
         bprm->argc++;  
         retval = copy_strings_kernel(1, &alt_exec, bprm);  
         if (retval < 0) return retval;  
         bprm->argc++;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)  
         bprm->interp = alt_exec;  
 #endif  
         filp = open_exec(alt_exec);  
         if (IS_ERR(filp)) return PTR_ERR(filp);  
         bprm->file= filp;  
         bprm->filename = alt_exec;  
         return prepare_binprm(bprm);  
 }  
1544    
1545            /* OK, now restart the process with execute handler program's dentry. */
1546            filp = open_exec(execute_handler);
1547            if (IS_ERR(filp)) {
1548                    retval = PTR_ERR(filp);
1549                    goto out;
1550            }
1551            bprm->file = filp;
1552            bprm->filename = execute_handler;
1553    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
1554            bprm->interp = execute_handler;
1555  #endif  #endif
1556            retval = prepare_binprm(bprm);
1557            if (retval < 0)
1558                    goto out;
1559            task->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1560            retval = find_next_domain(r, handler);
1561            task->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1562     out:
1563            return retval;
1564    }
1565    
1566  int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)  /**
1567  {   * find_execute_handler - Find an execute handler.
1568          struct domain_info *next_domain = NULL, *prev_domain = current->domain_info;   *
1569          int retval;   * @type: Type of execute handler.
1570          char *alt_exec = NULL; /* Keep valid until search_binary_handler() finishes. */   *
1571  #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)   * Returns pointer to "struct path_info" if found, NULL otherwise.
1572          extern void CCS_LoadPolicy(const char *filename);   */
1573          CCS_LoadPolicy(bprm->filename);  static const struct path_info *find_execute_handler(const u8 type)
1574  #endif  {
1575  #if defined(CONFIG_TOMOYO)          struct task_struct *task = current;
1576          retval = FindNextDomain(bprm, &next_domain, 1);          const struct domain_info *domain = task->domain_info;
1577          if (retval == -EPERM && try_alt_exec(bprm, &alt_exec) >= 0) {          struct acl_info *ptr;
1578                  current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;          /*
1579                  retval = FindNextDomain(bprm, &next_domain, 0);           * Don't use execute handler if the current process is
1580                  current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;           * marked as execute handler to avoid infinite execute handler loop.
1581          }           */
1582          if (retval == 0) {          if (task->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER)
1583                  current->domain_info = next_domain;                  return NULL;
1584                  retval = CheckEnviron(bprm);          list1_for_each_entry(ptr, &domain->acl_info_list, list) {
1585                  current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;                  struct execute_handler_record *acl;
1586                  if (!retval) retval = search_binary_handler(bprm, regs);                  if (ptr->type != type)
1587                  current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;                          continue;
1588                  if (retval < 0) current->domain_info = prev_domain;                  acl = container_of(ptr, struct execute_handler_record, head);
1589                    return acl->handler;
1590          }          }
1591          ccs_free(alt_exec);          return NULL;
1592    }
1593    
1594    /* List of next_domain which is used for checking interpreter's permissions. */
1595    struct execve_entry {
1596            struct list_head list;
1597            struct task_struct *task;
1598            struct domain_info *next_domain;
1599    };
1600    
1601    static LIST_HEAD(execve_list);
1602    static DEFINE_SPINLOCK(execve_list_lock);
1603    
1604    /**
1605     * ccs_register_next_domain - Remember next_domain.
1606     *
1607     * @next_domain: Pointer to "struct domain_info".
1608     *
1609     * Returns 0 on success, -ENOMEM otherwise.
1610     */
1611    static int ccs_register_next_domain(struct domain_info *next_domain)
1612    {
1613            struct execve_entry *ee = kmalloc(sizeof(*ee), GFP_KERNEL);
1614            if (!ee)
1615                    return -ENOMEM;
1616            ee->task = current;
1617            ee->next_domain = next_domain;
1618            /***** CRITICAL SECTION START *****/
1619            spin_lock(&execve_list_lock);
1620            list_add(&ee->list, &execve_list);
1621            spin_unlock(&execve_list_lock);
1622            /***** CRITICAL SECTION END *****/
1623            return 0;
1624    }
1625    
1626    /**
1627     * ccs_fetch_next_domain - Fetch next_domain from the list.
1628     *
1629     * Returns pointer to "struct domain_info" which will be used if execve()
1630     * succeeds. This function does not return NULL.
1631     */
1632    struct domain_info *ccs_fetch_next_domain(void)
1633    {
1634            struct task_struct *task = current;
1635            struct domain_info *next_domain = task->domain_info;
1636            struct execve_entry *p;
1637            /***** CRITICAL SECTION START *****/
1638            spin_lock(&execve_list_lock);
1639            list_for_each_entry(p, &execve_list, list) {
1640                    if (p->task != task)
1641                            continue;
1642                    next_domain = p->next_domain;
1643                    break;
1644            }
1645            spin_unlock(&execve_list_lock);
1646            /***** CRITICAL SECTION END *****/
1647            return next_domain;
1648    }
1649    
1650    /**
1651     * ccs_unregister_next_domain - Forget next_domain.
1652     */
1653    static void ccs_unregister_next_domain(void)
1654    {
1655            struct task_struct *task = current;
1656            struct execve_entry *p;
1657            struct execve_entry *ee = NULL;
1658            /***** CRITICAL SECTION START *****/
1659            spin_lock(&execve_list_lock);
1660            list_for_each_entry(p, &execve_list, list) {
1661                    if (p->task != task)
1662                            continue;
1663                    list_del(&p->list);
1664                    ee = p;
1665                    break;
1666            }
1667            spin_unlock(&execve_list_lock);
1668            /***** CRITICAL SECTION END *****/
1669            kfree(ee);
1670    }
1671    
1672    /**
1673     * search_binary_handler_with_transition - Perform domain transition.
1674     *
1675     * @bprm: Pointer to "struct linux_binprm".
1676     * @regs: Pointer to "struct pt_regs".
1677     *
1678     * Returns result of search_binary_handler() on success,
1679     * negative value otherwise.
1680     */
1681    int search_binary_handler_with_transition(struct linux_binprm *bprm,
1682                                              struct pt_regs *regs)
1683    {
1684            int retval;
1685            struct task_struct *task = current;
1686            const struct path_info *handler;
1687            struct ccs_request_info r;
1688            struct obj_info obj;
1689            /*
1690             * "eh_path" holds path to execute handler program.
1691             * Thus, keep valid until search_binary_handler() finishes.
1692             */
1693            char *eh_path = NULL;
1694            struct ccs_page_buffer *tmp = ccs_alloc(sizeof(struct ccs_page_buffer));
1695            memset(&obj, 0, sizeof(obj));
1696            if (!sbin_init_started)
1697                    ccs_load_policy(bprm->filename);
1698            if (!tmp)
1699                    return -ENOMEM;
1700    
1701            ccs_init_request_info(&r, NULL, CCS_TOMOYO_MAC_FOR_FILE);
1702            r.bprm = bprm;
1703            r.obj = &obj;
1704            obj.path1_dentry = bprm->file->f_dentry;
1705            obj.path1_vfsmnt = bprm->file->f_vfsmnt;
1706            obj.tmp = tmp;
1707    
1708            /* Clear manager flag. */
1709            task->tomoyo_flags &= ~CCS_TASK_IS_POLICY_MANAGER;
1710            handler = find_execute_handler(TYPE_EXECUTE_HANDLER);
1711            if (handler) {
1712                    retval = try_alt_exec(&r, handler, &eh_path);
1713                    if (!retval)
1714                            audit_execute_handler_log(true, handler->name, bprm);
1715                    goto ok;
1716            }
1717            retval = find_next_domain(&r, NULL);
1718            if (retval != -EPERM)
1719                    goto ok;
1720            handler = find_execute_handler(TYPE_DENIED_EXECUTE_HANDLER);
1721            if (handler) {
1722                    retval = try_alt_exec(&r, handler, &eh_path);
1723                    if (!retval)
1724                            audit_execute_handler_log(false, handler->name, bprm);
1725            }
1726     ok:
1727            if (retval < 0)
1728                    goto out;
1729            r.mode = ccs_check_flags(r.domain, CCS_TOMOYO_MAC_FOR_ENV);
1730            retval = check_environ(&r);
1731            if (retval < 0)
1732                    goto out;
1733            retval = ccs_register_next_domain(r.domain);
1734            if (retval < 0)
1735                    goto out;
1736            task->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1737            retval = search_binary_handler(bprm, regs);
1738            task->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1739            if (retval < 0)
1740                    goto out;
1741            /* Proceed to next domain if execution suceeded. */
1742            task->domain_info = r.domain;
1743            mb(); /* Make domain transition visible to other CPUs. */
1744            /* Mark the current process as execute handler. */
1745            if (handler)
1746                    task->tomoyo_flags |= TOMOYO_TASK_IS_EXECUTE_HANDLER;
1747            /* Mark the current process as normal process. */
1748            else
1749                    task->tomoyo_flags &= ~TOMOYO_TASK_IS_EXECUTE_HANDLER;
1750     out:
1751            ccs_unregister_next_domain();
1752            ccs_free(eh_path);
1753            ccs_free(tmp);
1754          return retval;          return retval;
1755    }
1756    
1757  #else  #else
1758          return search_binary_handler(bprm, regs);  
1759    /**
1760     * search_binary_handler_with_transition - Wrapper for search_binary_handler().
1761     *
1762     * @bprm: Pointer to "struct linux_binprm".
1763     * @regs: Pointer to "struct pt_regs".
1764     *
1765     * Returns the result of search_binary_handler().
1766     */
1767    int search_binary_handler_with_transition(struct linux_binprm *bprm,
1768                                              struct pt_regs *regs)
1769    {
1770    #ifdef CONFIG_SAKURA
1771            /* Clear manager flag. */
1772            current->tomoyo_flags &= ~CCS_TASK_IS_POLICY_MANAGER;
1773            ccs_load_policy(bprm->filename);
1774  #endif  #endif
1775            return search_binary_handler(bprm, regs);
1776  }  }
1777    
1778  /***** TOMOYO Linux end. *****/  #endif

Legend:
Removed from v.816  
changed lines
  Added in v.1695

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