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

Subversion リポジトリの参照

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

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

trunk/1.5.x/ccs-patch/fs/tomoyo_domain.c revision 851 by kumaneko, Wed Jan 2 06:29:56 2008 UTC branches/ccs-patch/security/ccsecurity/domain.c revision 3781 by kumaneko, Fri Jun 25 05:03:02 2010 UTC
# Line 1  Line 1 
1  /*  /*
2   * fs/tomoyo_domain.c   * security/ccsecurity/domain.c
3   *   *
4   * Implementation of the Domain-Based Mandatory Access Control.   * Copyright (C) 2005-2010  NTT DATA CORPORATION
5   *   *
6   * Copyright (C) 2005-2008  NTT DATA CORPORATION   * Version: 1.7.2+   2010/06/04
  *  
  * Version: 1.5.3-pre   2008/01/02  
7   *   *
8   * 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.
9   * See README.ccs for ChangeLog.   * See README.ccs for ChangeLog.
10   *   *
11   */   */
 /***** TOMOYO Linux start. *****/  
12    
13  #include <linux/ccs_common.h>  #include <linux/slab.h>
 #include <linux/tomoyo.h>  
 #include <linux/realpath.h>  
14  #include <linux/highmem.h>  #include <linux/highmem.h>
15  #include <linux/binfmts.h>  #include <linux/version.h>
16    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
17  #ifndef for_each_process  #include <linux/namei.h>
18  #define for_each_process for_each_task  #include <linux/mount.h>
19  #endif  #endif
20    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21    #include <linux/fs_struct.h>
22    #endif
23    #include "internal.h"
24    
25  /*************************  VARIABLES  *************************/  /* Variables definitions.*/
   
 /* The initial domain. */  
 struct domain_info KERNEL_DOMAIN;  
   
 /* List of domains. */  
 LIST1_HEAD(domain_list);  
   
 /* /sbin/init started? */  
 extern int sbin_init_started;  
   
 #ifdef CONFIG_TOMOYO  
   
 /* Lock for appending domain's ACL. */  
 DEFINE_MUTEX(domain_acl_lock);  
   
 /*************************  UTILITY FUNCTIONS  *************************/  
   
 /***** The structure for program files to force domain reconstruction. *****/  
   
 struct domain_initializer_entry {  
         struct list1_head list;  
         const struct path_info *domainname;    /* This may be NULL */  
         const struct path_info *program;  
         bool is_deleted;  
         bool is_not;  
         bool is_last_name;  
 };  
   
 /***** The structure for domains to not to transit domains. *****/  
   
 struct domain_keeper_entry {  
         struct list1_head list;  
         const struct path_info *domainname;  
         const struct path_info *program;       /* This may be NULL */  
         bool is_deleted;  
         bool is_not;  
         bool is_last_name;  
 };  
   
 /***** The structure for program files that should be aggregated. *****/  
   
 struct aggregator_entry {  
         struct list1_head list;  
         const struct path_info *original_name;  
         const struct path_info *aggregated_name;  
         bool is_deleted;  
 };  
   
 /***** 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;  
 };  
26    
27  /*************************  VARIABLES  *************************/  /* The global domain. */
28    struct ccs_domain_info ccs_global_domain;
29    
30  /* Domain creation lock. */  /* The initial domain. */
31  static DEFINE_MUTEX(new_domain_assign_lock);  struct ccs_domain_info ccs_kernel_domain;
32    
33  /*************************  UTILITY FUNCTIONS  *************************/  /* The list for "struct ccs_domain_info". */
34    LIST_HEAD(ccs_domain_list);
35    
36  const char *GetLastName(const struct domain_info *domain)  struct list_head ccs_policy_list[CCS_MAX_POLICY];
37  {  struct list_head ccs_group_list[CCS_MAX_GROUP];
38          const char *cp0 = domain->domainname->name, *cp1;  struct list_head ccs_shared_list[CCS_MAX_LIST];
         if ((cp1 = strrchr(cp0, ' ')) != NULL) return cp1 + 1;  
         return cp0;  
 }  
39    
40  int AddDomainACL(struct domain_info *domain, struct acl_info *acl)  /**
41     * ccs_audit_execute_handler_log - Audit execute_handler log.
42     *
43     * @ee:         Pointer to "struct ccs_execve".
44     *
45     * Returns 0 on success, negative value otherwise.
46     */
47    static int ccs_audit_execute_handler_log(struct ccs_execve *ee)
48  {  {
49          list1_add_tail_mb(&acl->list, &domain->acl_info_list);          struct ccs_request_info *r = &ee->r;
50          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);          const char *handler = ee->handler->name;
51          return 0;          r->type = CCS_MAC_FILE_EXECUTE;
52            r->mode = ccs_get_mode(r->profile, CCS_MAC_FILE_EXECUTE);
53            r->granted = true;
54            return ccs_write_log(r, "%s %s\n", ee->handler_type ==
55                                 CCS_TYPE_DENIED_EXECUTE_HANDLER ?
56                                 CCS_KEYWORD_DENIED_EXECUTE_HANDLER :
57                                 CCS_KEYWORD_EXECUTE_HANDLER, handler);
58  }  }
59    
60  int DelDomainACL(struct acl_info *ptr)  /**
61     * ccs_audit_domain_creation_log - Audit domain creation log.
62     *
63     * Returns 0 on success, negative value otherwise.
64     */
65    static int ccs_audit_domain_creation_log(void)
66  {  {
67          ptr->is_deleted = 1;          struct ccs_request_info r;
68          UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);          ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE);
69          return 0;          r.granted = false;
70            return ccs_write_log(&r, "use_profile %u\n", r.profile);
71  }  }
72    
73  /*************************  DOMAIN INITIALIZER HANDLER  *************************/  /**
74     * ccs_update_policy - Update an entry for exception policy.
75  static LIST1_HEAD(domain_initializer_list);   *
76     * @new_entry:       Pointer to "struct ccs_acl_info".
77  static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)   * @size:            Size of @new_entry in bytes.
78  {   * @is_delete:       True if it is a delete request.
79          struct domain_initializer_entry *new_entry, *ptr;   * @list:            Pointer to "struct list_head".
80          static DEFINE_MUTEX(lock);   * @check_duplicate: Callback function to find duplicated entry.
81          const struct path_info *saved_program, *saved_domainname = NULL;   *
82          int error = -ENOMEM;   * Returns 0 on success, negative value otherwise.
83          bool is_last_name = 0;   *
84          if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */   * Caller holds ccs_read_lock().
85          if (domainname) {   */
86                  if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {  int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
87                          is_last_name = 1;                        bool is_delete, struct list_head *list,
88                  } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {                        bool (*check_duplicate) (const struct ccs_acl_head *,
89                          return -EINVAL;                                                 const struct ccs_acl_head *))
90                  }  {
91                  if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
92            struct ccs_acl_head *entry;
93            if (mutex_lock_interruptible(&ccs_policy_lock))
94                    return -ENOMEM;
95            list_for_each_entry_rcu(entry, list, list) {
96                    if (!check_duplicate(entry, new_entry))
97                            continue;
98                    entry->is_deleted = is_delete;
99                    error = 0;
100                    break;
101          }          }
102          if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;          if (error && !is_delete) {
103          mutex_lock(&lock);                  entry = ccs_commit_ok(new_entry, size);
104          list1_for_each_entry(ptr, &domain_initializer_list, list) {                  if (entry) {
105                  if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {                          list_add_tail_rcu(&entry->list, list);
                         ptr->is_deleted = is_delete;  
106                          error = 0;                          error = 0;
                         goto out;  
107                  }                  }
108          }          }
109          if (is_delete) {          mutex_unlock(&ccs_policy_lock);
                 error = -ENOENT;  
                 goto out;  
         }  
         if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;  
         new_entry->domainname = saved_domainname;  
         new_entry->program = saved_program;  
         new_entry->is_not = is_not;  
         new_entry->is_last_name = is_last_name;  
         list1_add_tail_mb(&new_entry->list, &domain_initializer_list);  
         error = 0;  
  out:  
         mutex_unlock(&lock);  
