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

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

Legend:
Removed from v.588  
changed lines
  Added in v.2718

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