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

Subversion リポジトリの参照

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

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

branches/ccs-patch/fs/tomoyo_domain.c revision 2828 by kumaneko, Mon Aug 3 05:36:36 2009 UTC branches/ccs-patch/security/ccsecurity/domain.c revision 3808 by kumaneko, Fri Jul 9 04:40:54 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-2009  NTT DATA CORPORATION   * Version: 1.7.2+   2010/06/04
  *  
  * Version: 1.7.0-pre   2009/07/03  
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.
# Line 22  Line 20 
20  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21  #include <linux/fs_struct.h>  #include <linux/fs_struct.h>
22  #endif  #endif
23  #include <linux/ccs_common.h>  #include "internal.h"
 #include <linux/tomoyo.h>  
   
 /* For compatibility with older kernels. */  
 #ifndef for_each_process  
 #define for_each_process for_each_task  
 #endif  
24    
25  /* Variables definitions.*/  /* Variables definitions.*/
26    
27    /* The global domain. */
28    struct ccs_domain_info ccs_global_domain;
29    
30  /* The initial domain. */  /* The initial domain. */
31  struct ccs_domain_info ccs_kernel_domain;  struct ccs_domain_info ccs_kernel_domain;
32    
33  /* The list for "struct ccs_domain_info". */  /* The list for "struct ccs_domain_info". */
34  LIST_HEAD(ccs_domain_list);  LIST_HEAD(ccs_domain_list);
35    
36  /**  struct list_head ccs_policy_list[CCS_MAX_POLICY];
37   * ccs_get_last_name - Get last component of a domainname.  struct list_head ccs_group_list[CCS_MAX_GROUP];
38   *  struct list_head ccs_shared_list[CCS_MAX_LIST];
  * @domain: Pointer to "struct ccs_domain_info".  
  *  
  * Returns the last component of the domainname.  
  */  
 const char *ccs_get_last_name(const struct ccs_domain_info *domain)  
 {  
         const char *cp0 = domain->domainname->name;  
         const char *cp1 = strrchr(cp0, ' ');  
         if (cp1)  
                 return cp1 + 1;  
         return cp0;  
 }  
   
 /**  
  * ccs_add_domain_acl - Add the given ACL to the given domain.  
  *  
  * @domain: Pointer to "struct ccs_domain_info". May be NULL.  
  * @acl:    Pointer to "struct ccs_acl_info".  
  *  
  * Returns 0.  
  */  
 int ccs_add_domain_acl(struct ccs_domain_info *domain, struct ccs_acl_info *acl)  
 {  
         if (domain) {  
                 if (acl->cond)  
                         atomic_inc(&acl->cond->users);  
                 list_add_tail_rcu(&acl->list, &domain->acl_info_list);  
         } else {  
                 acl->type &= ~ACL_DELETED;  
         }  
         return 0;  
 }  
   
 /**  
  * ccs_del_domain_acl - Delete the given ACL from the domain.  
  *  
  * @acl: Pointer to "struct ccs_acl_info". May be NULL.  
  *  
  * Returns 0.  
  */  
 int ccs_del_domain_acl(struct ccs_acl_info *acl)  
 {  
         if (acl)  
                 acl->type |= ACL_DELETED;  
         return 0;  
 }  
39    
40  /**  /**
41   * ccs_audit_execute_handler_log - Audit execute_handler log.   * ccs_audit_execute_handler_log - Audit execute_handler log.
42   *   *
43   * @ee:         Pointer to "struct ccs_execve_entry".   * @ee:         Pointer to "struct ccs_execve".
  * @is_default: True if it is "execute_handler" log.  
44   *   *
45   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
46   */   */
47  static int ccs_audit_execute_handler_log(struct ccs_execve_entry *ee,  static int ccs_audit_execute_handler_log(struct ccs_execve *ee)
                                          const bool is_default)  
48  {  {
49          struct ccs_request_info *r = &ee->r;          struct ccs_request_info *r = &ee->r;
50          const char *handler = ee->handler->name;          const char *handler = ee->handler->name;
51          r->mode = ccs_check_flags(r->domain, CCS_MAC_FOR_FILE);          r->type = CCS_MAC_FILE_EXECUTE;
52          return ccs_write_audit_log(true, r, "%s %s\n",          r->mode = ccs_get_mode(r->profile, CCS_MAC_FILE_EXECUTE);
53                                     is_default ? KEYWORD_EXECUTE_HANDLER :          r->granted = true;
54                                     KEYWORD_DENIED_EXECUTE_HANDLER, handler);          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  /**  /**
61   * ccs_audit_domain_creation_log - Audit domain creation log.   * ccs_audit_domain_creation_log - Audit domain creation log.
62   *   *
  * @domain:  Pointer to "struct ccs_domain_info".  
  *  
63   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
64   */   */
65  static int ccs_audit_domain_creation_log(struct ccs_domain_info *domain)  static int ccs_audit_domain_creation_log(void)
66  {  {
         int error;  
67          struct ccs_request_info r;          struct ccs_request_info r;
68          ccs_init_request_info(&r, domain, CCS_MAC_FOR_FILE);          ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE);
69          error = ccs_write_audit_log(false, &r, "use_profile %u\n", r.profile);          r.granted = false;
70          return error;          return ccs_write_log(&r, "use_profile %u\n", r.profile);
71  }  }
72    
 /* The list for "struct ccs_domain_initializer_entry". */  
 LIST_HEAD(ccs_domain_initializer_list);  
   
73  /**  /**
74   * ccs_update_domain_initializer_entry - Update "struct ccs_domain_initializer_entry" list.   * ccs_update_policy - Update an entry for exception policy.
75   *   *
76   * @domainname: The name of domain. May be NULL.   * @new_entry:       Pointer to "struct ccs_acl_info".
77   * @program:    The name of program.   * @size:            Size of @new_entry in bytes.
78   * @is_not:     True if it is "no_initialize_domain" entry.   * @is_delete:       True if it is a delete request.
79   * @is_delete:  True if it is a delete request.   * @list:            Pointer to "struct list_head".
80     * @check_duplicate: Callback function to find duplicated entry.
81   *   *
82   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
83     *
84     * Caller holds ccs_read_lock().
85   */   */
86  static int ccs_update_domain_initializer_entry(const char *domainname,  int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
87                                                 const char *program,                        bool is_delete, struct list_head *list,
88                                                 const bool is_not,                        bool (*check_duplicate) (const struct ccs_acl_head *,
89                                                 const bool is_delete)                                                 const struct ccs_acl_head *))
90  {  {
         struct ccs_domain_initializer_entry *entry = NULL;  
         struct ccs_domain_initializer_entry *ptr;  
         const struct ccs_path_info *saved_program;  
         const struct ccs_path_info *saved_domainname = NULL;  
91          int error = is_delete ? -ENOENT : -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
92          bool is_last_name = false;          struct ccs_acl_head *entry;
93          if (!ccs_is_correct_path(program, 1, -1, -1))          if (mutex_lock_interruptible(&ccs_policy_lock))
                 return -EINVAL; /* No patterns allowed. */  
         if (domainname) {  
                 if (!ccs_is_domain_def(domainname) &&  
                     ccs_is_correct_path(domainname, 1, -1, -1))  
                         is_last_name = true;  
                 else if (!ccs_is_correct_domain(domainname))  
                         return -EINVAL;  
                 saved_domainname = ccs_get_name(domainname);  
                 if (!saved_domainname)  
                         return -ENOMEM;  
         }  
         saved_program = ccs_get_name(program);  
         if (!saved_program) {  
                 ccs_put_name(saved_domainname);  
94                  return -ENOMEM;                  return -ENOMEM;
95          }          list_for_each_entry_rcu(entry, list, list) {
96          if (!is_delete)                  if (!check_duplicate(entry, new_entry))
                 entry = kzalloc(sizeof(*entry), GFP_KERNEL);  
         mutex_lock(&ccs_policy_lock);  
         list_for_each_entry_rcu(ptr, &ccs_domain_initializer_list, list) {  
                 if (ptr->is_not != is_not ||  
                     ptr->domainname != saved_domainname ||  
                     ptr->program != saved_program)  
97                          continue;                          continue;
98                  ptr->is_deleted = is_delete;                  entry->is_deleted = is_delete;
99                  error = 0;                  error = 0;
100                  break;                  break;
101          }          }
102          if (!is_delete && error && ccs_memory_ok(entry, sizeof(*entry))) {          if (error && !is_delete) {
103                  entry->domainname = saved_domainname;                  entry = ccs_commit_ok(new_entry, size);
104                  saved_domainname = NULL;                  if (entry) {
105                  entry->program = saved_program;                          list_add_tail_rcu(&entry->list, list);
106                  saved_program = NULL;                          error = 0;
107                  entry->is_not = is_not;                  }
                 entry->is_last_name = is_last_name;  
                 list_add_tail_rcu(&entry->list, &ccs_domain_initializer_list);  
                 entry = NULL;  
                 error = 0;  
108          }          }
109          mutex_unlock(&ccs_policy_lock);          mutex_unlock(&ccs_policy_lock);
         ccs_put_name(saved_domainname);  
         ccs_put_name(saved_program);  
         kfree(entry);  
110          return error;          return error;
111  }  }
112    
113  /**  static void ccs_delete_type(struct ccs_domain_info *domain, u8 type)
  * ccs_read_domain_initializer_policy - Read "struct ccs_domain_initializer_entry" list.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  *  
  * Caller holds ccs_read_lock().  
  */  
 bool ccs_read_domain_initializer_policy(struct ccs_io_buffer *head)  