110          return error;          return error;
111  }  }
112    
113  int ReadDomainInitializerPolicy(struct io_buffer *head)  static void ccs_delete_type(struct ccs_domain_info *domain, u8 type)
114  {  {
115          struct list1_head *pos;          struct ccs_acl_info *ptr;
116          list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {          list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
117                  struct domain_initializer_entry *ptr;                  if (ptr->type == type)
118                  ptr = list1_entry(pos, struct domain_initializer_entry, list);                          ptr->is_deleted = true;
                 if (ptr->is_deleted) continue;  
                 if (ptr->domainname) {  
                         if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;  
                 } else {  
                         if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) return -ENOMEM;  
                 }  
119          }          }
         return 0;  
120  }  }
121    
122  int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete)  /**
123  {   * ccs_update_domain - Update an entry for domain policy.
124          char *cp = strstr(data, " from ");   *
125          if (cp) {   * @new_entry:       Pointer to "struct ccs_acl_info".
126                  *cp = '\0';   * @size:            Size of @new_entry in bytes.
127                  return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete);   * @is_delete:       True if it is a delete request.
128          } else {   * @domain:          Pointer to "struct ccs_domain_info".
129                  return AddDomainInitializerEntry(NULL, data, is_not, is_delete);   * @check_duplicate: Callback function to find duplicated entry.
130     * @merge_duplicate: Callback function to merge duplicated entry.
131     *
132     * Returns 0 on success, negative value otherwise.
133     *
134     * Caller holds ccs_read_lock().
135     */
136    int ccs_update_domain(struct ccs_acl_info *new_entry, const int size,
137                          bool is_delete, struct ccs_domain_info *domain,
138                          bool (*check_duplicate) (const struct ccs_acl_info *,
139                                                   const struct ccs_acl_info *),
140                          bool (*merge_duplicate) (struct ccs_acl_info *,
141                                                   struct ccs_acl_info *,
142                                                   const bool))
143    {
144            int error = is_delete ? -ENOENT : -ENOMEM;
145            struct ccs_acl_info *entry;
146            /*
147             * Only one "execute_handler" and "denied_execute_handler" can exist
148             * in a domain.
149             */
150            const u8 type = new_entry->type;
151            const bool exclusive = !is_delete &&
152                    (type == CCS_TYPE_EXECUTE_HANDLER ||
153                     type == CCS_TYPE_DENIED_EXECUTE_HANDLER);
154            if (mutex_lock_interruptible(&ccs_policy_lock))
155                    return error;
156            list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
157                    if (!check_duplicate(entry, new_entry))
158                            continue;
159                    if (exclusive)
160                            ccs_delete_type(domain, type);
161                    if (merge_duplicate)
162                            entry->is_deleted = merge_duplicate(entry, new_entry,
163                                                                is_delete);
164                    else
165                            entry->is_deleted = is_delete;
166                    error = 0;
167                    break;
168          }          }
169  }          if (error && !is_delete) {
170                    entry = ccs_commit_ok(new_entry, size);
171  static bool IsDomainInitializer(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)                  if (entry) {
172  {                          if (exclusive)
173          struct domain_initializer_entry *ptr;                                  ccs_delete_type(domain, type);
174          bool flag = 0;                          if (entry->cond)
175          list1_for_each_entry(ptr,  &domain_initializer_list, list) {                                  atomic_inc(&entry->cond->head.users);
176                  if (ptr->is_deleted) continue;                          list_add_tail_rcu(&entry->list,
177                  if (ptr->domainname) {                                            &domain->acl_info_list);
178                          if (!ptr->is_last_name) {                          error = 0;
                                 if (ptr->domainname != domainname) continue;  
                         } else {  
                                 if (pathcmp(ptr->domainname, last_name)) continue;  
                         }  
179                  }                  }
                 if (pathcmp(ptr->program, program)) continue;  
                 if (ptr->is_not) return 0;  
                 flag = 1;  
180          }          }
181          return flag;          mutex_unlock(&ccs_policy_lock);
182            return error;
183  }  }
184    
185  /*************************  DOMAIN KEEPER HANDLER  *************************/  void ccs_check_acl(struct ccs_request_info *r,
186                       bool (*check_entry) (const struct ccs_request_info *,
187  static LIST1_HEAD(domain_keeper_list);                                          const struct ccs_acl_info *))
188    {
189            const struct ccs_domain_info *domain = ccs_current_domain();
190            struct ccs_acl_info *ptr;
191     retry:
192            list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
193                    if (ptr->is_deleted || ptr->type != r->param_type)
194                            continue;
195                    if (check_entry(r, ptr) && ccs_condition(r, ptr->cond)) {
196                            r->cond = ptr->cond;
197                            r->granted = true;
198                            return;
199                    }
200            }
201            if (domain != &ccs_global_domain &&
202                !domain->flags[CCS_DIF_IGNORE_GLOBAL] &&
203                (r->param_type != CCS_TYPE_PATH_ACL ||
204                 r->param.path.operation != CCS_TYPE_READ ||
205                 !domain->flags[CCS_DIF_IGNORE_GLOBAL_ALLOW_READ]) &&
206                (r->param_type != CCS_TYPE_ENV_ACL ||
207                 !domain->flags[CCS_DIF_IGNORE_GLOBAL_ALLOW_ENV])) {
208                    domain = &ccs_global_domain;
209                    goto retry;
210            }
211            r->granted = false;
212    }
213    
214    static bool ccs_same_transition_control(const struct ccs_acl_head *a,
215                                            const struct ccs_acl_head *b)
216    {
217            const struct ccs_transition_control *p1 = container_of(a, typeof(*p1),
218                                                                   head);
219            const struct ccs_transition_control *p2 = container_of(b, typeof(*p2),
220                                                                   head);
221            return p1->type == p2->type && p1->is_last_name == p2->is_last_name
222                    && p1->domainname == p2->domainname
223                    && p1->program == p2->program;
224    }
225    
226  static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)  /**
227  {   * ccs_update_transition_control_entry - Update "struct ccs_transition_control" list.
228          struct domain_keeper_entry *new_entry, *ptr;   *
229          const struct path_info *saved_domainname, *saved_program = NULL;   * @domainname: The name of domain. Maybe NULL.
230          static DEFINE_MUTEX(lock);   * @program:    The name of program. Maybe NULL.
231          int error = -ENOMEM;   * @type:       Type of transition.
232          bool is_last_name = 0;   * @is_delete:  True if it is a delete request.
233          if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {   *
234                  is_last_name = 1;   * Returns 0 on success, negative value otherwise.
235          } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {   */
236                  return -EINVAL;  static int ccs_update_transition_control_entry(const char *domainname,
237          }                                                 const char *program,
238          if (program) {                                                 const u8 type,
239                  if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;                                                 const bool is_delete)
240                  if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;  {
241          }          struct ccs_transition_control e = { .type = type };
242          if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
243          mutex_lock(&lock);          if (program && strcmp(program, "any")) {
244          list1_for_each_entry(ptr, &domain_keeper_list, list) {                  if (!ccs_correct_path(program))
245                  if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {                          return -EINVAL;
246                          ptr->is_deleted = is_delete;                  e.program = ccs_get_name(program);
247                          error = 0;                  if (!e.program)
248                          goto out;                          goto out;
                 }  
249          }          }
250          if (is_delete) {          if (domainname && strcmp(domainname, "any")) {
251                  error = -ENOENT;                  if (!ccs_correct_domain(domainname)) {
252                  goto out;                          if (!ccs_correct_path(domainname))
253                                    goto out;
254                            e.is_last_name = true;
255                    }
256                    e.domainname = ccs_get_name(domainname);
257                    if (!e.domainname)
258                            goto out;
259          }          }
260          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
261          new_entry->domainname = saved_domainname;                                    &ccs_policy_list[CCS_ID_TRANSITION_CONTROL],
262          new_entry->program = saved_program;                                    ccs_same_transition_control);
         new_entry->is_not = is_not;  
         new_entry->is_last_name = is_last_name;  
         list1_add_tail_mb(&new_entry->list, &domain_keeper_list);  
         error = 0;  
