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

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

Legend:
Removed from v.851  
changed lines
  Added in v.2703

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