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

Subversion リポジトリの参照

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

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

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

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

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