263   out:   out:
264          mutex_unlock(&lock);          ccs_put_name(e.domainname);
265            ccs_put_name(e.program);
266          return error;          return error;
267  }  }
268    
269  int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete)  /**
270     * ccs_write_transition_control - Write "struct ccs_transition_control" list.
271     *
272     * @data:      String to parse.
273     * @is_delete: True if it is a delete request.
274     * @type:      Type of this entry.
275     *
276     * Returns 0 on success, negative value otherwise.
277     */
278    int ccs_write_transition_control(char *data, const bool is_delete,
279                                     const u8 type)
280  {  {
281          char *cp = strstr(data, " from ");          char *domainname = strstr(data, " from ");
282          if (cp) {          if (domainname) {
283                  *cp = '\0';                  *domainname = '\0';
284                  return AddDomainKeeperEntry(cp + 6, data, is_not, is_delete);                  domainname += 6;
285          } else {          } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP ||
286                  return AddDomainKeeperEntry(data, NULL, is_not, is_delete);                     type == CCS_TRANSITION_CONTROL_KEEP) {
287                    domainname = data;
288                    data = NULL;
289          }          }
290            return ccs_update_transition_control_entry(domainname, data, type,
291                                                       is_delete);
292  }  }
293    
294  int ReadDomainKeeperPolicy(struct io_buffer *head)  /**
295     * ccs_transition_type - Get domain transition type.
296     *
297     * @domainname: The name of domain.
298     * @program:    The name of program.
299     *
300     * Returns CCS_TRANSITION_CONTROL_INITIALIZE if executing @program
301     * reinitializes domain transition, CCS_TRANSITION_CONTROL_KEEP if executing
302     * @program suppresses domain transition, others otherwise.
303     *
304     * Caller holds ccs_read_lock().
305     */
306    static u8 ccs_transition_type(const struct ccs_path_info *domainname,
307                                  const struct ccs_path_info *program)
308  {  {
309          struct list1_head *pos;          const struct ccs_transition_control *ptr;
310          list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {          const char *last_name = ccs_last_word(domainname->name);
311                  struct domain_keeper_entry *ptr;          u8 type;
312                  ptr = list1_entry(pos, struct domain_keeper_entry, list);          for (type = 0; type < CCS_MAX_TRANSITION_TYPE; type++) {
313                  if (ptr->is_deleted) continue;   next:
314                  if (ptr->program) {                  list_for_each_entry_rcu(ptr, &ccs_policy_list
315                          if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;                                          [CCS_ID_TRANSITION_CONTROL],
316                  } else {                                          head.list) {
317                          if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->domainname->name)) return -ENOMEM;                          if (ptr->head.is_deleted || ptr->type != type)
318                                    continue;
319                            if (ptr->domainname) {
320                                    if (!ptr->is_last_name) {
321                                            if (ptr->domainname != domainname)
322                                                    continue;
323                                    } else {
324                                            /*
325                                             * Use direct strcmp() since this is
326                                             * unlikely used.
327                                             */
328                                            if (strcmp(ptr->domainname->name,
329                                                       last_name))
330                                                    continue;
331                                    }
332                            }
333                            if (ptr->program && ccs_pathcmp(ptr->program, program))
334                                    continue;
335                            if (type == CCS_TRANSITION_CONTROL_NO_INITIALIZE) {
336                                    /*
337                                     * Do not check for initialize_domain if
338                                     * no_initialize_domain matched.
339                                     */
340                                    type = CCS_TRANSITION_CONTROL_NO_KEEP;
341                                    goto next;
342                            }
343                            goto done;
344                  }                  }
345          }          }
346          return 0;   done:
347            return type;
348  }  }
349    
350  static bool IsDomainKeeper(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)  static bool ccs_same_aggregator(const struct ccs_acl_head *a,
351                                    const struct ccs_acl_head *b)
352  {  {
353          struct domain_keeper_entry *ptr;          const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head);
354          bool flag = 0;          const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head);
355          list1_for_each_entry(ptr, &domain_keeper_list, list) {          return p1->original_name == p2->original_name &&
356                  if (ptr->is_deleted) continue;                  p1->aggregated_name == p2->aggregated_name;
                 if (!ptr->is_last_name) {  
                         if (ptr->domainname != domainname) continue;  
                 } else {  
                         if (pathcmp(ptr->domainname, last_name)) continue;  
                 }  
                 if (ptr->program && pathcmp(ptr->program, program)) continue;  
                 if (ptr->is_not) return 0;  
                 flag = 1;  
         }  
         return flag;  
357  }  }
358    
359  /*************************  SYMBOLIC LINKED PROGRAM HANDLER  *************************/  /**
360     * ccs_update_aggregator_entry - Update "struct ccs_aggregator" list.
361  static LIST1_HEAD(alias_list);   *
362     * @original_name:   The original program's name.
363  static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete)   * @aggregated_name: The aggregated program's name.
364  {   * @is_delete:       True if it is a delete request.
365          struct alias_entry *new_entry, *ptr;   *
366          static DEFINE_MUTEX(lock);   * Returns 0 on success, negative value otherwise.
367          const struct path_info *saved_original_name, *saved_aliased_name;   */
368          int error = -ENOMEM;  static int ccs_update_aggregator_entry(const char *original_name,
369          if (!IsCorrectPath(original_name, 1, -1, -1, __FUNCTION__) || !IsCorrectPath(aliased_name, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */                                         const char *aggregated_name,
370          if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aliased_name = SaveName(aliased_name)) == NULL) return -ENOMEM;                                         const bool is_delete)
371          mutex_lock(&lock);  {
372          list1_for_each_entry(ptr, &alias_list, list) {          struct ccs_aggregator e = { };
373                  if (ptr->original_name == saved_original_name && ptr->aliased_name == saved_aliased_name) {          int error = is_delete ? -ENOENT : -ENOMEM;
374                          ptr->is_deleted = is_delete;          if (!ccs_correct_path(original_name) ||
375                          error = 0;              !ccs_correct_path(aggregated_name))
376                          goto out;                  return -EINVAL;
377                  }          e.original_name = ccs_get_name(original_name);
378          }          e.aggregated_name = ccs_get_name(aggregated_name);
379          if (is_delete) {          if (!e.original_name || !e.aggregated_name ||
380                  error = -ENOENT;              e.aggregated_name->is_patterned) /* No patterns allowed. */
381                  goto out;                  goto out;
382          }          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
383          if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;                                    &ccs_policy_list[CCS_ID_AGGREGATOR],
384          new_entry->original_name = saved_original_name;                                    ccs_same_aggregator);
         new_entry->aliased_name = saved_aliased_name;  
         list1_add_tail_mb(&new_entry->list, &alias_list);  
         error = 0;  
385   out:   out:
386          mutex_unlock(&lock);          ccs_put_name(e.original_name);
387            ccs_put_name(e.aggregated_name);
388          return error;          return error;
389  }  }
390    
391  int ReadAliasPolicy(struct io_buffer *head)  /**
392  {   * ccs_write_aggregator - Write "struct ccs_aggregator" list.
393          struct list1_head *pos;   *
394          list1_for_each_cookie(pos, head->read_var2, &alias_list) {   * @data:      String to parse.
395                  struct alias_entry *ptr;   * @is_delete: True if it is a delete request.
396                  ptr = list1_entry(pos, struct alias_entry, list);   *
397                  if (ptr->is_deleted) continue;   * Returns 0 on success, negative value otherwise.
398                  if (io_printf(head, KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, ptr->aliased_name->name)) return -ENOMEM;   */
399          }  int ccs_write_aggregator(char *data, const bool is_delete)
         return 0;  
 }  
   
 int AddAliasPolicy(char *data, const bool is_delete)  
 {  
         char *cp = strchr(data, ' ');  
         if (!cp) return -EINVAL;  
         *cp++ = '\0';  
         return AddAliasEntry(data, cp, is_delete);  
 }  
   
 /*************************  DOMAIN AGGREGATOR HANDLER  *************************/  
   
 static LIST1_HEAD(aggregator_list);  
   
 static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete)  
400  {  {
401          struct aggregator_entry *new_entry, *ptr;          char *w[2];
402          static DEFINE_MUTEX(lock);          if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
403          const struct path_info *saved_original_name, *saved_aggregated_name;                  return -EINVAL;
404          int error = -ENOMEM;          return ccs_update_aggregator_entry(w[0], w[1], is_delete);
         if (!IsCorrectPath(original_name, 1, 0, -1, __FUNCTION__) || !IsCorrectPath(aggregated_name, 1, -1, -1, __FUNCTION__)) return -EINVAL;  
         if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aggregated_name = SaveName(aggregated_name)) == NULL) return -ENOMEM;  
         mutex_lock(&lock);  
         list1_for_each_entry(ptr, &aggregator_list, list) {  
                 if (ptr->original_name == saved_original_name && ptr->aggregated_name == saved_aggregated_name) {  
                         ptr->is_deleted = is_delete;  
                         error = 0;  
                         goto out;  
                 }  
         }  
         if (is_delete) {  
                 error = -ENOENT;  
                 goto out;  
         }  
         if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;  
         new_entry->original_name = saved_original_name;  
         new_entry->aggregated_name = saved_aggregated_name;  
         list1_add_tail_mb(&new_entry->list, &aggregator_list);  
         error = 0;  
  out:  
         mutex_unlock(&lock);  
         return error;  