114  {  {
115          struct list_head *pos;          struct ccs_acl_info *ptr;
116          bool done = true;          list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
117          ccs_check_read_lock();                  if (ptr->type == type)
118          list_for_each_cookie(pos, head->read_var2,                          ptr->is_deleted = true;
                              &ccs_domain_initializer_list) {  
                 const char *no;  
                 const char *from = "";  
                 const char *domain = "";  
                 struct ccs_domain_initializer_entry *ptr;  
                 ptr = list_entry(pos, struct ccs_domain_initializer_entry,  
                                   list);  
                 if (ptr->is_deleted)  
                         continue;  
                 no = ptr->is_not ? "no_" : "";  
                 if (ptr->domainname) {  
                         from = " from ";  
                         domain = ptr->domainname->name;  
                 }  
                 done = ccs_io_printf(head,  
                                      "%s" KEYWORD_INITIALIZE_DOMAIN "%s%s%s\n",  
                                      no, ptr->program->name, from, domain);  
                 if (!done)  
                         break;  
119          }          }
         return done;  
120  }  }
121    
122  /**  /**
123   * ccs_write_domain_initializer_policy - Write "struct ccs_domain_initializer_entry" list.   * ccs_update_domain - Update an entry for domain policy.
124   *   *
125   * @data:      String to parse.   * @new_entry:       Pointer to "struct ccs_acl_info".
126   * @is_not:    True if it is "no_initialize_domain" entry.   * @size:            Size of @new_entry in bytes.
127   * @is_delete: True if it is a delete request.   * @is_delete:       True if it is a delete request.
128     * @domain:          Pointer to "struct ccs_domain_info".
129     * @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.   * Returns 0 on success, negative value otherwise.
  */  
 int ccs_write_domain_initializer_policy(char *data, const bool is_not,  
                                         const bool is_delete)  
 {  
         char *cp = strstr(data, " from ");  
         if (cp) {  
                 *cp = '\0';  
                 return ccs_update_domain_initializer_entry(cp + 6, data,  
                                                            is_not, is_delete);  
         }  
         return ccs_update_domain_initializer_entry(NULL, data, is_not,  
                                                    is_delete);  
 }  
   
 /**  
  * ccs_is_domain_initializer - Check whether the given program causes domainname reinitialization.  
  *  
  * @domainname: The name of domain.  
  * @program:    The name of program.  
  * @last_name:  The last component of @domainname.  
  *  
  * Returns true if executing @program reinitializes domain transition,  
  * false otherwise.  
133   *   *
134   * Caller holds ccs_read_lock().   * Caller holds ccs_read_lock().
135   */   */
136  static bool ccs_is_domain_initializer(const struct ccs_path_info *domainname,  int ccs_update_domain(struct ccs_acl_info *new_entry, const int size,
137                                        const struct ccs_path_info *program,                        bool is_delete, struct ccs_domain_info *domain,
138                                        const struct ccs_path_info *last_name)                        bool (*check_duplicate) (const struct ccs_acl_info *,
139  {                                                 const struct ccs_acl_info *),
140          struct ccs_domain_initializer_entry *ptr;                        bool (*merge_duplicate) (struct ccs_acl_info *,
141          bool flag = false;                                                 struct ccs_acl_info *,
142          ccs_check_read_lock();                                                 const bool))
143          list_for_each_entry_rcu(ptr, &ccs_domain_initializer_list, list) {  {
144                  if (ptr->is_deleted)          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;                          continue;
159                  if (ptr->domainname) {                  if (exclusive)
160                          if (!ptr->is_last_name) {                          ccs_delete_type(domain, type);
161                                  if (ptr->domainname != domainname)                  if (merge_duplicate)
162                                          continue;                          entry->is_deleted = merge_duplicate(entry, new_entry,
163                          } else {                                                              is_delete);
164                                  if (ccs_pathcmp(ptr->domainname, last_name))                  else
165                                          continue;                          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                    if (entry) {
172                            if (exclusive)
173                                    ccs_delete_type(domain, type);
174                            if (entry->cond)
175                                    atomic_inc(&entry->cond->head.users);
176                            list_add_tail_rcu(&entry->list,
177                                              &domain->acl_info_list);
178                            error = 0;
179                  }                  }
180                  if (ccs_pathcmp(ptr->program, program))          }
181            mutex_unlock(&ccs_policy_lock);
182            return error;
183    }
184    
185    void ccs_check_acl(struct ccs_request_info *r,
186                       bool (*check_entry) (const struct ccs_request_info *,
187                                            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;                          continue;
195                  if (ptr->is_not) {                  if (check_entry(r, ptr) && ccs_condition(r, ptr->cond)) {
196                          flag = false;                          r->cond = ptr->cond;
197                          break;                          r->granted = true;
198                            return;
199                  }                  }
                 flag = true;  
200          }          }
201          return flag;          if (domain != &ccs_global_domain &&
202                !domain->flags[CCS_DIF_IGNORE_GLOBAL]) {
203                    domain = &ccs_global_domain;
204                    goto retry;
205            }
206            r->granted = false;
207  }  }
208    
209  /* The list for "struct ccs_domain_keeper_entry". */  static bool ccs_same_transition_control(const struct ccs_acl_head *a,
210  LIST_HEAD(ccs_domain_keeper_list);                                          const struct ccs_acl_head *b)
211    {
212            const struct ccs_transition_control *p1 = container_of(a, typeof(*p1),
213                                                                   head);
214            const struct ccs_transition_control *p2 = container_of(b, typeof(*p2),
215                                                                   head);
216            return p1->type == p2->type && p1->is_last_name == p2->is_last_name
217                    && p1->domainname == p2->domainname
218                    && p1->program == p2->program;
219    }
220    
221  /**  /**
222   * ccs_update_domain_keeper_entry - Update "struct ccs_domain_keeper_entry" list.   * ccs_update_transition_control_entry - Update "struct ccs_transition_control" list.
223   *   *
224   * @domainname: The name of domain.   * @domainname: The name of domain. Maybe NULL.
225   * @program:    The name of program. May be NULL.   * @program:    The name of program. Maybe NULL.
226   * @is_not:     True if it is "no_keep_domain" entry.   * @type:       Type of transition.
227   * @is_delete:  True if it is a delete request.   * @is_delete:  True if it is a delete request.
228   *   *
229   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
230   */   */
231  static int ccs_update_domain_keeper_entry(const char *domainname,  static int ccs_update_transition_control_entry(const char *domainname,
232                                            const char *program,                                                 const char *program,
233                                            const bool is_not,                                                 const u8 type,
234                                            const bool is_delete)                                                 const bool is_delete)
235  {  {
236          struct ccs_domain_keeper_entry *entry = NULL;          struct ccs_transition_control e = { .type = type };
         struct ccs_domain_keeper_entry *ptr;  
         const struct ccs_path_info *saved_domainname;  
         const struct ccs_path_info *saved_program = NULL;  
237          int error = is_delete ? -ENOENT : -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
238          bool is_last_name = false;          if (program && strcmp(program, "any")) {
239          if (!ccs_is_domain_def(domainname) &&                  if (!ccs_correct_path(program))
             ccs_is_correct_path(domainname, 1, -1, -1))  
                 is_last_name = true;  
         else if (!ccs_is_correct_domain(domainname))  
                 return -EINVAL;  
         if (program) {  
                 if (!ccs_is_correct_path(program, 1, -1, -1))  
240                          return -EINVAL;                          return -EINVAL;
241                  saved_program = ccs_get_name(program);                  e.program = ccs_get_name(program);
242                  if (!saved_program)                  if (!e.program)
243                          return -ENOMEM;                          goto out;
         }  
         saved_domainname = ccs_get_name(domainname);  
         if (!saved_domainname) {  
                 ccs_put_name(saved_program);  
                 return -ENOMEM;  
         }  
         if (!is_delete)  
                 entry = kzalloc(sizeof(*entry), GFP_KERNEL);  
         mutex_lock(&ccs_policy_lock);  
         list_for_each_entry_rcu(ptr, &ccs_domain_keeper_list, list) {  
                 if (ptr->is_not != is_not ||  
                     ptr->domainname != saved_domainname ||  
                     ptr->program != saved_program)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 break;  
244          }          }
245          if (!is_delete && error && ccs_memory_ok(entry, sizeof(*entry))) {          if (domainname && strcmp(domainname, "any")) {
246                  entry->domainname = saved_domainname;                  if (!ccs_correct_domain(domainname)) {
247                  saved_domainname = NULL;                          if (!ccs_correct_path(domainname))
248                  entry->program = saved_program;                                  goto out;
249                  saved_program = NULL;                          e.is_last_name = true;
250                  entry->is_not = is_not;                  }
251                  entry->is_last_name = is_last_name;                  e.domainname = ccs_get_name(domainname);
252                  list_add_tail_rcu(&entry->list, &ccs_domain_keeper_list);                  if (!e.domainname)
253                  entry = NULL;                          goto out;
                 error = 0;  
254          }          }
255          mutex_unlock(&ccs_policy_lock);          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
256          ccs_put_name(saved_domainname);                                    &ccs_policy_list[CCS_ID_TRANSITION_CONTROL],
257          ccs_put_name(saved_program);                                    ccs_same_transition_control);
258          kfree(entry);   out:
259            ccs_put_name(e.domainname);
260            ccs_put_name(e.program);
261          return error;          return error;
262  }  }
263    
264  /**  /**
265   * ccs_write_domain_keeper_policy - Write "struct ccs_domain_keeper_entry" list.   * ccs_write_transition_control - Write "struct ccs_transition_control" list.
266   *   *
267   * @data:      String to parse.   * @data:      String to parse.
  * @is_not:    True if it is "no_keep_domain" entry.  
268   * @is_delete: True if it is a delete request.   * @is_delete: True if it is a delete request.
269     * @type:      Type of this entry.
270   *   *
271     * Returns 0 on success, negative value otherwise.
272   */   */
273  int ccs_write_domain_keeper_policy(char *data, const bool is_not,  int ccs_write_transition_control(char *data, const bool is_delete,
274                                     const bool is_delete)                                   const u8 type)
 {  
         char *cp = strstr(data, " from ");  
         if (cp) {  
                 *cp = '\0';  
                 return ccs_update_domain_keeper_entry(cp + 6, data,  
                                                       is_not, is_delete);  
         }  
         return ccs_update_domain_keeper_entry(data, NULL, is_not, is_delete);  
 }  
   
 /**  
  * ccs_read_domain_keeper_policy - Read "struct ccs_domain_keeper_entry" list.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  *  
  * Caller holds ccs_read_lock().  
  */  
 bool ccs_read_domain_keeper_policy(struct ccs_io_buffer *head)  
