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

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

Legend:
Removed from v.987  
changed lines
  Added in v.3519

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