405  }  }
406    
407  int ReadAggregatorPolicy(struct io_buffer *head)  /* Domain create/delete handler. */
 {  
         struct list1_head *pos;  
         list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {  
                 struct aggregator_entry *ptr;  
                 ptr = list1_entry(pos, struct aggregator_entry, list);  
                 if (ptr->is_deleted) continue;  
                 if (io_printf(head, KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, ptr->aggregated_name->name)) return -ENOMEM;  
         }  
         return 0;  
 }  
408    
409  int AddAggregatorPolicy(char *data, const bool is_delete)  /**
410     * ccs_delete_domain - Delete a domain.
411     *
412     * @domainname: The name of domain.
413     *
414     * Returns 0.
415     */
416    int ccs_delete_domain(char *domainname)
417  {  {
418          char *cp = strchr(data, ' ');          struct ccs_domain_info *domain;
419          if (!cp) return -EINVAL;          struct ccs_path_info name;
420          *cp++ = '\0';          name.name = domainname;
421          return AddAggregatorEntry(data, cp, is_delete);          ccs_fill_path_info(&name);
422  }          if (mutex_lock_interruptible(&ccs_policy_lock))
423                    return 0;
 /*************************  DOMAIN DELETION HANDLER  *************************/  
   
 /* #define DEBUG_DOMAIN_UNDELETE */  
   
 int DeleteDomain(char *domainname0)  
 {  
         struct domain_info *domain;  
         struct path_info domainname;  
         domainname.name = domainname0;  
         fill_path_info(&domainname);  
         mutex_lock(&new_domain_assign_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  
424          /* Is there an active domain? */          /* Is there an active domain? */
425          list1_for_each_entry(domain, &domain_list, list) {          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
426                  struct domain_info *domain2;                  /* Never delete ccs_kernel_domain */
427                  /* Never delete KERNEL_DOMAIN */                  if (domain == &ccs_kernel_domain)
428                  if (domain == &KERNEL_DOMAIN || domain->is_deleted || pathcmp(domain->domainname, &domainname)) continue;                          continue;
429                  /* Mark already deleted domains as non undeletable. */                  if (domain->is_deleted ||
430                  list1_for_each_entry(domain2, &domain_list, list) {                      ccs_pathcmp(domain->domainname, &name))
431                          if (!domain2->is_deleted || pathcmp(domain2->domainname, &domainname)) continue;                          continue;
432  #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  
433                  break;                  break;
434          }          }
435          mutex_unlock(&new_domain_assign_lock);          mutex_unlock(&ccs_policy_lock);
436          return 0;          return 0;
437  }  }
438    
439  struct domain_info *UndeleteDomain(const char *domainname0)  /**
440     * ccs_assign_domain - Create a domain.
441     *
442     * @domainname: The name of domain.
443     * @profile:    Profile number to assign if the domain was newly created.
444     *
445     * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
446     */
447    struct ccs_domain_info *ccs_assign_domain(const char *domainname,
448                                              const u8 profile)
449  {  {
450          struct domain_info *domain, *candidate_domain = NULL;          struct ccs_domain_info e = { };
451          struct path_info domainname;          struct ccs_domain_info *entry = NULL;
452          domainname.name = domainname0;          bool found = false;
453          fill_path_info(&domainname);  
454          mutex_lock(&new_domain_assign_lock);          if (!ccs_correct_domain(domainname))
455  #ifdef DEBUG_DOMAIN_UNDELETE                  return NULL;
456          printk("UndeleteDomain %s\n", domainname0);          e.profile = profile;
457          list1_for_each_entry(domain, &domain_list, list) {          e.domainname = ccs_get_name(domainname);
458                  if (pathcmp(domain->domainname, &domainname)) continue;          if (!e.domainname)
459                  printk("List: %p %u\n", domain, domain->is_deleted);                  return NULL;
460          }          if (mutex_lock_interruptible(&ccs_policy_lock))
 #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;  
461                  goto out;                  goto out;
462            list_for_each_entry_rcu(entry, &ccs_domain_list, list) {
463                    if (entry->is_deleted ||
464                        ccs_pathcmp(e.domainname, entry->domainname))
465                            continue;
466                    found = true;
467                    break;
468          }          }
469          /* No memory reusable. Create using new memory. */          if (!found) {
470          if ((domain = alloc_element(sizeof(*domain))) != NULL) {                  entry = ccs_commit_ok(&e, sizeof(e));
471                  INIT_LIST1_HEAD(&domain->acl_info_list);                  if (entry) {
472                  domain->domainname = saved_domainname;                          INIT_LIST_HEAD(&entry->acl_info_list);
473                  domain->profile = profile;                          list_add_tail_rcu(&entry->list, &ccs_domain_list);
474                  list1_add_tail_mb(&domain->list, &domain_list);                          found = true;
         }  
  out: ;  
         mutex_unlock(&new_domain_assign_lock);  
         return domain;  
 }  
   
 static int Escape(char *dest, const char *src, int dest_len)  
 {  
         while (*src) {  
                 const unsigned char c = * (const unsigned char *) src;  
                 if (c == '\\') {  
                         dest_len -= 2;  
                         if (dest_len <= 0) goto out;  
                         *dest++ = '\\';  
                         *dest++ = '\\';  
                 } else if (c > ' ' && c < 127) {  
                         if (--dest_len <= 0) goto out;  
                         *dest++ = c;  
                 } else {  
                         dest_len -= 4;  
                         if (dest_len <= 0) goto out;  
                         *dest++ = '\\';  
                         *dest++ = (c >> 6) + '0';  
                         *dest++ = ((c >> 3) & 7) + '0';  
                         *dest++ = (c & 7) + '0';  
475                  }                  }
                 src++;  
476          }          }
477          if (--dest_len <= 0) goto out;          mutex_unlock(&ccs_policy_lock);
         *dest = '\0';  
         return 0;  
478   out:   out:
479          return -ENOMEM;          ccs_put_name(e.domainname);
480            return found ? entry : NULL;
481  }  }
482    
483  static char *get_argv0(struct linux_binprm *bprm)  /**
484     * ccs_find_next_domain - Find a domain.
485     *
486     * @ee: Pointer to "struct ccs_execve".
487     *
488     * Returns 0 on success, negative value otherwise.
489     *
490     * Caller holds ccs_read_lock().
491     */
492    static int ccs_find_next_domain(struct ccs_execve *ee)
493  {  {
494          char *arg_ptr = ccs_alloc(PAGE_SIZE); /* Initial buffer. */          struct ccs_request_info *r = &ee->r;
495          int arg_len = 0;          const struct ccs_path_info *handler = ee->handler;
496          unsigned long pos = bprm->p;          struct ccs_domain_info *domain = NULL;
497          int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;          struct ccs_domain_info * const old_domain = ccs_current_domain();
498          if (!bprm->argc || !arg_ptr) goto out;          struct linux_binprm *bprm = ee->bprm;
499          while (1) {          struct task_struct *task = current;
500                  struct page *page;          const u32 ccs_flags = task->ccs_flags;
501  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)          struct ccs_path_info rn = { }; /* real name */
                 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);  
                 pos += PAGE_SIZE - offset;  
 #endif  
                 arg_len += PAGE_SIZE - offset;  
                 if (memchr(arg_ptr, '\0', arg_len)) break;  
                 { /* Initial buffer was too small for argv[0]. Retry after expanding buffer. */  
                         char *tmp_arg_ptr = ccs_alloc(arg_len + PAGE_SIZE);  
                         if (!tmp_arg_ptr) goto out;  
                         memmove(tmp_arg_ptr, arg_ptr, arg_len);  
                         ccs_free(arg_ptr);  
                         arg_ptr = tmp_arg_ptr;  
                 }  
                 i++;  
                 offset = 0;  
         }  
         return arg_ptr;  
  out: /* Release initial buffer. */  
         ccs_free(arg_ptr);  
         return NULL;  
 }  
   
 static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check)  
 {  
         /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */  
         struct domain_info *old_domain = current->domain_info, *domain = NULL;  
         const char *old_domain_name = old_domain->domainname->name;  
         const char *original_name = bprm->filename;  
         struct file *filp = bprm->file;  
         char *new_domain_name = NULL;  
         char *real_program_name = NULL, *symlink_program_name = NULL;  
         const bool is_enforce = (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_FILE) == 3);  