275  {  {
276          struct list_head *pos;          char *domainname = strstr(data, " from ");
277          bool done = true;          if (domainname) {
278          ccs_check_read_lock();                  *domainname = '\0';
279          list_for_each_cookie(pos, head->read_var2,                  domainname += 6;
280                               &ccs_domain_keeper_list) {          } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP ||
281                  struct ccs_domain_keeper_entry *ptr;                     type == CCS_TRANSITION_CONTROL_KEEP) {
282                  const char *no;                  domainname = data;
283                  const char *from = "";                  data = NULL;
                 const char *program = "";  
                 ptr = list_entry(pos, struct ccs_domain_keeper_entry, list);  
                 if (ptr->is_deleted)  
                         continue;  
                 no = ptr->is_not ? "no_" : "";  
                 if (ptr->program) {  
                         from = " from ";  
                         program = ptr->program->name;  
                 }  
                 done = ccs_io_printf(head,  
                                      "%s" KEYWORD_KEEP_DOMAIN "%s%s%s\n", no,  
                                      program, from, ptr->domainname->name);  
                 if (!done)  
                         break;  
284          }          }
285          return done;          return ccs_update_transition_control_entry(domainname, data, type,
286                                                       is_delete);
287  }  }
288    
289  /**  /**
290   * ccs_is_domain_keeper - Check whether the given program causes domain transition suppression.   * ccs_transition_type - Get domain transition type.
291   *   *
292   * @domainname: The name of domain.   * @domainname: The name of domain.
293   * @program:    The name of program.   * @program:    The name of program.
  * @last_name:  The last component of @domainname.  
294   *   *
295   * Returns true if executing @program supresses domain transition,   * Returns CCS_TRANSITION_CONTROL_INITIALIZE if executing @program
296   * false otherwise.   * reinitializes domain transition, CCS_TRANSITION_CONTROL_KEEP if executing
297     * @program suppresses domain transition, others otherwise.
298   *   *
299   * Caller holds ccs_read_lock().   * Caller holds ccs_read_lock().
300   */   */
301  static bool ccs_is_domain_keeper(const struct ccs_path_info *domainname,  static u8 ccs_transition_type(const struct ccs_path_info *domainname,
302                                   const struct ccs_path_info *program,                                const struct ccs_path_info *program)
303                                   const struct ccs_path_info *last_name)  {
304  {          const struct ccs_transition_control *ptr;
305          struct ccs_domain_keeper_entry *ptr;          const char *last_name = ccs_last_word(domainname->name);
306          bool flag = false;          u8 type;
307          ccs_check_read_lock();          for (type = 0; type < CCS_MAX_TRANSITION_TYPE; type++) {
308          list_for_each_entry_rcu(ptr, &ccs_domain_keeper_list, list) {   next:
309                  if (ptr->is_deleted)                  list_for_each_entry_rcu(ptr, &ccs_policy_list
310                          continue;                                          [CCS_ID_TRANSITION_CONTROL],
311                  if (!ptr->is_last_name) {                                          head.list) {
312                          if (ptr->domainname != domainname)                          if (ptr->head.is_deleted || ptr->type != type)
313                                  continue;                                  continue;
314                  } else {                          if (ptr->domainname) {
315                          if (ccs_pathcmp(ptr->domainname, last_name))                                  if (!ptr->is_last_name) {
316                                            if (ptr->domainname != domainname)
317                                                    continue;
318                                    } else {
319                                            /*
320                                             * Use direct strcmp() since this is
321                                             * unlikely used.
322                                             */
323                                            if (strcmp(ptr->domainname->name,
324                                                       last_name))
325                                                    continue;
326                                    }
327                            }
328                            if (ptr->program && ccs_pathcmp(ptr->program, program))
329                                  continue;                                  continue;
330                            if (type == CCS_TRANSITION_CONTROL_NO_INITIALIZE) {
331                                    /*
332                                     * Do not check for initialize_domain if
333                                     * no_initialize_domain matched.
334                                     */
335                                    type = CCS_TRANSITION_CONTROL_NO_KEEP;
336                                    goto next;
337                            }
338                            goto done;
339                  }                  }
                 if (ptr->program && ccs_pathcmp(ptr->program, program))  
                         continue;  
                 if (ptr->is_not) {  
                         flag = false;  
                         break;  
                 }  
                 flag = true;  
340          }          }
341          return flag;   done:
342            return type;
343  }  }
344    
345  /* The list for "struct ccs_aggregator_entry". */  static bool ccs_same_aggregator(const struct ccs_acl_head *a,
346  LIST_HEAD(ccs_aggregator_list);                                  const struct ccs_acl_head *b)
347    {
348            const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head);
349            const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head);
350            return p1->original_name == p2->original_name &&
351                    p1->aggregated_name == p2->aggregated_name;
352    }
353    
354  /**  /**
355   * ccs_update_aggregator_entry - Update "struct ccs_aggregator_entry" list.   * ccs_update_aggregator_entry - Update "struct ccs_aggregator" list.
356   *   *
357   * @original_name:   The original program's name.   * @original_name:   The original program's name.
358   * @aggregated_name: The aggregated program's name.   * @aggregated_name: The aggregated program's name.
# Line 480  static int ccs_update_aggregator_entry(c Line 364  static int ccs_update_aggregator_entry(c
364                                         const char *aggregated_name,                                         const char *aggregated_name,
365                                         const bool is_delete)                                         const bool is_delete)
366  {  {
367          struct ccs_aggregator_entry *entry = NULL;          struct ccs_aggregator e = { };
         struct ccs_aggregator_entry *ptr;  
         const struct ccs_path_info *saved_original_name;  
         const struct ccs_path_info *saved_aggregated_name;  
368          int error = is_delete ? -ENOENT : -ENOMEM;          int error = is_delete ? -ENOENT : -ENOMEM;
369          if (!ccs_is_correct_path(original_name, 1, 0, -1) ||          if (!ccs_correct_path(original_name) ||
370              !ccs_is_correct_path(aggregated_name, 1, -1, -1))              !ccs_correct_path(aggregated_name))
371                  return -EINVAL;                  return -EINVAL;
372          saved_original_name = ccs_get_name(original_name);          e.original_name = ccs_get_name(original_name);
373          saved_aggregated_name = ccs_get_name(aggregated_name);          e.aggregated_name = ccs_get_name(aggregated_name);
374          if (!saved_original_name || !saved_aggregated_name) {          if (!e.original_name || !e.aggregated_name ||
375                  ccs_put_name(saved_original_name);              e.aggregated_name->is_patterned) /* No patterns allowed. */
376                  ccs_put_name(saved_aggregated_name);                  goto out;
377                  return -ENOMEM;          error = ccs_update_policy(&e.head, sizeof(e), is_delete,
378          }                                    &ccs_policy_list[CCS_ID_AGGREGATOR],
379          if (!is_delete)                                    ccs_same_aggregator);
380                  entry = kzalloc(sizeof(*entry), GFP_KERNEL);   out:
381          mutex_lock(&ccs_policy_lock);          ccs_put_name(e.original_name);
382          list_for_each_entry_rcu(ptr, &ccs_aggregator_list, list) {          ccs_put_name(e.aggregated_name);
                 if (ptr->original_name != saved_original_name ||  
                     ptr->aggregated_name != saved_aggregated_name)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 break;  
         }  
         if (!is_delete && error && ccs_memory_ok(entry, sizeof(*entry))) {  
                 entry->original_name = saved_original_name;  
                 saved_original_name = NULL;  
                 entry->aggregated_name = saved_aggregated_name;  
                 saved_aggregated_name = NULL;  
                 list_add_tail_rcu(&entry->list, &ccs_aggregator_list);  
                 entry = NULL;  
                 error = 0;  
         }  
         mutex_unlock(&ccs_policy_lock);  
         ccs_put_name(saved_original_name);  
         ccs_put_name(saved_aggregated_name);  
         kfree(entry);  
