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

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

Legend:
Removed from v.776  
changed lines
  Added in v.2691

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