502          int retval;          int retval;
503          struct path_info r, s, l;          bool need_kfree = false;
504            bool domain_created = false;
505          {   retry:
506                  /*          current->ccs_flags = ccs_flags;
507                   * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .          r->cond = NULL;
508                   */          if (need_kfree) {
509                  static int first = 1;                  kfree(rn.name);
510                  if (first) {                  need_kfree = false;
                         AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);  
                         AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);  
                         first = 0;  
                 }  
511          }          }
512    
513          /* Get realpath of program. */          /* Get symlink's pathname of program. */
514          retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */          retval = ccs_symlink_path(bprm->filename, &rn);
515          if ((real_program_name = realpath(original_name)) == NULL) goto out;          if (retval < 0)
516          /* Get realpath of symbolic link. */                  goto out;
517          if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;          need_kfree = true;
518    
519          r.name = real_program_name;          if (handler) {
520          fill_path_info(&r);                  if (ccs_pathcmp(&rn, handler)) {
521          s.name = symlink_program_name;                          /* Failed to verify execute handler. */
522          fill_path_info(&s);                          static u8 counter = 20;
523          if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;                          if (counter) {
524          else l.name = old_domain_name;                                  counter--;
525          fill_path_info(&l);                                  printk(KERN_WARNING "Failed to verify: %s\n",
526                                           handler->name);
         if (!do_perm_check) goto ok;  
   
         /* Check 'alias' directive. */  
         if (pathcmp(&r, &s)) {  
                 struct alias_entry *ptr;  
                 /* Is this program allowed to be called via symbolic links? */  
                 list1_for_each_entry(ptr, &alias_list, list) {  
                         if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;  
                         memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);  
                         strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);  
                         fill_path_info(&r);  
                         break;  
                 }  
         }  
           
         /* Compare basename of real_program_name and argv[0] */  
         if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {  
                 char *org_argv0 = get_argv0(bprm);  
                 retval = -ENOMEM;  
                 if (org_argv0) {  
                         const int len = strlen(org_argv0);  
                         char *printable_argv0 = ccs_alloc(len * 4 + 8);  
                         if (printable_argv0 && Escape(printable_argv0, org_argv0, len * 4 + 8) == 0) {  
                                 const char *base_argv0, *base_filename;  
                                 if ((base_argv0 = strrchr(printable_argv0, '/')) == NULL) base_argv0 = printable_argv0; else base_argv0++;  
                                 if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;  
                                 if (strcmp(base_argv0, base_filename)) retval = CheckArgv0Perm(&r, base_argv0);  
                                 else retval = 0;  
527                          }                          }
528                          ccs_free(printable_argv0);                          goto out;
                         ccs_free(org_argv0);  
529                  }                  }
530                  if (retval) goto out;          } else {
531          }                  struct ccs_aggregator *ptr;
532                            /* Check 'aggregator' directive. */
533          /* Check 'aggregator' directive. */                  list_for_each_entry_rcu(ptr,
534          {                                          &ccs_policy_list[CCS_ID_AGGREGATOR],
535                  struct aggregator_entry *ptr;                                          head.list) {
536                  /* Is this program allowed to be aggregated? */                          if (ptr->head.is_deleted ||
537                  list1_for_each_entry(ptr, &aggregator_list, list) {                              !ccs_path_matches_pattern(&rn, ptr->original_name))
538                          if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;                                  continue;
539                          memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);                          kfree(rn.name);
540                          strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);                          need_kfree = false;
541                          fill_path_info(&r);                          /* This is OK because it is read only. */
542                            rn = *ptr->aggregated_name;
543                          break;                          break;
544                  }                  }
         }  
545    
546          /* Check execute permission. */                  /* Check execute permission. */
547          if ((retval = CheckExecPerm(&r, filp)) < 0) goto out;                  retval = ccs_path_permission(r, CCS_TYPE_EXECUTE, &rn);
548                    if (retval == CCS_RETRY_REQUEST)
549                            goto retry;
550                    if (retval < 0)
551                            goto out;
552            }
553    
554   ok: ;          /* Calculate domain to transit to. */
555          /* Allocate memory for calcurating domain name. */          switch (ccs_transition_type(old_domain->domainname, &rn)) {
556          retval = -ENOMEM;          case CCS_TRANSITION_CONTROL_INITIALIZE:
557          if ((new_domain_name = ccs_alloc(CCS_MAX_PATHNAME_LEN + 16)) == NULL) goto out;                  /* Transit to the child of ccs_kernel_domain domain. */
558                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, CCS_ROOT_NAME " " "%s",
559          if (IsDomainInitializer(old_domain->domainname, &r, &l)) {                           rn.name);
560                  /* Transit to the child of KERNEL_DOMAIN domain. */                  break;
561                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, ROOT_NAME " " "%s", real_program_name);          case CCS_TRANSITION_CONTROL_KEEP:
         } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {  
                 /*  
                  * Needn't to transit from kernel domain before starting /sbin/init .  
                  * But transit from kernel domain if executing initializers, for they might start before /sbin/init .  
                  */  
                 domain = old_domain;  
         } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {  
562                  /* Keep current domain. */                  /* Keep current domain. */
563                  domain = old_domain;                  domain = old_domain;
564          } else {                  break;
565                  /* Normal domain transition. */          default:
566                  snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);                  if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) {
567          }                          /*
568          if (!domain && strlen(new_domain_name) < CCS_MAX_PATHNAME_LEN) {                           * Needn't to transit from kernel domain before
569                  if (is_enforce) {                           * starting /sbin/init. But transit from kernel domain
570                          domain = FindDomain(new_domain_name);                           * if executing initializers because they might start
571                          if (!domain) if (CheckSupervisor("#Need to create domain\n%s\n", new_domain_name) == 0) domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);                           * before /sbin/init.
572                             */
573                            domain = old_domain;
574                  } else {                  } else {
575                          domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);                          /* Normal domain transition. */
576                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
577                                     old_domain->domainname->name, rn.name);
578                  }                  }
579                    break;
580          }          }
581            if (domain || strlen(ee->tmp) >= CCS_EXEC_TMPSIZE - 10)
582                    goto done;
583            domain = ccs_find_domain(ee->tmp);
584            if (domain)
585                    goto done;
586            if (r->mode == CCS_CONFIG_ENFORCING) {
587                    int error = ccs_supervisor(r, "# wants to create domain\n"
588                                               "%s\n", ee->tmp);
589                    if (error == CCS_RETRY_REQUEST)
590                            goto retry;
591                    if (error < 0)
592                            goto done;
593            }
594            domain = ccs_assign_domain(ee->tmp, r->profile);
595            if (domain)
596                    domain_created = true;
597     done:
598          if (!domain) {          if (!domain) {
599                  printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);                  retval = (r->mode == CCS_CONFIG_ENFORCING) ? -EPERM : 0;
600                  if (is_enforce) retval = -EPERM;                  if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) {
601                            old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true;
602                            r->granted = false;
603                            ccs_write_log(r, CCS_KEYWORD_TRANSITION_FAILED "\n");
604                            printk(KERN_WARNING
605                                   "ERROR: Domain '%s' not defined.\n", ee->tmp);
606                    }
607          } else {          } else {
608                  retval = 0;                  retval = 0;
609          }          }
610   out: ;          if (!retval && handler)
611          ccs_free(new_domain_name);                  ccs_audit_execute_handler_log(ee);
612          ccs_free(real_program_name);          /*
613          ccs_free(symlink_program_name);           * Tell GC that I started execve().
614          *next_domain = domain ? domain : old_domain;           * Also, tell open_exec() to check read permission.
615             */
616            task->ccs_flags |= CCS_TASK_IS_IN_EXECVE;
617            /*
618             * Make task->ccs_flags visible to GC before changing
619             * task->ccs_domain_info .
620             */
621            smp_mb();
622            /*
623             * Proceed to the next domain in order to allow reaching via PID.
624             * It will be reverted if execve() failed. Reverting is not good.
625             * But it is better than being unable to reach via PID in interactive
626             * enforcing mode.
627             */
628            if (domain)
629                    task->ccs_domain_info = domain;
630            if (domain_created)
631                    ccs_audit_domain_creation_log();
632     out:
633            if (need_kfree)
634                    kfree(rn.name);
635          return retval;          return retval;
636  }  }
637    
638  static int CheckEnviron(struct linux_binprm *bprm)  /**
639     * ccs_environ - Check permission for environment variable names.
640     *
641     * @ee: Pointer to "struct ccs_execve".
642     *
643     * Returns 0 on success, negative value otherwise.
644     */
645    static int ccs_environ(struct ccs_execve *ee)
646  {  {
647          const u8 profile = current->domain_info->profile;          struct ccs_request_info *r = &ee->r;
648          const u8 mode = CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV);          struct linux_binprm *bprm = ee->bprm;
649          char *arg_ptr;          /* env_page->data is allocated by ccs_dump_page(). */
650            struct ccs_page_dump env_page = { };
651            char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */
652          int arg_len = 0;          int arg_len = 0;
653          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
654          int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;          int offset = pos % PAGE_SIZE;
655          int argv_count = bprm->argc;          int argv_count = bprm->argc;
656          int envp_count = bprm->envc;          int envp_count = bprm->envc;
657          //printk("start %d %d\n", argv_count, envp_count);          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
658          int error = -ENOMEM;          int error = -ENOMEM;
659          if (!mode || !envp_count) return 0;          ee->r.type = CCS_MAC_ENVIRON;
660          arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN);          ee->r.mode = ccs_get_mode(ccs_current_domain()->profile,
661          if (!arg_ptr) goto out;                                    CCS_MAC_ENVIRON);
662            if (!r->mode || !envp_count)
663                    return 0;
664            arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
665            if (!arg_ptr)
666                    goto out;
667          while (error == -ENOMEM) {          while (error == -ENOMEM) {
668                  struct page *page;                  if (!ccs_dump_page(bprm, pos, &env_page))
                 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  
669                          goto out;                          goto out;
670                  }                  pos += PAGE_SIZE - offset;
671                  /* Read. */                  /* Read. */
672                  while (argv_count && offset < PAGE_SIZE) {                  while (argv_count && offset < PAGE_SIZE) {
673                          if (!kaddr[offset++]) argv_count--;                          if (!env_page.data[offset++])
674                                    argv_count--;
675                    }
676                    if (argv_count) {
677                            offset = 0;
678                            continue;
679                  }                  }
                 if (argv_count) goto unmap_page;  
680                  while (offset < PAGE_SIZE) {                  while (offset < PAGE_SIZE) {
681                          const unsigned char c = kaddr[offset++];                          const unsigned char c = env_page.data[offset++];
682                          if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {                          if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
683                                  if (c == '=') {                                  if (c == '=') {
684                                          arg_ptr[arg_len++] = '\0';                                          arg_ptr[arg_len++] = '\0';
685                                  } else if (c == '\\') {                                  } else if (c == '\\') {
# Line 783  static int CheckEnviron(struct linux_bin Line 690  static int CheckEnviron(struct linux_bin
690                                  } else {                                  } else {
691                                          arg_ptr[arg_len++] = '\\';                                          arg_ptr[arg_len++] = '\\';
692                                          arg_ptr[arg_len++] = (c >> 6) + '0';                                          arg_ptr[arg_len++] = (c >> 6) + '0';
693                                          arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';                                          arg_ptr[arg_len++]
694                                                    = ((c >> 3) & 7) + '0';
695                                          arg_ptr[arg_len++] = (c & 7) + '0';                                          arg_ptr[arg_len++] = (c & 7) + '0';
696                                  }                                  }
697                          } else {                          } else {
698                                  arg_ptr[arg_len] = '\0';                                  arg_ptr[arg_len] = '\0';
699                          }                          }
700                          if (c) continue;                          if (c)
701                          if (CheckEnvPerm(arg_ptr, profile, mode)) {                                  continue;
702                            if (ccs_env_perm(r, arg_ptr)) {
703                                  error = -EPERM;                                  error = -EPERM;
704                                  break;                                  break;
705                          }                          }
# Line 800  static int CheckEnviron(struct linux_bin Line 709  static int CheckEnviron(struct linux_bin
709                          }                          }
710                          arg_len = 0;                          arg_len = 0;
711                  }                  }
         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++;  