383          return error;          return error;
384  }  }
385    
386  /**  /**
387   * ccs_read_aggregator_policy - Read "struct ccs_aggregator_entry" list.   * ccs_write_aggregator - Write "struct ccs_aggregator" list.
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns true on success, false otherwise.  
  *  
  * Caller holds ccs_read_lock().  
  */  
 bool ccs_read_aggregator_policy(struct ccs_io_buffer *head)  
 {  
         struct list_head *pos;  
         bool done = true;  
         ccs_check_read_lock();  
         list_for_each_cookie(pos, head->read_var2, &ccs_aggregator_list) {  
                 struct ccs_aggregator_entry *ptr;  
                 ptr = list_entry(pos, struct ccs_aggregator_entry, list);  
                 if (ptr->is_deleted)  
                         continue;  
                 done = ccs_io_printf(head, KEYWORD_AGGREGATOR "%s %s\n",  
                                      ptr->original_name->name,  
                                      ptr->aggregated_name->name);  
                 if (!done)  
                         break;  
         }  
         return done;  
 }  
   
 /**  
  * ccs_write_aggregator_policy - Write "struct ccs_aggregator_entry" list.  
388   *   *
389   * @data:      String to parse.   * @data:      String to parse.
390   * @is_delete: True if it is a delete request.   * @is_delete: True if it is a delete request.
391   *   *
392   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
393   */   */
394  int ccs_write_aggregator_policy(char *data, const bool is_delete)  int ccs_write_aggregator(char *data, const bool is_delete)
395  {  {
396          char *w[2];          char *w[2];
397          if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])          if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
398                  return -EINVAL;                  return -EINVAL;
399          return ccs_update_aggregator_entry(w[0], w[1], is_delete);          return ccs_update_aggregator_entry(w[0], w[1], is_delete);
400  }  }
401    
# Line 581  int ccs_delete_domain(char *domainname) Line 414  int ccs_delete_domain(char *domainname)
414          struct ccs_path_info name;          struct ccs_path_info name;
415          name.name = domainname;          name.name = domainname;
416          ccs_fill_path_info(&name);          ccs_fill_path_info(&name);
417          mutex_lock(&ccs_policy_lock);          if (mutex_lock_interruptible(&ccs_policy_lock))
418                    return 0;
419          /* Is there an active domain? */          /* Is there an active domain? */
420          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
421                  /* Never delete ccs_kernel_domain */                  /* Never delete ccs_kernel_domain */
# Line 598  int ccs_delete_domain(char *domainname) Line 432  int ccs_delete_domain(char *domainname)
432  }  }
433    
434  /**  /**
435   * ccs_find_or_assign_new_domain - Create a domain.   * ccs_assign_domain - Create a domain.
436   *   *
437   * @domainname: The name of domain.   * @domainname: The name of domain.
438   * @profile:    Profile number to assign if the domain was newly created.   * @profile:    Profile number to assign if the domain was newly created.
439   *   *
440   * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.   * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
441   */   */
442  struct ccs_domain_info *ccs_find_or_assign_new_domain(const char *domainname,  struct ccs_domain_info *ccs_assign_domain(const char *domainname,
443                                                        const u8 profile)                                            const u8 profile)
444  {  {
445          struct ccs_domain_info *entry;          struct ccs_domain_info e = { };
446          struct ccs_domain_info *domain;          struct ccs_domain_info *entry = NULL;
         const struct ccs_path_info *saved_domainname;  
447          bool found = false;          bool found = false;
448    
449          if (!ccs_is_correct_domain(domainname))          if (!ccs_correct_domain(domainname))
450                  return NULL;                  return NULL;
451          saved_domainname = ccs_get_name(domainname);          e.profile = profile;
452          if (!saved_domainname)          e.domainname = ccs_get_name(domainname);
453            if (!e.domainname)
454                  return NULL;                  return NULL;
455          entry = kzalloc(sizeof(*entry), GFP_KERNEL);          if (mutex_lock_interruptible(&ccs_policy_lock))
456          mutex_lock(&ccs_policy_lock);                  goto out;
457          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {          list_for_each_entry_rcu(entry, &ccs_domain_list, list) {
458                  if (domain->is_deleted ||                  if (entry->is_deleted ||
459                      ccs_pathcmp(saved_domainname, domain->domainname))                      ccs_pathcmp(e.domainname, entry->domainname))
460                          continue;                          continue;
461                  found = true;                  found = true;
462                  break;                  break;
463          }          }
464          if (!found && ccs_memory_ok(entry, sizeof(*entry))) {          if (!found) {
465                  INIT_LIST_HEAD(&entry->acl_info_list);                  entry = ccs_commit_ok(&e, sizeof(e));
466                  entry->domainname = saved_domainname;                  if (entry) {
467                  saved_domainname = NULL;                          INIT_LIST_HEAD(&entry->acl_info_list);
468                  entry->profile = profile;                          list_add_tail_rcu(&entry->list, &ccs_domain_list);
469                  list_add_tail_rcu(&entry->list, &ccs_domain_list);                          found = true;
                 domain = entry;  
                 entry = NULL;  
                 found = true;  
         }  
         mutex_unlock(&ccs_policy_lock);  
         ccs_put_name(saved_domainname);  
         kfree(entry);  
         return found ? domain : NULL;  
 }  
   
 /**  
  * ccs_get_argv0 - Get argv[0].  
  *  
  * @ee: Pointer to "struct ccs_execve_entry".  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool ccs_get_argv0(struct ccs_execve_entry *ee)  
 {  
         struct linux_binprm *bprm = ee->bprm;  
         char *arg_ptr = ee->tmp;  
         int arg_len = 0;  
         unsigned long pos = bprm->p;  
         int offset = pos % PAGE_SIZE;  
         bool done = false;  
         if (!bprm->argc)  
                 goto out;  
         while (1) {  
                 if (!ccs_dump_page(bprm, pos, &ee->dump))  
                         goto out;  
                 pos += PAGE_SIZE - offset;  
                 /* Read. */  
                 while (offset < PAGE_SIZE) {  
                         const char *kaddr = ee->dump.data;  
                         const unsigned char c = kaddr[offset++];  
                         if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {  
                                 if (c == '\\') {  
                                         arg_ptr[arg_len++] = '\\';  
                                         arg_ptr[arg_len++] = '\\';  
                                 } else if (c == '/') {  
                                         arg_len = 0;  
                                 } else if (c > ' ' && c < 127) {  
                                         arg_ptr[arg_len++] = c;  
                                 } else {  
                                         arg_ptr[arg_len++] = '\\';  
                                         arg_ptr[arg_len++] = (c >> 6) + '0';  
                                         arg_ptr[arg_len++]  
                                                 = ((c >> 3) & 7) + '0';  
                                         arg_ptr[arg_len++] = (c & 7) + '0';  
                                 }  
                         } else {  
                                 arg_ptr[arg_len] = '\0';  
                                 done = true;  
                                 break;  
                         }  
470                  }                  }
                 offset = 0;  
                 if (done)  
                         break;  
471          }          }
472          return true;          mutex_unlock(&ccs_policy_lock);
473   out:   out:
474          return false;          ccs_put_name(e.domainname);
475            return found ? entry : NULL;
476  }  }
477    
478  /**  /**
479   * ccs_find_next_domain - Find a domain.   * ccs_find_next_domain - Find a domain.
480   *   *
481   * @ee: Pointer to "struct ccs_execve_entry".   * @ee: Pointer to "struct ccs_execve".
482   *   *
483   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
484   *   *
485   * Caller holds ccs_read_lock().   * Caller holds ccs_read_lock().
486   */   */
487  static int ccs_find_next_domain(struct ccs_execve_entry *ee)  static int ccs_find_next_domain(struct ccs_execve *ee)
488  {  {
489          struct ccs_request_info *r = &ee->r;          struct ccs_request_info *r = &ee->r;
490          const struct ccs_path_info *handler = ee->handler;          const struct ccs_path_info *handler = ee->handler;
491          struct ccs_domain_info *domain = NULL;          struct ccs_domain_info *domain = NULL;
492          const char *old_domain_name = r->domain->domainname->name;          struct ccs_domain_info * const old_domain = ccs_current_domain();
493          struct linux_binprm *bprm = ee->bprm;          struct linux_binprm *bprm = ee->bprm;
494          const u8 mode = r->mode;          struct task_struct *task = current;
495          const bool is_enforce = (mode == 3);          const u32 ccs_flags = task->ccs_flags;
496          const u32 ccs_flags = current->ccs_flags;          struct ccs_path_info rn = { }; /* real name */
         char *new_domain_name = NULL;  
         struct ccs_path_info rn; /* real name */  
         struct ccs_path_info ln; /* last name */  
497          int retval;          int retval;
498          ccs_check_read_lock();          bool need_kfree = false;
499            bool domain_created = false;
500   retry:   retry:
501          current->ccs_flags = ccs_flags;          current->ccs_flags = ccs_flags;
502          r->cond = NULL;          r->cond = NULL;
503            if (need_kfree) {
504                    kfree(rn.name);
505                    need_kfree = false;
506            }
507    
508          /* Get symlink's pathname of program. */          /* Get symlink's pathname of program. */
509          retval = ccs_symlink_path(bprm->filename, ee);          retval = ccs_symlink_path(bprm->filename, &rn);
510          if (retval < 0)          if (retval < 0)
511                  goto out;                  goto out;
512            need_kfree = true;
         rn.name = ee->program_path;  
         ccs_fill_path_info(&rn);  
         ln.name = ccs_get_last_name(r->domain);  
         ccs_fill_path_info(&ln);  
513    
514          if (handler) {          if (handler) {
515                  if (ccs_pathcmp(&rn, handler)) {                  if (ccs_pathcmp(&rn, handler)) {
# Line 746  static int ccs_find_next_domain(struct c Line 522  static int ccs_find_next_domain(struct c
522                          }                          }
523                          goto out;                          goto out;
524                  }                  }
525                  goto calculate_domain;          } else {
526          }                  struct ccs_aggregator *ptr;
527                    /* Check 'aggregator' directive. */
528          /* Compare basename of program_path and argv[0] */                  list_for_each_entry_rcu(ptr,
529          r->mode = ccs_check_flags(r->domain, CCS_MAC_FOR_ARGV0);                                          &ccs_policy_list[CCS_ID_AGGREGATOR],
530          if (bprm->argc > 0 && r->mode) {                                          head.list) {
531                  char *base_argv0 = ee->tmp;                          if (ptr->head.is_deleted ||
                 const char *base_filename;  
                 retval = -ENOMEM;  
                 if (!ccs_get_argv0(ee))  
                         goto out;  
                 base_filename = strrchr(ee->program_path, '/');  
                 if (!base_filename)  
                         base_filename = ee->program_path;  
                 else  
                         base_filename++;  
                 if (strcmp(base_argv0, base_filename)) {  
                         retval = ccs_check_argv0_perm(r, &rn, base_argv0);  
                         if (retval == 1)  
                                 goto retry;  
                         if (retval < 0)  
                                 goto out;  
                 }  
         }  
   
         /* Check 'aggregator' directive. */  
         {  
                 struct ccs_aggregator_entry *ptr;  
                 /* Is this program allowed to be aggregated? */  
                 list_for_each_entry_rcu(ptr, &ccs_aggregator_list, list) {  
                         if (ptr->is_deleted ||  
532                              !ccs_path_matches_pattern(&rn, ptr->original_name))                              !ccs_path_matches_pattern(&rn, ptr->original_name))
533                                  continue;                                  continue;
534                          strncpy(ee->program_path, ptr->aggregated_name->name,                          kfree(rn.name);
535                                  CCS_MAX_PATHNAME_LEN - 1);                          need_kfree = false;
536                          ccs_fill_path_info(&rn);                          /* This is OK because it is read only. */
537                            rn = *ptr->aggregated_name;
538                          break;                          break;
539                  }                  }
         }  
540    
541          /* Check execute permission. */                  /* Check execute permission. */
542          r->mode = mode;                  retval = ccs_path_permission(r, CCS_TYPE_EXECUTE, &rn);
543          retval = ccs_check_exec_perm(r, &rn);                  if (retval == CCS_RETRY_REQUEST)
544          if (retval == 1)                          goto retry;
545                  goto retry;                  if (retval < 0)
546          if (retval < 0)                          goto out;
547                  goto out;          }
548    
549   calculate_domain:          /* Calculate domain to transit to. */
550          new_domain_name = ee->tmp;          switch (ccs_transition_type(old_domain->domainname, &rn)) {
551          if (ccs_is_domain_initializer(r->domain->domainname, &rn, &ln)) {          case CCS_TRANSITION_CONTROL_INITIALIZE:
552                  /* Transit to the child of ccs_kernel_domain domain. */                  /* Transit to the child of ccs_kernel_domain domain. */
553                  snprintf(new_domain_name, CCS_EXEC_TMPSIZE - 1,                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, CCS_ROOT_NAME " " "%s",
554                           ROOT_NAME " " "%s", ee->program_path);                           rn.name);
555          } else if (r->domain == &ccs_kernel_domain && !ccs_policy_loaded) {                  break;
556                  /*          case CCS_TRANSITION_CONTROL_KEEP:
                  * Needn't to transit from kernel domain before starting  
                  * /sbin/init. But transit from kernel domain if executing  
                  * initializers because they might start before /sbin/init.  
                  */  
                 domain = r->domain;  
         } else if (ccs_is_domain_keeper(r->domain->domainname, &rn, &ln)) {  
557                  /* Keep current domain. */                  /* Keep current domain. */
558                  domain = r->domain;                  domain = old_domain;
559          } else {                  break;
560                  /* Normal domain transition. */          default:
561                  snprintf(new_domain_name, CCS_EXEC_TMPSIZE - 1,                  if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) {
562                           "%s %s", old_domain_name, ee->program_path);                          /*
563                             * Needn't to transit from kernel domain before
564                             * starting /sbin/init. But transit from kernel domain
565                             * if executing initializers because they might start
566                             * before /sbin/init.
567                             */
568                            domain = old_domain;
569                    } else {
570                            /* Normal domain transition. */
571                            snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
572                                     old_domain->domainname->name, rn.name);
573                    }
574                    break;
575          }          }
576          if (domain || strlen(new_domain_name) >= CCS_MAX_PATHNAME_LEN)          if (domain || strlen(ee->tmp) >= CCS_EXEC_TMPSIZE - 10)
577                  goto done;                  goto done;
578          domain = ccs_find_domain(new_domain_name);          domain = ccs_find_domain(ee->tmp);
579          if (domain)          if (domain)
580                  goto done;                  goto done;
581          if (is_enforce) {          if (r->mode == CCS_CONFIG_ENFORCING) {
582                  int error = ccs_check_supervisor(r,                  int error = ccs_supervisor(r, "# wants to create domain\n"
583                                                   "# wants to create domain\n"                                             "%s\n", ee->tmp);
584                                                   "%s\n", new_domain_name);                  if (error == CCS_RETRY_REQUEST)
                 if (error == 1)  
585                          goto retry;                          goto retry;
586                  if (error < 0)                  if (error < 0)
587                          goto done;                          goto done;
588          }          }
589          domain = ccs_find_or_assign_new_domain(new_domain_name, r->profile);          domain = ccs_assign_domain(ee->tmp, r->profile);
590          if (domain)          if (domain)
591                  ccs_audit_domain_creation_log(r->domain);                  domain_created = true;
592   done:   done:
593          if (!domain) {          if (!domain) {
594                  printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",                  retval = (r->mode == CCS_CONFIG_ENFORCING) ? -EPERM : 0;
595                         new_domain_name);                  if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) {
596                  if (is_enforce)                          old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true;
597                          retval = -EPERM;                          r->granted = false;
598                  else {                          ccs_write_log(r, CCS_KEYWORD_TRANSITION_FAILED "\n");
599                          retval = 0;                          printk(KERN_WARNING
600                          r->domain->domain_transition_failed = true;                                 "ERROR: Domain '%s' not defined.\n", ee->tmp);
601                  }                  }
602          } else {          } else {
603                  retval = 0;                  retval = 0;
604          }          }
605   out:          if (!retval && handler)
606                    ccs_audit_execute_handler_log(ee);
607            /*
608             * Tell GC that I started execve().
609             * Also, tell open_exec() to check read permission.
610             */
611            task->ccs_flags |= CCS_TASK_IS_IN_EXECVE;
612            /*
613             * Make task->ccs_flags visible to GC before changing
614             * task->ccs_domain_info .
615             */
616            smp_mb();
617            /*
618             * Proceed to the next domain in order to allow reaching via PID.
619             * It will be reverted if execve() failed. Reverting is not good.
620             * But it is better than being unable to reach via PID in interactive
621             * enforcing mode.
622             */
623          if (domain)          if (domain)
624                  r->domain = domain;                  task->ccs_domain_info = domain;
625            if (domain_created)
626                    ccs_audit_domain_creation_log();
627     out:
628            if (need_kfree)
629                    kfree(rn.name);
630          return retval;          return retval;
631  }  }
632    
633  /**  /**
634   * ccs_check_environ - Check permission for environment variable names.   * ccs_environ - Check permission for environment variable names.
635   *   *
636   * @ee: Pointer to "struct ccs_execve_entry".   * @ee: Pointer to "struct ccs_execve".
637   *   *
638   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
639   */   */
640  static int ccs_check_environ(struct ccs_execve_entry *ee)  static int ccs_environ(struct ccs_execve *ee)
641  {  {
642          struct ccs_request_info *r = &ee->r;          struct ccs_request_info *r = &ee->r;
643          struct linux_binprm *bprm = ee->bprm;          struct linux_binprm *bprm = ee->bprm;
644          char *arg_ptr = ee->tmp;          /* env_page->data is allocated by ccs_dump_page(). */
645            struct ccs_page_dump env_page = { };
646            char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */
647          int arg_len = 0;          int arg_len = 0;
648          unsigned long pos = bprm->p;          unsigned long pos = bprm->p;
649          int offset = pos % PAGE_SIZE;          int offset = pos % PAGE_SIZE;
# Line 870  static int ccs_check_environ(struct ccs_ Line 651  static int ccs_check_environ(struct ccs_
651          int envp_count = bprm->envc;          int envp_count = bprm->envc;
652          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */          /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
653          int error = -ENOMEM;          int error = -ENOMEM;
654            ee->r.type = CCS_MAC_ENVIRON;
655            ee->r.mode = ccs_get_mode(ccs_current_domain()->profile,
656                                      CCS_MAC_ENVIRON);
657          if (!r->mode || !envp_count)          if (!r->mode || !envp_count)
658                  return 0;                  return 0;
659            arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
660            if (!arg_ptr)
661                    goto out;
662          while (error == -ENOMEM) {          while (error == -ENOMEM) {
663                  if (!ccs_dump_page(bprm, pos, &ee->dump))                  if (!ccs_dump_page(bprm, pos, &env_page))
664                          goto out;                          goto out;
665                  pos += PAGE_SIZE - offset;                  pos += PAGE_SIZE - offset;
666                  /* Read. */                  /* Read. */
667                  while (argv_count && offset < PAGE_SIZE) {                  while (argv_count && offset < PAGE_SIZE) {
668                          const char *kaddr = ee->dump.data;                          if (!env_page.data[offset++])
                         if (!kaddr[offset++])  
669                                  argv_count--;                                  argv_count--;
670                  }                  }
671                  if (argv_count) {                  if (argv_count) {
# Line 887  static int ccs_check_environ(struct ccs_ Line 673  static int ccs_check_environ(struct ccs_
673                          continue;                          continue;
674                  }                  }
675                  while (offset < PAGE_SIZE) {                  while (offset < PAGE_SIZE) {
676                          const char *kaddr = ee->dump.data;                          const unsigned char c = env_page.data[offset++];
677                          const unsigned char c = kaddr[offset++];                          if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
                         if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {  
678                                  if (c == '=') {                                  if (c == '=') {
679                                          arg_ptr[arg_len++] = '\0';                                          arg_ptr[arg_len++] = '\0';
680                                  } else if (c == '\\') {                                  } else if (c == '\\') {
# Line 909  static int ccs_check_environ(struct ccs_ Line 694  static int ccs_check_environ(struct ccs_
694                          }                          }
695                          if (c)                          if (c)
696                                  continue;                                  continue;
697                          if (ccs_check_env_perm(r, arg_ptr)) {                          if (ccs_env_perm(r, arg_ptr)) {
698                                  error = -EPERM;                                  error = -EPERM;
699                                  break;                                  break;
700                          }                          }
# Line 924  static int ccs_check_environ(struct ccs_ Line 709  static int ccs_check_environ(struct ccs_
709   out:   out:
710          if (r->mode != 3)          if (r->mode != 3)
711                  error = 0;                  error = 0;
712            kfree(env_page.data);
713            kfree(arg_ptr);
714          return error;          return error;
715  }  }
716    
# Line 974  static void ccs_unescape(unsigned char * Line 761  static void ccs_unescape(unsigned char *
761   *   *
762   * Returns number of directories to strip.   * Returns number of directories to strip.
763   */   */
764  static inline int ccs_root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)  static int ccs_root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)
765  {  {
766          int depth = 0;          int depth = 0;
         /***** CRITICAL SECTION START *****/  
767          ccs_realpath_lock();          ccs_realpath_lock();
768          for (;;) {          for (;;) {
769                  if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {                  if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
# Line 992  static inline int ccs_root_depth(struct Line 778  static inline int ccs_root_depth(struct
778                  depth++;                  depth++;
779          }          }
780          ccs_realpath_unlock();          ccs_realpath_unlock();
         /***** CRITICAL SECTION END *****/  
781          return depth;          return depth;
782  }  }
783    
# Line 1009  static int ccs_get_root_depth(void) Line 794  static int ccs_get_root_depth(void)
794  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
795          struct path root;          struct path root;
796  #endif  #endif
         /***** CRITICAL SECTION START *****/  
797          read_lock(&current->fs->lock);          read_lock(&current->fs->lock);
798  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
799          root = current->fs->root;          root = current->fs->root;
# Line 1021  static int ccs_get_root_depth(void) Line 805  static int ccs_get_root_depth(void)
805          vfsmnt = mntget(current->fs->rootmnt);          vfsmnt = mntget(current->fs->rootmnt);
806  #endif  #endif
807          read_unlock(&current->fs->lock);          read_unlock(&current->fs->lock);
         /***** CRITICAL SECTION END *****/  
808          depth = ccs_root_depth(dentry, vfsmnt);          depth = ccs_root_depth(dentry, vfsmnt);
809  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
810          path_put(&root);          path_put(&root);
# Line 1032  static int ccs_get_root_depth(void) Line 815  static int ccs_get_root_depth(void)
815          return depth;          return depth;
816  }  }
817    
 static LIST_HEAD(ccs_execve_list);  
 static DEFINE_SPINLOCK(ccs_execve_list_lock);  
   
 /**  
  * ccs_allocate_execve_entry - Allocate memory for execve().  
  *  
  * Returns pointer to "struct ccs_execve_entry" on success, NULL otherwise.  
  */  
 static struct ccs_execve_entry *ccs_allocate_execve_entry(void)  
 {  
         struct ccs_execve_entry *ee = kzalloc(sizeof(*ee), GFP_KERNEL);  
         if (!ee)  
                 return NULL;  
         ee->program_path = kzalloc(CCS_MAX_PATHNAME_LEN, GFP_KERNEL);  
         ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, GFP_KERNEL);  
         if (!ee->program_path || !ee->tmp) {  
                 kfree(ee->program_path);  
                 kfree(ee->tmp);  
                 kfree(ee);  
                 return NULL;  
         }  
         ee->reader_idx = ccs_read_lock();  
         /* ee->dump->data is allocated by ccs_dump_page(). */  
         ee->task = current;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&ccs_execve_list_lock);  
         list_add(&ee->list, &ccs_execve_list);  
         spin_unlock(&ccs_execve_list_lock);  
         /***** CRITICAL SECTION END *****/  
         return ee;  
 }  
   
 /**  
  * ccs_find_execve_entry - Find ccs_execve_entry of current process.  
  *  
  * Returns pointer to "struct ccs_execve_entry" on success, NULL otherwise.  
  */  
 static struct ccs_execve_entry *ccs_find_execve_entry(void)  
 {  
         struct task_struct *task = current;  
         struct ccs_execve_entry *ee = NULL;  
         struct ccs_execve_entry *p;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&ccs_execve_list_lock);  
         list_for_each_entry(p, &ccs_execve_list, list) {  
                 if (p->task != task)  
                         continue;  
                 ee = p;  
                 break;  
         }  
         spin_unlock(&ccs_execve_list_lock);  
         /***** CRITICAL SECTION END *****/  
         return ee;  
 }  
   
 /**  
  * ccs_free_execve_entry - Free memory for execve().  
  *  
  * @ee: Pointer to "struct ccs_execve_entry".  
  */  
 static void ccs_free_execve_entry(struct ccs_execve_entry *ee)  
 {  
         if (!ee)  
                 return;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&ccs_execve_list_lock);  
         list_del(&ee->list);  
         spin_unlock(&ccs_execve_list_lock);  
         /***** CRITICAL SECTION END *****/  
         kfree(ee->program_path);  
         kfree(ee->tmp);  
         kfree(ee->dump.data);  
         ccs_read_unlock(ee->reader_idx);  
         kfree(ee);  
 }  
   
818  /**  /**
819   * ccs_try_alt_exec - Try to start execute handler.   * ccs_try_alt_exec - Try to start execute handler.
820   *   *
821   * @ee: Pointer to "struct ccs_execve_entry".   * @ee: Pointer to "struct ccs_execve".
822   *   *
823   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
824   */   */
825  static int ccs_try_alt_exec(struct ccs_execve_entry *ee)  static int ccs_try_alt_exec(struct ccs_execve *ee)
826  {  {
827          /*          /*
828           * Contents of modified bprm.           * Contents of modified bprm.
# Line 1161  static int ccs_try_alt_exec(struct ccs_e Line 868  static int ccs_try_alt_exec(struct ccs_e
868          struct task_struct *task = current;          struct task_struct *task = current;
869    
870          /* Close the requested program's dentry. */          /* Close the requested program's dentry. */
871            ee->obj.path1.dentry = NULL;
872            ee->obj.path1.mnt = NULL;
873            ee->obj.validate_done = false;
874          allow_write_access(bprm->file);          allow_write_access(bprm->file);
875          fput(bprm->file);          fput(bprm->file);
876          bprm->file = NULL;          bprm->file = NULL;
# Line 1208  static int ccs_try_alt_exec(struct ccs_e Line 918  static int ccs_try_alt_exec(struct ccs_e
918                           "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "                           "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
919                           "sgid=%d fsuid=%d fsgid=%d state[0]=%u "                           "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
920                           "state[1]=%u state[2]=%u",                           "state[1]=%u state[2]=%u",
921                           (pid_t) sys_getpid(), current_uid(), current_gid(),                           (pid_t) ccsecurity_exports.sys_getpid(),
922                           current_euid(), current_egid(), current_suid(),                           current_uid(), current_gid(), current_euid(),
923                           current_sgid(), current_fsuid(), current_fsgid(),                           current_egid(), current_suid(), current_sgid(),
924                             current_fsuid(), current_fsgid(),
925                           (u8) (ccs_flags >> 24), (u8) (ccs_flags >> 16),                           (u8) (ccs_flags >> 24), (u8) (ccs_flags >> 16),
926                           (u8) (ccs_flags >> 8));                           (u8) (ccs_flags >> 8));
927                  retval = copy_strings_kernel(1, &cp, bprm);                  retval = copy_strings_kernel(1, &cp, bprm);
# Line 1227  static int ccs_try_alt_exec(struct ccs_e Line 938  static int ccs_try_alt_exec(struct ccs_e
938                          kfree(exe);                          kfree(exe);
939                  } else {                  } else {
940                          exe = ee->tmp;                          exe = ee->tmp;
941                          strncpy(ee->tmp, "<unknown>", CCS_EXEC_TMPSIZE - 1);                          snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
942                          retval = copy_strings_kernel(1, &exe, bprm);                          retval = copy_strings_kernel(1, &exe, bprm);
943                  }                  }
944                  if (retval < 0)                  if (retval < 0)
# Line 1238  static int ccs_try_alt_exec(struct ccs_e Line 949  static int ccs_try_alt_exec(struct ccs_e
949          /* Set argv[1] */          /* Set argv[1] */
950          {          {
951                  char *cp = ee->tmp;                  char *cp = ee->tmp;
952                  strncpy(ee->tmp, ccs_current_domain()->domainname->name,                  snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
953                          CCS_EXEC_TMPSIZE - 1);                           ccs_current_domain()->domainname->name);
954                  retval = copy_strings_kernel(1, &cp, bprm);                  retval = copy_strings_kernel(1, &cp, bprm);
955                  if (retval < 0)                  if (retval < 0)
956                          goto out;                          goto out;
# Line 1249  static int ccs_try_alt_exec(struct ccs_e Line 960  static int ccs_try_alt_exec(struct ccs_e
960          /* Set argv[0] */          /* Set argv[0] */
961          {          {
962                  int depth = ccs_get_root_depth();                  int depth = ccs_get_root_depth();
963                  char *cp = ee->program_path;                  int len = ee->handler->total_len + 1;
964                  strncpy(cp, ee->handler->name, CCS_MAX_PATHNAME_LEN - 1);                  char *cp = kmalloc(len, CCS_GFP_FLAGS);
965                    if (!cp) {
966                            retval = -ENOMEM;
967                            goto out;
968                    }
969                    ee->handler_path = cp;
970                    memmove(cp, ee->handler->name, len);
971                  ccs_unescape(cp);                  ccs_unescape(cp);
972                  retval = -ENOENT;                  retval = -ENOENT;
973                  if (!*cp || *cp != '/')                  if (!*cp || *cp != '/')
# Line 1262  static int ccs_try_alt_exec(struct ccs_e Line 979  static int ccs_try_alt_exec(struct ccs_e
979                                  goto out;                                  goto out;
980                          depth--;                          depth--;
981                  }                  }
982                  memmove(ee->program_path, cp, strlen(cp) + 1);                  memmove(ee->handler_path, cp, strlen(cp) + 1);
983                  cp = ee->program_path;                  cp = ee->handler_path;
984                  retval = copy_strings_kernel(1, &cp, bprm);                  retval = copy_strings_kernel(1, &cp, bprm);
985                  if (retval < 0)                  if (retval < 0)
986                          goto out;                          goto out;
# Line 1275  static int ccs_try_alt_exec(struct ccs_e Line 992  static int ccs_try_alt_exec(struct ccs_e
992  #endif  #endif
993  #endif  #endif
994    
995          /* OK, now restart the process with execute handler program's dentry. */          /*
996          filp = open_exec(ee->program_path);           * OK, now restart the process with execute handler program's dentry.
997             */
998            filp = open_exec(ee->handler_path);
999          if (IS_ERR(filp)) {          if (IS_ERR(filp)) {
1000                  retval = PTR_ERR(filp);                  retval = PTR_ERR(filp);
1001                  goto out;                  goto out;
1002          }          }
1003            ee->obj.path1.dentry = filp->f_dentry;
1004            ee->obj.path1.mnt = filp->f_vfsmnt;
1005          bprm->file = filp;          bprm->file = filp;
1006          bprm->filename = ee->program_path;          bprm->filename = ee->handler_path;
1007  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
1008          bprm->interp = bprm->filename;          bprm->interp = bprm->filename;
1009  #endif  #endif
1010          retval = prepare_binprm(bprm);          retval = prepare_binprm(bprm);
1011          if (retval < 0)          if (retval < 0)
1012                  goto out;                  goto out;
1013          {          task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1014                  /*          retval = ccs_find_next_domain(ee);
1015                   * Backup ee->program_path because ccs_find_next_domain() will          task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
                  * overwrite ee->program_path and ee->tmp.  
                  */  
                 const int len = strlen(ee->program_path) + 1;  
                 char *cp = kzalloc(len, GFP_KERNEL);  
                 if (!cp) {  
                         retval = -ENOMEM;  
                         goto out;  
                 }  
                 memmove(cp, ee->program_path, len);  
                 bprm->filename = cp;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  
                 bprm->interp = bprm->filename;  
 #endif  
                 task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;  
                 retval = ccs_find_next_domain(ee);  
                 task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;  
                 /* Restore ee->program_path for search_binary_handler(). */  
                 memmove(ee->program_path, cp, len);  
                 bprm->filename = ee->program_path;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  
                 bprm->interp = bprm->filename;  
 #endif  
                 kfree(cp);  
         }  
1016   out:   out:
1017          return retval;          return retval;
1018  }  }
# Line 1323  static int ccs_try_alt_exec(struct ccs_e Line 1020  static int ccs_try_alt_exec(struct ccs_e
1020  /**  /**
1021   * ccs_find_execute_handler - Find an execute handler.   * ccs_find_execute_handler - Find an execute handler.
1022   *   *
1023   * @ee:   Pointer to "struct ccs_execve_entry".   * @ee:   Pointer to "struct ccs_execve".
1024   * @type: Type of execute handler.   * @type: Type of execute handler.
1025   *   *
1026   * Returns true if found, false otherwise.   * Returns true if found, false otherwise.
1027   *   *
1028   * Caller holds ccs_read_lock().   * Caller holds ccs_read_lock().
1029   */   */
1030  static bool ccs_find_execute_handler(struct ccs_execve_entry *ee,  static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type)
                                      const u8 type)  