712                  offset = 0;                  offset = 0;
713          }          }
714   out:   out:
715          ccs_free(arg_ptr);          if (r->mode != 3)
716          if (error && mode != 3) error = 0;                  error = 0;
717            kfree(env_page.data);
718            kfree(arg_ptr);
719          return error;          return error;
720  }  }
721    
722  static void UnEscape(unsigned char *dest)  /**
723     * ccs_unescape - Unescape escaped string.
724     *
725     * @dest: String to unescape.
726     *
727     * Returns nothing.
728     */
729    static void ccs_unescape(unsigned char *dest)
730  {  {
731          unsigned char *src = dest;          unsigned char *src = dest;
732          unsigned char c, d, e;          unsigned char c;
733          while ((c = *src++) != '\0') {          unsigned char d;
734            unsigned char e;
735            while (1) {
736                    c = *src++;
737                    if (!c)
738                            break;
739                  if (c != '\\') {                  if (c != '\\') {
740                          *dest++ = c;                          *dest++ = c;
741                          continue;                          continue;
# Line 828  static void UnEscape(unsigned char *dest Line 743  static void UnEscape(unsigned char *dest
743                  c = *src++;                  c = *src++;
744                  if (c == '\\') {                  if (c == '\\') {
745                          *dest++ = c;                          *dest++ = c;
746                  } 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;  
747                  }                  }
748                    if (c < '0' || c > '3')
749                            break;
750                    d = *src++;
751                    if (d < '0' || d > '7')
752                            break;
753                    e = *src++;
754                    if (e < '0' || e > '7')
755                            break;
756                    *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
757          }          }
758          *dest = '\0';          *dest = '\0';
759  }  }
760    
761  static int try_alt_exec(struct linux_binprm *bprm, char **alt_exec0)  /**
762     * ccs_root_depth - Get number of directories to strip.
763     *
764     * @dentry: Pointer to "struct dentry".
765     * @vfsmnt: Pointer to "struct vfsmount".
766     *
767     * Returns number of directories to strip.
768     */
769    static int ccs_root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)
770  {  {
771            int depth = 0;
772            ccs_realpath_lock();
773            for (;;) {
774                    if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
775                            /* Global root? */
776                            if (vfsmnt->mnt_parent == vfsmnt)
777                                    break;
778                            dentry = vfsmnt->mnt_mountpoint;
779                            vfsmnt = vfsmnt->mnt_parent;
780                            continue;
781                    }
782                    dentry = dentry->d_parent;
783                    depth++;
784            }
785            ccs_realpath_unlock();
786            return depth;
787    }
788    
789    /**
790     * ccs_get_root_depth - return the depth of root directory.
791     *
792     * Returns number of directories to strip.
793     */
794    static int ccs_get_root_depth(void)
795    {
796            int depth;
797            struct dentry *dentry;
798            struct vfsmount *vfsmnt;
799    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
800            struct path root;
801    #endif
802            read_lock(&current->fs->lock);
803    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
804            root = current->fs->root;
805            path_get(&current->fs->root);
806            dentry = root.dentry;
807            vfsmnt = root.mnt;
808    #else
809            dentry = dget(current->fs->root);
810            vfsmnt = mntget(current->fs->rootmnt);
811    #endif
812            read_unlock(&current->fs->lock);
813            depth = ccs_root_depth(dentry, vfsmnt);
814    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
815            path_put(&root);
816    #else
817            dput(dentry);
818            mntput(vfsmnt);
819    #endif
820            return depth;
821    }
822    
823    /**
824     * ccs_try_alt_exec - Try to start execute handler.
825     *
826     * @ee: Pointer to "struct ccs_execve".
827     *
828     * Returns 0 on success, negative value otherwise.
829     */
830    static int ccs_try_alt_exec(struct ccs_execve *ee)
831    {
832            /*
833             * Contents of modified bprm.
834             * The envp[] in original bprm is moved to argv[] so that
835             * the alternatively executed program won't be affected by
836             * some dangerous environment variables like LD_PRELOAD.
837             *
838             * modified bprm->argc
839             *    = original bprm->argc + original bprm->envc + 7
840             * modified bprm->envc
841             *    = 0
842             *
843             * modified bprm->argv[0]
844             *    = the program's name specified by execute_handler
845             * modified bprm->argv[1]
846             *    = ccs_current_domain()->domainname->name
847             * modified bprm->argv[2]
848             *    = the current process's name
849             * modified bprm->argv[3]
850             *    = the current process's information (e.g. uid/gid).
851             * modified bprm->argv[4]
852             *    = original bprm->filename
853             * modified bprm->argv[5]
854             *    = original bprm->argc in string expression
855             * modified bprm->argv[6]
856             *    = original bprm->envc in string expression
857             * modified bprm->argv[7]
858             *    = original bprm->argv[0]
859             *  ...
860             * modified bprm->argv[bprm->argc + 6]
861             *     = original bprm->argv[bprm->argc - 1]
862             * modified bprm->argv[bprm->argc + 7]
863             *     = original bprm->envp[0]
864             *  ...
865             * modified bprm->argv[bprm->envc + bprm->argc + 6]
866             *     = original bprm->envp[bprm->envc - 1]
867             */
868            struct linux_binprm *bprm = ee->bprm;
869          struct file *filp;          struct file *filp;
870          int retval;          int retval;
871          /* domainname must not be modified. */          const int original_argc = bprm->argc;
872          char *domainname = (char *) current->domain_info->domainname->name;          const int original_envc = bprm->envc;
873          char *alt_exec;          struct task_struct *task = current;
874          const char *alt_exec1 = GetAltExec();  
875          if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL;          /* Close the requested program's dentry. */
876          retval = strlen(alt_exec1) + 1;          ee->obj.path1.dentry = NULL;
877          *alt_exec0 = alt_exec = ccs_alloc(retval);          ee->obj.path1.mnt = NULL;
878          if (!alt_exec) return -ENOMEM;          ee->obj.validate_done = false;
         memmove(alt_exec, alt_exec1, retval);  
         UnEscape(alt_exec);  
879          allow_write_access(bprm->file);          allow_write_access(bprm->file);
880          fput(bprm->file);          fput(bprm->file);
881          bprm->file = NULL;          bprm->file = NULL;
882  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)  
883          retval = remove_arg_zero(bprm);          /* Invalidate page dump cache. */
884          if (retval) return retval;          ee->dump.page = NULL;
885  #else  
886          remove_arg_zero(bprm);          /* Move envp[] to argv[] */
887            bprm->argc += bprm->envc;
888            bprm->envc = 0;
889    
890            /* Set argv[6] */
891            {
892                    char *cp = ee->tmp;
893                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc);
894                    retval = copy_strings_kernel(1, &cp, bprm);
895                    if (retval < 0)
896                            goto out;
897                    bprm->argc++;
898            }
899    
900            /* Set argv[5] */
901            {
902                    char *cp = ee->tmp;
903                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc);
904                    retval = copy_strings_kernel(1, &cp, bprm);
905                    if (retval < 0)
906                            goto out;
907                    bprm->argc++;
908            }
909    
910            /* Set argv[4] */
911            {
912                    retval = copy_strings_kernel(1, &bprm->filename, bprm);
913                    if (retval < 0)
914                            goto out;
915                    bprm->argc++;
916            }
917    
918            /* Set argv[3] */
919            {
920                    char *cp = ee->tmp;
921                    const u32 ccs_flags = task->ccs_flags;
922                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1,
923                             "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
924                             "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
925                             "state[1]=%u state[2]=%u",
926                             (pid_t) ccsecurity_exports.sys_getpid(),
927                             current_uid(), current_gid(), current_euid(),
928                             current_egid(), current_suid(), current_sgid(),
929                             current_fsuid(), current_fsgid(),
930                             (u8) (ccs_flags >> 24), (u8) (ccs_flags >> 16),
931                             (u8) (ccs_flags >> 8));
932                    retval = copy_strings_kernel(1, &cp, bprm);
933                    if (retval < 0)
934                            goto out;
935                    bprm->argc++;
936            }
937    
938            /* Set argv[2] */
939            {
940                    char *exe = (char *) ccs_get_exe();
941                    if (exe) {
942                            retval = copy_strings_kernel(1, &exe, bprm);
943                            kfree(exe);
944                    } else {
945                            exe = ee->tmp;
946                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
947                            retval = copy_strings_kernel(1, &exe, bprm);
948                    }
949                    if (retval < 0)
950                            goto out;
951                    bprm->argc++;
952            }
953    
954            /* Set argv[1] */
955            {
956                    char *cp = ee->tmp;
957                    snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
958                             ccs_current_domain()->domainname->name);
959                    retval = copy_strings_kernel(1, &cp, bprm);
960                    if (retval < 0)
961                            goto out;
962                    bprm->argc++;
963            }
964    
965            /* Set argv[0] */
966            {
967                    int depth = ccs_get_root_depth();
968                    int len = ee->handler->total_len + 1;
969                    char *cp = kmalloc(len, CCS_GFP_FLAGS);
970                    if (!cp) {
971                            retval = -ENOMEM;
972                            goto out;
973                    }
974                    ee->handler_path = cp;
975                    memmove(cp, ee->handler->name, len);
976                    ccs_unescape(cp);
977                    retval = -ENOENT;
978                    if (!*cp || *cp != '/')
979                            goto out;
980                    /* Adjust root directory for open_exec(). */
981                    while (depth) {
982                            cp = strchr(cp + 1, '/');
983                            if (!cp)
984                                    goto out;
985                            depth--;
986                    }
987                    memmove(ee->handler_path, cp, strlen(cp) + 1);
988                    cp = ee->handler_path;
989                    retval = copy_strings_kernel(1, &cp, bprm);
990                    if (retval < 0)
991                            goto out;
992                    bprm->argc++;
993            }
994    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
995    #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
996            bprm->argv_len = bprm->exec - bprm->p;
997  #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);  
998  #endif  #endif
999          if (retval < 0) return retval;  
1000          bprm->argc++;          /*
1001          retval = copy_strings_kernel(1, &domainname, bprm);           * OK, now restart the process with execute handler program's dentry.
1002          if (retval < 0) return retval;           */
1003          bprm->argc++;          filp = open_exec(ee->handler_path);
1004          retval = copy_strings_kernel(1, &alt_exec, bprm);          if (IS_ERR(filp)) {
1005          if (retval < 0) return retval;                  retval = PTR_ERR(filp);
1006          bprm->argc++;                  goto out;
1007  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)          }
1008          bprm->interp = alt_exec;          ee->obj.path1.dentry = filp->f_dentry;
1009  #endif          ee->obj.path1.mnt = filp->f_vfsmnt;
1010          filp = open_exec(alt_exec);          bprm->file = filp;
1011          if (IS_ERR(filp)) return PTR_ERR(filp);          bprm->filename = ee->handler_path;
1012          bprm->file= filp;  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
1013          bprm->filename = alt_exec;          bprm->interp = bprm->filename;
1014          return prepare_binprm(bprm);  #endif
1015            retval = prepare_binprm(bprm);
1016            if (retval < 0)
1017                    goto out;
1018            task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1019            retval = ccs_find_next_domain(ee);
1020            task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1021     out:
1022            return retval;
1023  }  }
1024    
1025    /**
1026     * ccs_find_execute_handler - Find an execute handler.
1027     *
1028     * @ee:   Pointer to "struct ccs_execve".
1029     * @type: Type of execute handler.
1030     *
1031     * Returns true if found, false otherwise.
1032     *
1033     * Caller holds ccs_read_lock().
1034     */
1035    static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type)
1036    {
1037            struct ccs_request_info *r = &ee->r;
1038            const struct ccs_domain_info *domain = ccs_current_domain();
1039            struct ccs_acl_info *ptr;
1040            /*
1041             * To avoid infinite execute handler loop, don't use execute handler
1042             * if the current process is marked as execute handler .
1043             */
1044            if (current->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
1045                    return false;
1046     retry:
1047            list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1048                    struct ccs_execute_handler *acl;
1049                    if (ptr->type != type || !ccs_condition(r, ptr->cond))
1050                            continue;
1051                    acl = container_of(ptr, struct ccs_execute_handler, head);
1052                    ee->handler = acl->handler;
1053                    ee->handler_type = type;
1054                    r->cond = ptr->cond;
1055                    return true;
1056            }
1057            if (domain != &ccs_global_domain &&
1058                !domain->flags[CCS_DIF_IGNORE_GLOBAL]) {
1059                    domain = &ccs_global_domain;
1060                    goto retry;
1061            }
1062            return false;
1063    }
1064    
1065    #ifdef CONFIG_MMU
1066    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1067    #define CCS_BPRM_MMU
1068    #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3
1069    #define CCS_BPRM_MMU
1070    #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2
1071    #define CCS_BPRM_MMU
1072    #endif
1073  #endif  #endif
1074    
1075  int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)  /**
1076  {   * ccs_dump_page - Dump a page to buffer.
1077          struct domain_info *next_domain = NULL, *prev_domain = current->domain_info;   *
1078          int retval;   * @bprm: Pointer to "struct linux_binprm".
1079          char *alt_exec = NULL; /* Keep valid until search_binary_handler() finishes. */   * @pos:  Location to dump.
1080  #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)   * @dump: Poiner to "struct ccs_page_dump".
1081          extern void CCS_LoadPolicy(const char *filename);   *
1082          CCS_LoadPolicy(bprm->filename);   * Returns true on success, false otherwise.
1083  #endif   */
1084  #if defined(CONFIG_TOMOYO)  bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos,
1085          retval = FindNextDomain(bprm, &next_domain, 1);                     struct ccs_page_dump *dump)
1086          if (retval == -EPERM && try_alt_exec(bprm, &alt_exec) >= 0) {  {
1087                  current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;          struct page *page;
1088                  retval = FindNextDomain(bprm, &next_domain, 0);          /* dump->data is released by ccs_finish_execve(). */
1089                  current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;          if (!dump->data) {
1090          }                  dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS);
1091          if (retval == 0) {                  if (!dump->data)
1092                  current->domain_info = next_domain;                          return false;
1093                  retval = CheckEnviron(bprm);          }
1094                  current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;          /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
1095                  if (!retval) retval = search_binary_handler(bprm, regs);  #ifdef CCS_BPRM_MMU
1096                  current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;          if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1097                  if (retval < 0) current->domain_info = prev_domain;                  return false;
         }  
         ccs_free(alt_exec);  
         return retval;  