1031  {  {
1032          struct task_struct *task = current;          struct ccs_request_info *r = &ee->r;
1033          const struct ccs_domain_info *domain = ccs_current_domain();          const struct ccs_domain_info *domain = ccs_current_domain();
1034          struct ccs_acl_info *ptr;          struct ccs_acl_info *ptr;
         bool found = false;  
         ccs_check_read_lock();  
1035          /*          /*
1036           * Don't use execute handler if the current process is           * To avoid infinite execute handler loop, don't use execute handler
1037           * marked as execute handler to avoid infinite execute handler loop.           * if the current process is marked as execute handler .
1038           */           */
1039          if (task->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)          if (current->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
1040                  return false;                  return false;
1041          list_for_each_entry(ptr, &domain->acl_info_list, list) {   retry:
1042                  struct ccs_execute_handler_record *acl;          list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1043                  if (ptr->type != type)                  struct ccs_execute_handler *acl;
1044                    if (ptr->type != type || !ccs_condition(r, ptr->cond))
1045                          continue;                          continue;
1046                  acl = container_of(ptr, struct ccs_execute_handler_record,                  acl = container_of(ptr, struct ccs_execute_handler, head);
                                    head);  
1047                  ee->handler = acl->handler;                  ee->handler = acl->handler;
1048                  found = true;                  ee->handler_type = type;
1049                  break;                  r->cond = ptr->cond;
1050                    return true;
1051            }
1052            if (domain != &ccs_global_domain &&
1053                !domain->flags[CCS_DIF_IGNORE_GLOBAL]) {
1054                    domain = &ccs_global_domain;
1055                    goto retry;
1056          }          }
1057          return found;          return false;
1058  }  }
1059    
1060    #ifdef CONFIG_MMU
1061    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1062    #define CCS_BPRM_MMU
1063    #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3
1064    #define CCS_BPRM_MMU
1065    #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2
1066    #define CCS_BPRM_MMU
1067    #endif
1068    #endif
1069    
1070  /**  /**
1071   * ccs_dump_page - Dump a page to buffer.   * ccs_dump_page - Dump a page to buffer.
1072   *   *
# Line 1370  bool ccs_dump_page(struct linux_binprm * Line 1080  bool ccs_dump_page(struct linux_binprm *
1080                     struct ccs_page_dump *dump)                     struct ccs_page_dump *dump)
1081  {  {
1082          struct page *page;          struct page *page;
1083          /* dump->data is released by ccs_free_execve_entry(). */          /* dump->data is released by ccs_finish_execve(). */
1084          if (!dump->data) {          if (!dump->data) {
1085                  dump->data = kzalloc(PAGE_SIZE, GFP_KERNEL);                  dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS);
1086                  if (!dump->data)                  if (!dump->data)
1087                          return false;                          return false;
1088          }          }
1089          /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */          /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
1090  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  #ifdef CCS_BPRM_MMU
         if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)  
                 return false;  
 #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR == 3 && defined(CONFIG_MMU)  
1091          if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)          if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1092                  return false;                  return false;
1093  #else  #else
# Line 1395  bool ccs_dump_page(struct linux_binprm * Line 1102  bool ccs_dump_page(struct linux_binprm *
1102                   */                   */
1103                  char *kaddr = kmap_atomic(page, KM_USER0);                  char *kaddr = kmap_atomic(page, KM_USER0);
1104                  dump->page = page;                  dump->page = page;
1105                  memcpy(dump->data + offset, kaddr + offset, PAGE_SIZE - offset);                  memcpy(dump->data + offset, kaddr + offset,
1106                           PAGE_SIZE - offset);
1107                  kunmap_atomic(kaddr, KM_USER0);                  kunmap_atomic(kaddr, KM_USER0);
1108          }          }
1109          /* Same with put_arg_page(page) in fs/exec.c */          /* Same with put_arg_page(page) in fs/exec.c */
1110  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)  #ifdef CCS_BPRM_MMU
         put_page(page);  
 #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR == 3 && defined(CONFIG_MMU)  
1111          put_page(page);          put_page(page);
1112  #endif  #endif
1113          return true;          return true;
1114  }  }
1115    
1116  /**  /**
  * ccs_fetch_next_domain - Fetch next_domain from the list.  
  *  
  * Returns pointer to "struct ccs_domain_info" which will be used if execve()  
  * succeeds. This function does not return NULL.  
  */  
 struct ccs_domain_info *ccs_fetch_next_domain(void)  
 {  
         struct ccs_execve_entry *ee = ccs_find_execve_entry();  
         struct ccs_domain_info *next_domain = NULL;  
         if (ee)  
                 next_domain = ee->r.domain;  
         if (!next_domain)  
                 next_domain = ccs_current_domain();  
         return next_domain;  
 }  
   
 /**  
1117   * ccs_start_execve - Prepare for execve() operation.   * ccs_start_execve - Prepare for execve() operation.
1118   *   *
1119   * @bprm: Pointer to "struct linux_binprm".   * @bprm: Pointer to "struct linux_binprm".
1120     * @eep:  Pointer to "struct ccs_execve *".
1121   *   *
1122   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
1123   */   */
1124  int ccs_start_execve(struct linux_binprm *bprm)  static int ccs_start_execve(struct linux_binprm *bprm,
1125                                struct ccs_execve **eep)
1126  {  {
1127          int retval;          int retval;
1128          struct task_struct *task = current;          struct task_struct *task = current;
1129          struct ccs_execve_entry *ee = ccs_allocate_execve_entry();          struct ccs_execve *ee;
1130          if (!ccs_policy_loaded)          *eep = NULL;
1131                  ccs_load_policy(bprm->filename);          ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS);
1132          if (!ee)          if (!ee)
1133                  return -ENOMEM;                  return -ENOMEM;
1134          ccs_init_request_info(&ee->r, NULL, CCS_MAC_FOR_FILE);          ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
1135            if (!ee->tmp) {
1136                    kfree(ee);
1137                    return -ENOMEM;
1138            }
1139            ee->reader_idx = ccs_read_lock();
1140            /* ee->dump->data is allocated by ccs_dump_page(). */
1141            ee->previous_domain = task->ccs_domain_info;
1142            /* Clear manager flag. */
1143            task->ccs_flags &= ~CCS_TASK_IS_MANAGER;
1144            *eep = ee;
1145            ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE);
1146          ee->r.ee = ee;          ee->r.ee = ee;
1147          ee->bprm = bprm;          ee->bprm = bprm;
1148          ee->r.obj = &ee->obj;          ee->r.obj = &ee->obj;
1149          ee->obj.path1_dentry = bprm->file->f_dentry;          ee->obj.path1.dentry = bprm->file->f_dentry;
1150          ee->obj.path1_vfsmnt = bprm->file->f_vfsmnt;          ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1151          /* Clear manager flag. */          /*
1152          task->ccs_flags &= ~CCS_TASK_IS_POLICY_MANAGER;           * No need to call ccs_environ() for execute handler because envp[] is
1153          if (ccs_find_execute_handler(ee, TYPE_EXECUTE_HANDLER)) {           * moved to argv[].
1154                  retval = ccs_try_alt_exec(ee);           */
1155                  if (!retval)          if (ccs_find_execute_handler(ee, CCS_TYPE_EXECUTE_HANDLER))
1156                          ccs_audit_execute_handler_log(ee, true);                  return ccs_try_alt_exec(ee);
                 goto ok;  
         }  