1098  #else  #else
1099          return search_binary_handler(bprm, regs);          page = bprm->page[pos / PAGE_SIZE];
1100  #endif  #endif
1101            if (page != dump->page) {
1102                    const unsigned int offset = pos % PAGE_SIZE;
1103                    /*
1104                     * Maybe kmap()/kunmap() should be used here.
1105                     * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
1106                     * So do I.
1107                     */
1108                    char *kaddr = kmap_atomic(page, KM_USER0);
1109                    dump->page = page;
1110                    memcpy(dump->data + offset, kaddr + offset,
1111                           PAGE_SIZE - offset);
1112                    kunmap_atomic(kaddr, KM_USER0);
1113            }
1114            /* Same with put_arg_page(page) in fs/exec.c */
1115    #ifdef CCS_BPRM_MMU
1116            put_page(page);
1117    #endif
1118            return true;
1119    }
1120    
1121    /**
1122     * ccs_start_execve - Prepare for execve() operation.
1123     *
1124     * @bprm: Pointer to "struct linux_binprm".
1125     * @eep:  Pointer to "struct ccs_execve *".
1126     *
1127     * Returns 0 on success, negative value otherwise.
1128     */
1129    static int ccs_start_execve(struct linux_binprm *bprm,
1130                                struct ccs_execve **eep)
1131    {
1132            int retval;
1133            struct task_struct *task = current;
1134            struct ccs_execve *ee;
1135            *eep = NULL;
1136            ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS);
1137            if (!ee)
1138                    return -ENOMEM;
1139            ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
1140            if (!ee->tmp) {
1141                    kfree(ee);
1142                    return -ENOMEM;
1143            }
1144            ee->reader_idx = ccs_read_lock();
1145            /* ee->dump->data is allocated by ccs_dump_page(). */
1146            ee->previous_domain = task->ccs_domain_info;
1147            /* Clear manager flag. */
1148            task->ccs_flags &= ~CCS_TASK_IS_MANAGER;
1149            *eep = ee;
1150            ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE);
1151            ee->r.ee = ee;
1152            ee->bprm = bprm;
1153            ee->r.obj = &ee->obj;
1154            ee->obj.path1.dentry = bprm->file->f_dentry;
1155            ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1156            /*
1157             * No need to call ccs_environ() for execute handler because envp[] is
1158             * moved to argv[].
1159             */
1160            if (ccs_find_execute_handler(ee, CCS_TYPE_EXECUTE_HANDLER))
1161                    return ccs_try_alt_exec(ee);
1162            retval = ccs_find_next_domain(ee);
1163            if (retval == -EPERM) {
1164                    if (ccs_find_execute_handler(ee,
1165                                                 CCS_TYPE_DENIED_EXECUTE_HANDLER))
1166                            return ccs_try_alt_exec(ee);
1167            }
1168            if (!retval)
1169                    retval = ccs_environ(ee);
1170            return retval;
1171    }
1172    
1173    /**
1174     * ccs_finish_execve - Clean up execve() operation.
1175     *
1176     * @retval: Return code of an execve() operation.
1177     * @ee:     Pointer to "struct ccs_execve".
1178     *
1179     * Caller holds ccs_read_lock().
1180     */
1181    static void ccs_finish_execve(int retval, struct ccs_execve *ee)
1182    {
1183            struct task_struct *task = current;
1184            if (!ee)
1185                    return;
1186            if (retval < 0) {
1187                    task->ccs_domain_info = ee->previous_domain;
1188                    /*
1189                     * Make task->ccs_domain_info visible to GC before changing
1190                     * task->ccs_flags .
1191                     */
1192                    smp_mb();
1193            } else {
1194                    /* Mark the current process as execute handler. */
1195                    if (ee->handler)
1196                            task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1197                    /* Mark the current process as normal process. */
1198                    else
1199                            task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1200            }
1201            /* Tell GC that I finished execve(). */
1202            task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE;
1203            ccs_read_unlock(ee->reader_idx);
1204            kfree(ee->handler_path);
1205            kfree(ee->tmp);
1206            kfree(ee->dump.data);
1207            kfree(ee);
1208    }
1209    
1210    /**
1211     * ccs_may_transit - Check permission and do domain transition without execve().
1212     *
1213     * @domainname: Domainname to transit to.
1214     * @pathname: Pathname to check.
1215     *
1216     * Returns 0 on success, negative value otherwise.
1217     *
1218     * Caller holds ccs_read_lock().
1219     */
1220    int ccs_may_transit(const char *domainname, const char *pathname)
1221    {
1222            struct ccs_path_info name;
1223            struct ccs_request_info r;
1224            struct ccs_domain_info *domain;
1225            int error;
1226            bool domain_created = false;
1227            name.name = pathname;
1228            ccs_fill_path_info(&name);
1229            /* Check allow_transit permission. */
1230            ccs_init_request_info(&r, CCS_MAC_FILE_TRANSIT);
1231            error = ccs_path_permission(&r, CCS_TYPE_TRANSIT, &name);
1232            if (error)
1233                    return error;
1234            /* Check destination domain. */
1235            domain = ccs_find_domain(domainname);
1236            if (!domain && r.mode != CCS_CONFIG_ENFORCING &&
1237                strlen(domainname) < CCS_EXEC_TMPSIZE - 10) {
1238                    domain = ccs_assign_domain(domainname, r.profile);
1239                    if (domain)
1240                            domain_created = true;
1241            }
1242            if (domain) {
1243                    error = 0;
1244                    current->ccs_domain_info = domain;
1245                    if (domain_created)
1246                            ccs_audit_domain_creation_log();
1247            } else {
1248                    error = -ENOENT;
1249            }
1250            return error;
1251  }  }
1252    
1253  /***** TOMOYO Linux end. *****/  static int __ccs_search_binary_handler(struct linux_binprm *bprm,
1254                                           struct pt_regs *regs)
1255    {
1256            struct ccs_execve *ee;
1257            int retval;
1258            if (!ccs_policy_loaded)
1259                    ccsecurity_exports.load_policy(bprm->filename);
1260            retval = ccs_start_execve(bprm, &ee);
1261            if (!retval)
1262                    retval = search_binary_handler(bprm, regs);
1263            ccs_finish_execve(retval, ee);
1264            return retval;
1265    }
1266    
1267    void __init ccs_domain_init(void)
1268    {
1269            ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler;
1270    }

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

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