1157          retval = ccs_find_next_domain(ee);          retval = ccs_find_next_domain(ee);
1158          if (retval != -EPERM)          if (retval == -EPERM) {
1159                  goto ok;                  if (ccs_find_execute_handler(ee,
1160          if (ccs_find_execute_handler(ee, TYPE_DENIED_EXECUTE_HANDLER)) {                                               CCS_TYPE_DENIED_EXECUTE_HANDLER))
1161                  retval = ccs_try_alt_exec(ee);                          return ccs_try_alt_exec(ee);
                 if (!retval)  
                         ccs_audit_execute_handler_log(ee, false);  
1162          }          }
1163   ok:          if (!retval)
1164          if (retval < 0)                  retval = ccs_environ(ee);
                 goto out;  
         ee->r.mode = ccs_check_flags(ee->r.domain, CCS_MAC_FOR_ENV);  
         retval = ccs_check_environ(ee);  
         if (retval < 0)  
                 goto out;  
         task->ccs_flags |= CCS_CHECK_READ_FOR_OPEN_EXEC;  
         retval = 0;  
  out:  
         if (retval)  
                 ccs_finish_execve(retval);  
1165          return retval;          return retval;
1166  }  }
1167    
# Line 1481  int ccs_start_execve(struct linux_binprm Line 1169  int ccs_start_execve(struct linux_binprm
1169   * ccs_finish_execve - Clean up execve() operation.   * ccs_finish_execve - Clean up execve() operation.
1170   *   *
1171   * @retval: Return code of an execve() operation.   * @retval: Return code of an execve() operation.
1172     * @ee:     Pointer to "struct ccs_execve".
1173   *   *
1174   * Caller holds ccs_read_lock().   * Caller holds ccs_read_lock().
1175   */   */
1176  void ccs_finish_execve(int retval)  static void ccs_finish_execve(int retval, struct ccs_execve *ee)
1177  {  {
1178          struct task_struct *task = current;          struct task_struct *task = current;
         struct ccs_execve_entry *ee = ccs_find_execve_entry();  
         ccs_check_read_lock();  
         task->ccs_flags &= ~CCS_CHECK_READ_FOR_OPEN_EXEC;  
1179          if (!ee)          if (!ee)
1180                  return;                  return;
1181          if (retval < 0)          if (retval < 0) {
1182                  goto out;                  task->ccs_domain_info = ee->previous_domain;
1183          /* Proceed to next domain if execution suceeded. */                  /*
1184          task->ccs_domain_info = ee->r.domain;                   * Make task->ccs_domain_info visible to GC before changing
1185          /* Mark the current process as execute handler. */                   * task->ccs_flags .
1186          if (ee->handler)                   */
1187                  task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;                  smp_mb();
1188          /* Mark the current process as normal process. */          } else {
1189          else                  /* Mark the current process as execute handler. */
1190                  task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;                  if (ee->handler)
1191   out:                          task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1192          ccs_free_execve_entry(ee);                  /* Mark the current process as normal process. */
1193                    else
1194                            task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1195            }
1196            /* Tell GC that I finished execve(). */
1197            task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE;
1198            ccs_read_unlock(ee->reader_idx);
1199            kfree(ee->handler_path);
1200            kfree(ee->tmp);
1201            kfree(ee->dump.data);
1202            kfree(ee);
1203    }
1204    
1205    /**
1206     * ccs_may_transit - Check permission and do domain transition without execve().
1207     *
1208     * @domainname: Domainname to transit to.
1209     * @pathname: Pathname to check.
1210     *
1211     * Returns 0 on success, negative value otherwise.
1212     *
1213     * Caller holds ccs_read_lock().
1214     */
1215    int ccs_may_transit(const char *domainname, const char *pathname)
1216    {
1217            struct ccs_path_info name;
1218            struct ccs_request_info r;
1219            struct ccs_domain_info *domain;
1220            int error;
1221            bool domain_created = false;
1222            name.name = pathname;
1223            ccs_fill_path_info(&name);
1224            /* Check "file transit" permission. */
1225            ccs_init_request_info(&r, CCS_MAC_FILE_TRANSIT);
1226            error = ccs_path_permission(&r, CCS_TYPE_TRANSIT, &name);
1227            if (error)
1228                    return error;
1229            /* Check destination domain. */
1230            domain = ccs_find_domain(domainname);
1231            if (!domain && r.mode != CCS_CONFIG_ENFORCING &&
1232                strlen(domainname) < CCS_EXEC_TMPSIZE - 10) {
1233                    domain = ccs_assign_domain(domainname, r.profile);
1234                    if (domain)
1235                            domain_created = true;
1236            }
1237            if (domain) {
1238                    error = 0;
1239                    current->ccs_domain_info = domain;
1240                    if (domain_created)
1241                            ccs_audit_domain_creation_log();
1242            } else {
1243                    error = -ENOENT;
1244            }
1245            return error;
1246    }
1247    
1248    static int __ccs_search_binary_handler(struct linux_binprm *bprm,
1249                                           struct pt_regs *regs)
1250    {
1251            struct ccs_execve *ee;
1252            int retval;
1253            if (!ccs_policy_loaded)
1254                    ccsecurity_exports.load_policy(bprm->filename);
1255            retval = ccs_start_execve(bprm, &ee);
1256            if (!retval)
1257                    retval = search_binary_handler(bprm, regs);
1258            ccs_finish_execve(retval, ee);
1259            return retval;
1260    }
1261    
1262    void __init ccs_domain_init(void)
1263    {
1264            ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler;
1265  }  }

Legend:
Removed from v.2828  
changed lines
  Added in v.3808

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