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

Subversion リポジトリの参照

Diff of /trunk/1.7.x/ccs-patch/security/ccsecurity/util.c

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

trunk/1.5.x/ccs-patch/fs/ccs_common.c revision 813 by kumaneko, Tue Dec 18 05:35:28 2007 UTC trunk/1.6.x/ccs-patch/fs/ccs_common.c revision 1007 by kumaneko, Thu Feb 28 06:42:01 2008 UTC
# Line 3  Line 3 
3   *   *
4   * Common functions for SAKURA and TOMOYO.   * Common functions for SAKURA and TOMOYO.
5   *   *
6   * Copyright (C) 2005-2007  NTT DATA CORPORATION   * Copyright (C) 2005-2008  NTT DATA CORPORATION
7   *   *
8   * Version: 1.5.3-pre   2007/12/18   * Version: 1.6.0-pre   2008/02/28
9   *   *
10   * This file is applicable to both 2.4.30 and 2.6.11 and later.   * This file is applicable to both 2.4.30 and 2.6.11 and later.
11   * See README.ccs for ChangeLog.   * See README.ccs for ChangeLog.
# Line 32  static const int lookup_flags = LOOKUP_F Line 32  static const int lookup_flags = LOOKUP_F
32  #include <linux/ccs_common.h>  #include <linux/ccs_common.h>
33  #include <linux/ccs_proc.h>  #include <linux/ccs_proc.h>
34  #include <linux/tomoyo.h>  #include <linux/tomoyo.h>
35    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
36    #define find_task_by_pid find_task_by_vpid
37    #endif
38    
39  #ifdef CONFIG_TOMOYO_MAX_ACCEPT_ENTRY  #ifdef CONFIG_TOMOYO_MAX_ACCEPT_ENTRY
40  #define MAX_ACCEPT_ENTRY (CONFIG_TOMOYO_MAX_ACCEPT_ENTRY)  #define MAX_ACCEPT_ENTRY (CONFIG_TOMOYO_MAX_ACCEPT_ENTRY)
# Line 52  static const int lookup_flags = LOOKUP_F Line 55  static const int lookup_flags = LOOKUP_F
55  /*************************  VARIABLES  *************************/  /*************************  VARIABLES  *************************/
56    
57  /* /sbin/init started? */  /* /sbin/init started? */
58  int sbin_init_started = 0;  bool sbin_init_started = false;
59    
60  const char *ccs_log_level = KERN_DEBUG;  const char *ccs_log_level = KERN_DEBUG;
61    
# Line 89  struct profile { Line 92  struct profile {
92  };  };
93    
94  static struct profile *profile_ptr[MAX_PROFILES];  static struct profile *profile_ptr[MAX_PROFILES];
95    static bool manage_by_non_root = false;
96    
97  /*************************  UTILITY FUNCTIONS  *************************/  /*************************  UTILITY FUNCTIONS  *************************/
98    
# Line 102  static int __init TOMOYO_Quiet_Setup(cha Line 106  static int __init TOMOYO_Quiet_Setup(cha
106  __setup("TOMOYO_QUIET", TOMOYO_Quiet_Setup);  __setup("TOMOYO_QUIET", TOMOYO_Quiet_Setup);
107  #endif  #endif
108    
 /* Am I root? */  
 static int isRoot(void)  
 {  
         return !current->uid && !current->euid;  
 }  
   
109  /*  /*
110   * Format string.   * Format string.
111   * Leading and trailing whitespaces are removed.   * Leading and trailing whitespaces are removed.
# Line 116  static int isRoot(void) Line 114  static int isRoot(void)
114  static void NormalizeLine(unsigned char *buffer)  static void NormalizeLine(unsigned char *buffer)
115  {  {
116          unsigned char *sp = buffer, *dp = buffer;          unsigned char *sp = buffer, *dp = buffer;
117          int first = 1;          bool first = true;
118          while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;          while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
119          while (*sp) {          while (*sp) {
120                  if (!first) *dp++ = ' ';                  if (!first) *dp++ = ' ';
121                  first = 0;                  first = false;
122                  while (*sp > ' ' && *sp < 127) *dp++ = *sp++;                  while (*sp > ' ' && *sp < 127) *dp++ = *sp++;
123                  while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;                  while (*sp && (*sp <= ' ' || *sp >= 127)) sp++;
124          }          }
# Line 129  static void NormalizeLine(unsigned char Line 127  static void NormalizeLine(unsigned char
127    
128  /*  /*
129   *  Check whether the given filename follows the naming rules.   *  Check whether the given filename follows the naming rules.
130   *  Returns nonzero if follows, zero otherwise.   *  Returns true if follows, false otherwise.
131   */   */
132  bool IsCorrectPath(const char *filename, const int start_type, const int pattern_type, const int end_type, const char *function)  bool IsCorrectPath(const char *filename, const s8 start_type, const s8 pattern_type, const s8 end_type, const char *function)
133  {  {
134          int contains_pattern = 0;          bool contains_pattern = false;
135          char c, d, e;          char c, d, e;
136          const char *original_filename = filename;          const char *original_filename = filename;
137          if (!filename) goto out;          if (!filename) goto out;
# Line 165  bool IsCorrectPath(const char *filename, Line 163  bool IsCorrectPath(const char *filename,
163                          case 'A':   /* "\A" */                          case 'A':   /* "\A" */
164                          case '-':   /* "\-" */                          case '-':   /* "\-" */
165                                  if (pattern_type == -1) break; /* Must not contain pattern */                                  if (pattern_type == -1) break; /* Must not contain pattern */
166                                  contains_pattern = 1;                                  contains_pattern = true;
167                                  continue;                                  continue;
168                          case '0':   /* "\ooo" */                          case '0':   /* "\ooo" */
169                          case '1':                          case '1':
# Line 187  bool IsCorrectPath(const char *filename, Line 185  bool IsCorrectPath(const char *filename,
185          if (pattern_type == 1) { /* Must contain pattern */          if (pattern_type == 1) { /* Must contain pattern */
186                  if (!contains_pattern) goto out;                  if (!contains_pattern) goto out;
187          }          }
188          return 1;          return true;
189   out:   out:
190          printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, original_filename);          printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, original_filename);
191          return 0;          return false;
192  }  }
193    
194  /*  /*
195   *  Check whether the given domainname follows the naming rules.   *  Check whether the given domainname follows the naming rules.
196   *  Returns nonzero if follows, zero otherwise.   *  Returns true if follows, false otherwise.
197   */   */
198  bool IsCorrectDomain(const unsigned char *domainname, const char *function)  bool IsCorrectDomain(const unsigned char *domainname, const char *function)
199  {  {
# Line 203  bool IsCorrectDomain(const unsigned char Line 201  bool IsCorrectDomain(const unsigned char
201          const char *org_domainname = domainname;          const char *org_domainname = domainname;
202          if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN)) goto out;          if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN)) goto out;
203          domainname += ROOT_NAME_LEN;          domainname += ROOT_NAME_LEN;
204          if (!*domainname) return 1;          if (!*domainname) return true;
205          do {          do {
206                  if (*domainname++ != ' ') goto out;                  if (*domainname++ != ' ') goto out;
207                  if (*domainname++ != '/') goto out;                  if (*domainname++ != '/') goto out;
# Line 231  bool IsCorrectDomain(const unsigned char Line 229  bool IsCorrectDomain(const unsigned char
229                          }                          }
230                  }                  }
231          } while (*domainname);          } while (*domainname);
232          return 1;          return true;
233   out:   out:
234          printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, org_domainname);          printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, org_domainname);
235          return 0;          return false;
236  }  }
237    
238  bool IsDomainDef(const unsigned char *buffer)  bool IsDomainDef(const unsigned char *buffer)
# Line 302  void fill_path_info(struct path_info *pt Line 300  void fill_path_info(struct path_info *pt
300          ptr->depth = PathDepth(name);          ptr->depth = PathDepth(name);
301  }  }
302    
303  static int FileMatchesToPattern2(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end)  static bool FileMatchesToPattern2(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end)
304  {  {
305          while (filename < filename_end && pattern < pattern_end) {          while (filename < filename_end && pattern < pattern_end) {
306                  if (*pattern != '\\') {                  if (*pattern != '\\') {
307                          if (*filename++ != *pattern++) return 0;                          if (*filename++ != *pattern++) return false;
308                  } else {                  } else {
309                          char c = *filename;                          char c = *filename;
310                          pattern++;                          pattern++;
311                          switch (*pattern) {                          switch (*pattern) {
312                          case '?':                          case '?':
313                                  if (c == '/') {                                  if (c == '/') {
314                                          return 0;                                          return false;
315                                  } else if (c == '\\') {                                  } else if (c == '\\') {
316                                          if ((c = filename[1]) == '\\') {                                          if ((c = filename[1]) == '\\') {
317                                                  filename++; /* safe because filename is \\ */                                                  filename++; /* safe because filename is \\ */
318                                          } else if (c >= '0' && c <= '3' && (c = filename[2]) >= '0' && c <= '7' && (c = filename[3]) >= '0' && c <= '7') {                                          } else if (c >= '0' && c <= '3' && (c = filename[2]) >= '0' && c <= '7' && (c = filename[3]) >= '0' && c <= '7') {
319                                                  filename += 3; /* safe because filename is \ooo */                                                  filename += 3; /* safe because filename is \ooo */
320                                          } else {                                          } else {
321                                                  return 0;                                                  return false;
322                                          }                                          }
323                                  }                                  }
324                                  break;                                  break;
325                          case '\\':                          case '\\':
326                                  if (c != '\\') return 0;                                  if (c != '\\') return false;
327                                  if (*++filename != '\\') return 0; /* safe because *filename != '\0' */                                  if (*++filename != '\\') return false; /* safe because *filename != '\0' */
328                                  break;                                  break;
329                          case '+':                          case '+':
330                                  if (c < '0' || c > '9') return 0;                                  if (c < '0' || c > '9') return false;
331                                  break;                                  break;
332                          case 'x':                          case 'x':
333                                  if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) return 0;                                  if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) return false;
334                                  break;                                  break;
335                          case 'a':                          case 'a':
336                                  if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) return 0;                                  if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) return false;
337                                  break;                                  break;
338                          case '0':                          case '0':
339                          case '1':                          case '1':
# Line 348  static int FileMatchesToPattern2(const c Line 346  static int FileMatchesToPattern2(const c
346                                          pattern += 2; /* safe because pattern is \ooo  */                                          pattern += 2; /* safe because pattern is \ooo  */
347                                          break;                                          break;
348                                  }                                  }
349                                  return 0; /* Not matched. */                                  return false; /* Not matched. */
350                          case '*':                          case '*':
351                          case '@':                          case '@':
352                                  {                                  {
353                                          int i;                                          int i;
354                                          for (i = 0; i <= filename_end - filename; i++) {                                          for (i = 0; i <= filename_end - filename; i++) {
355                                                  if (FileMatchesToPattern2(filename + i, filename_end, pattern + 1, pattern_end)) return 1;                                                  if (FileMatchesToPattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true;
356                                                  if ((c = filename[i]) == '.' && *pattern == '@') break;                                                  if ((c = filename[i]) == '.' && *pattern == '@') break;
357                                                  if (c == '\\') {                                                  if (c == '\\') {
358                                                          if ((c = filename[i + 1]) == '\\') {                                                          if ((c = filename[i + 1]) == '\\') {
# Line 366  static int FileMatchesToPattern2(const c Line 364  static int FileMatchesToPattern2(const c
364                                                          }                                                          }
365                                                  }                                                  }
366                                          }                                          }
367                                          return 0; /* Not matched. */                                          return false; /* Not matched. */
368                                  }                                  }
369                          default:                          default:
370                                  {                                  {
# Line 379  static int FileMatchesToPattern2(const c Line 377  static int FileMatchesToPattern2(const c
377                                                  while (((c = filename[j]) >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) j++;                                                  while (((c = filename[j]) >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) j++;
378                                          }                                          }
379                                          for (i = 1; i <= j; i++) {                                          for (i = 1; i <= j; i++) {
380                                                  if (FileMatchesToPattern2(filename + i, filename_end, pattern + 1, pattern_end)) return 1;                                                  if (FileMatchesToPattern2(filename + i, filename_end, pattern + 1, pattern_end)) return true;
381                                          }                                          }
382                                  }                                  }
383                                  return 0; /* Not matched or bad pattern. */                                  return false; /* Not matched or bad pattern. */
384                          }                          }
385                          filename++; /* safe because *filename != '\0' */                          filename++; /* safe because *filename != '\0' */
386                          pattern++; /* safe because *pattern != '\0' */                          pattern++; /* safe because *pattern != '\0' */
# Line 392  static int FileMatchesToPattern2(const c Line 390  static int FileMatchesToPattern2(const c
390          return (filename == filename_end && pattern == pattern_end);          return (filename == filename_end && pattern == pattern_end);
391  }  }
392    
393  static int FileMatchesToPattern(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end)  static bool FileMatchesToPattern(const char *filename, const char *filename_end, const char *pattern, const char *pattern_end)
394  {  {
395          const char *pattern_start = pattern;          const char *pattern_start = pattern;
396          int first = 1;          bool first = true;
397          int result;          bool result;
398          while (pattern < pattern_end - 1) {          while (pattern < pattern_end - 1) {
399                  if (*pattern++ != '\\' || *pattern++ != '-') continue;                  if (*pattern++ != '\\' || *pattern++ != '-') continue;
400                  result = FileMatchesToPattern2(filename, filename_end, pattern_start, pattern - 2);                  result = FileMatchesToPattern2(filename, filename_end, pattern_start, pattern - 2);
401                  if (first) result = !result;                  if (first) result = !result;
402                  if (result) return 0;                  if (result) return false;
403                  first = 0;                  first = false;
404                  pattern_start = pattern;                  pattern_start = pattern;
405          }          }
406          result = FileMatchesToPattern2(filename, filename_end, pattern_start, pattern_end);          result = FileMatchesToPattern2(filename, filename_end, pattern_start, pattern_end);
# Line 411  static int FileMatchesToPattern(const ch Line 409  static int FileMatchesToPattern(const ch
409    
410  /*  /*
411   *  Check whether the given pathname matches to the given pattern.   *  Check whether the given pathname matches to the given pattern.
412   *  Returns nonzero if matches, zero otherwise.   *  Returns true if matches, false otherwise.
413   *   *
414   *  The following patterns are available.   *  The following patterns are available.
415   *    \\     \ itself.   *    \\     \ itself.
# Line 428  static int FileMatchesToPattern(const ch Line 426  static int FileMatchesToPattern(const ch
426   *    \-     Subtraction operator.   *    \-     Subtraction operator.
427   */   */
428    
429  int PathMatchesToPattern(const struct path_info *pathname0, const struct path_info *pattern0)  bool PathMatchesToPattern(const struct path_info *pathname0, const struct path_info *pattern0)
430  {  {
431          /* if (!pathname || !pattern) return 0; */          /* if (!pathname || !pattern) return false; */
432          const char *pathname = pathname0->name, *pattern = pattern0->name;          const char *pathname = pathname0->name, *pattern = pattern0->name;
433          const int len = pattern0->const_len;          const int len = pattern0->const_len;
434          if (!pattern0->is_patterned) return !pathcmp(pathname0, pattern0);          if (!pattern0->is_patterned) return !pathcmp(pathname0, pattern0);
435          if (pathname0->depth != pattern0->depth) return 0;          if (pathname0->depth != pattern0->depth) return false;
436          if (strncmp(pathname, pattern, len)) return 0;          if (strncmp(pathname, pattern, len)) return false;
437          pathname += len; pattern += len;          pathname += len; pattern += len;
438          while (*pathname && *pattern) {          while (*pathname && *pattern) {
439                  const char *pathname_delimiter = strchr(pathname, '/'), *pattern_delimiter = strchr(pattern, '/');                  const char *pathname_delimiter = strchr(pathname, '/'), *pattern_delimiter = strchr(pattern, '/');
440                  if (!pathname_delimiter) pathname_delimiter = strchr(pathname, '\0');                  if (!pathname_delimiter) pathname_delimiter = strchr(pathname, '\0');
441                  if (!pattern_delimiter) pattern_delimiter = strchr(pattern, '\0');                  if (!pattern_delimiter) pattern_delimiter = strchr(pattern, '\0');
442                  if (!FileMatchesToPattern(pathname, pathname_delimiter, pattern, pattern_delimiter)) return 0;                  if (!FileMatchesToPattern(pathname, pathname_delimiter, pattern, pattern_delimiter)) return false;
443                  pathname = *pathname_delimiter ? pathname_delimiter + 1 : pathname_delimiter;                  pathname = *pathname_delimiter ? pathname_delimiter + 1 : pathname_delimiter;
444                  pattern = *pattern_delimiter ? pattern_delimiter + 1 : pattern_delimiter;                  pattern = *pattern_delimiter ? pattern_delimiter + 1 : pattern_delimiter;
445          }          }
# Line 503  const char *GetAltExec(void) Line 501  const char *GetAltExec(void)
501  /*************************  DOMAIN POLICY HANDLER  *************************/  /*************************  DOMAIN POLICY HANDLER  *************************/
502    
503  /* Check whether the given access control is enabled. */  /* Check whether the given access control is enabled. */
504  unsigned int CheckCCSFlags(const unsigned int index)  unsigned int CheckCCSFlags_NoSleepCheck(const u8 index)
505  {  {
506          const u8 profile = current->domain_info->profile;          const u8 profile = current->domain_info->profile;
507          return sbin_init_started && index < CCS_MAX_CONTROL_INDEX          return sbin_init_started && index < CCS_MAX_CONTROL_INDEX
# Line 513  unsigned int CheckCCSFlags(const unsigne Line 511  unsigned int CheckCCSFlags(const unsigne
511                  && profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0;                  && profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0;
512  }  }
513    
514  bool TomoyoVerboseMode(void)  unsigned int CheckCCSFlags(const u8 index)
515  {  {
516          return CheckCCSFlags(CCS_TOMOYO_VERBOSE) != 0;  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
517            if (unlikely(in_interrupt()))
518    #else
519            if (unlikely(in_atomic()))
520    #endif
521            {
522                    static u8 count = 20;
523                    if (count) {
524                            count--;
525                            printk(KERN_ERR "BUG: sleeping function called from invalid context.\n");
526                            dump_stack();
527                    }
528                    /* Return 0 so that no MAC checks are performed. */
529                    return 0;
530            }
531            return CheckCCSFlags_NoSleepCheck(index);
532  }  }
533    
534  /* Check whether the given access control is enforce mode. */  bool TomoyoVerboseMode(void)
 bool CheckCCSEnforce(const unsigned int index)  
535  {  {
536          return CheckCCSFlags(index) == 3;          return CheckCCSFlags(CCS_TOMOYO_VERBOSE) != 0;
537  }  }
538    
539  bool CheckDomainQuota(struct domain_info * const domain)  bool CheckDomainQuota(struct domain_info * const domain)
540  {  {
541          unsigned int count = 0;          unsigned int count = 0;
542          struct acl_info *ptr;          struct acl_info *ptr;
543          if (!domain) return 1;          if (!domain) return true;
544          list1_for_each_entry(ptr, &domain->acl_info_list, list) {          list1_for_each_entry(ptr, &domain->acl_info_list, list) {
545                  if (!ptr->is_deleted) count++;                  if (!(ptr->type & ACL_DELETED)) count++;
546          }          }
547          if (count < CheckCCSFlags(CCS_TOMOYO_MAX_ACCEPT_ENTRY)) return 1;          if (count < CheckCCSFlags(CCS_TOMOYO_MAX_ACCEPT_ENTRY)) return true;
548          if (!domain->quota_warned) {          if (!domain->quota_warned) {
549                  domain->quota_warned = 1;                  domain->quota_warned = true;
550                  printk("TOMOYO-WARNING: Domain '%s' has so many ACLs to hold. Stopped learning mode.\n", domain->domainname->name);                  printk("TOMOYO-WARNING: Domain '%s' has so many ACLs to hold. Stopped learning mode.\n", domain->domainname->name);
551          }          }
552          return 0;          return false;
 }  
   
 /* Check whether the given access control is learning mode. */  
 bool CheckCCSAccept(const unsigned int index, struct domain_info * const domain)  
 {  
         if (CheckCCSFlags(index) != 1) return 0;  
         return CheckDomainQuota(domain);  
553  }  }
554    
555  static struct profile *FindOrAssignNewProfile(const unsigned int profile)  static struct profile *FindOrAssignNewProfile(const unsigned int profile)
# Line 572  static int SetProfile(struct io_buffer * Line 577  static int SetProfile(struct io_buffer *
577          unsigned int i, value;          unsigned int i, value;
578          char *cp;          char *cp;
579          struct profile *profile;          struct profile *profile;
         if (!isRoot()) return -EPERM;  
580          i = simple_strtoul(data, &cp, 10);          i = simple_strtoul(data, &cp, 10);
581          if (data != cp) {          if (data != cp) {
582                  if (*cp != '-') return -EINVAL;                  if (*cp != '-') return -EINVAL;
# Line 615  static int SetProfile(struct io_buffer * Line 619  static int SetProfile(struct io_buffer *
619    
620  static int ReadProfile(struct io_buffer *head)  static int ReadProfile(struct io_buffer *head)
621  {  {
622          if (!head->read_eof) {          int step;
623                  if (!isRoot()) return -EPERM;          if (head->read_eof) return 0;
624                  if (!head->read_var2) {          if (head->read_var2) goto print_capability;
625                          int step;          for (step = head->read_step; step < MAX_PROFILES * CCS_MAX_CONTROL_INDEX; step++) {
626                          for (step = head->read_step; step < MAX_PROFILES * CCS_MAX_CONTROL_INDEX; step++) {                  const int i = step / CCS_MAX_CONTROL_INDEX, j = step % CCS_MAX_CONTROL_INDEX;
627                                  const int i = step / CCS_MAX_CONTROL_INDEX, j = step % CCS_MAX_CONTROL_INDEX;                  const struct profile *profile = profile_ptr[i];
628                                  const struct profile *profile = profile_ptr[i];                  head->read_step = step;
629                                  head->read_step = step;                  if (!profile) continue;
630                                  if (!profile) continue;                  switch (j) {
631                                  switch (j) {                  case -1: /* Dummy */
                                 case -1: /* Dummy */  
632  #ifndef CONFIG_SAKURA  #ifndef CONFIG_SAKURA
633                                  case CCS_SAKURA_DENY_CONCEAL_MOUNT:                  case CCS_SAKURA_DENY_CONCEAL_MOUNT:
634                                  case CCS_SAKURA_RESTRICT_CHROOT:                  case CCS_SAKURA_RESTRICT_CHROOT:
635                                  case CCS_SAKURA_RESTRICT_MOUNT:                  case CCS_SAKURA_RESTRICT_MOUNT:
636                                  case CCS_SAKURA_RESTRICT_UNMOUNT:                  case CCS_SAKURA_RESTRICT_UNMOUNT:
637                                  case CCS_SAKURA_RESTRICT_PIVOT_ROOT:                  case CCS_SAKURA_RESTRICT_PIVOT_ROOT:
638                                  case CCS_SAKURA_RESTRICT_AUTOBIND:                  case CCS_SAKURA_RESTRICT_AUTOBIND:
639  #endif  #endif
640  #ifndef CONFIG_TOMOYO  #ifndef CONFIG_TOMOYO
641                                  case CCS_TOMOYO_MAC_FOR_FILE:                  case CCS_TOMOYO_MAC_FOR_FILE:
642                                  case CCS_TOMOYO_MAC_FOR_ARGV0:                  case CCS_TOMOYO_MAC_FOR_ARGV0:
643                                  case CCS_TOMOYO_MAC_FOR_ENV:                  case CCS_TOMOYO_MAC_FOR_ENV:
644                                  case CCS_TOMOYO_MAC_FOR_NETWORK:                  case CCS_TOMOYO_MAC_FOR_NETWORK:
645                                  case CCS_TOMOYO_MAC_FOR_SIGNAL:                  case CCS_TOMOYO_MAC_FOR_SIGNAL:
646                                  case CCS_TOMOYO_MAX_ACCEPT_ENTRY:                  case CCS_TOMOYO_MAX_ACCEPT_ENTRY:
647                                  case CCS_TOMOYO_MAX_GRANT_LOG:                  case CCS_TOMOYO_MAX_GRANT_LOG:
648                                  case CCS_TOMOYO_MAX_REJECT_LOG:                  case CCS_TOMOYO_MAX_REJECT_LOG:
649                                  case CCS_TOMOYO_VERBOSE:                  case CCS_TOMOYO_VERBOSE:
650  #endif  #endif
651  #ifndef ALT_EXEC  #ifndef ALT_EXEC
652                                  case CCS_TOMOYO_ALT_EXEC:                  case CCS_TOMOYO_ALT_EXEC:
653                                  case CCS_SLEEP_PERIOD:                  case CCS_SLEEP_PERIOD:
654  #endif  #endif
655                                          continue;                          continue;
                                 }  
                                 if (j == CCS_PROFILE_COMMENT) {  
                                         if (io_printf(head, "%u-%s=%s\n", i, ccs_control_array[CCS_PROFILE_COMMENT].keyword, profile->comment ? profile->comment->name : "")) break;  
                                 } else if (j == CCS_TOMOYO_ALT_EXEC) {  
                                         const struct path_info *alt_exec = profile->alt_exec;  
                                         if (io_printf(head, "%u-%s=%s\n", i, ccs_control_array[CCS_TOMOYO_ALT_EXEC].keyword, alt_exec ? alt_exec->name : "")) break;  
                                 } else {  
                                         if (io_printf(head, "%u-%s=%u\n", i, ccs_control_array[j].keyword, profile->value[j])) break;  
                                 }  
                         }  
                         if (step == MAX_PROFILES * CCS_MAX_CONTROL_INDEX) {  
                                 head->read_var2 = (void *) "";  
                                 head->read_step = 0;  
                         }  
656                  }                  }
657                  if (head->read_var2) {                  if (j == CCS_PROFILE_COMMENT) {
658                            if (io_printf(head, "%u-%s=%s\n", i, ccs_control_array[CCS_PROFILE_COMMENT].keyword, profile->comment ? profile->comment->name : "")) break;
659                    } else if (j == CCS_TOMOYO_ALT_EXEC) {
660                            const struct path_info *alt_exec = profile->alt_exec;
661                            if (io_printf(head, "%u-%s=%s\n", i, ccs_control_array[CCS_TOMOYO_ALT_EXEC].keyword, alt_exec ? alt_exec->name : "")) break;
662                    } else {
663                            if (io_printf(head, "%u-%s=%u\n", i, ccs_control_array[j].keyword, profile->value[j])) break;
664                    }
665            }
666            if (step == MAX_PROFILES * CCS_MAX_CONTROL_INDEX) {
667                    head->read_var2 = (void *) "";
668                    head->read_step = 0;
669            }
670     print_capability:
671            if (head->read_var2) {
672  #ifdef CONFIG_TOMOYO  #ifdef CONFIG_TOMOYO
673                          if (ReadCapabilityStatus(head) == 0)                  if (ReadCapabilityStatus(head) == 0)
674  #endif  #endif
675                                  head->read_eof = 1;                          head->read_eof = true;
                 }  
676          }          }
677          return 0;          return 0;
678  }  }
# Line 692  static int AddManagerEntry(const char *m Line 694  static int AddManagerEntry(const char *m
694          static DEFINE_MUTEX(lock);          static DEFINE_MUTEX(lock);
695          const struct path_info *saved_manager;          const struct path_info *saved_manager;
696          int error = -ENOMEM;          int error = -ENOMEM;
697          bool is_domain = 0;          bool is_domain = false;
         if (!isRoot()) return -EPERM;  
698          if (IsDomainDef(manager)) {          if (IsDomainDef(manager)) {
699                  if (!IsCorrectDomain(manager, __FUNCTION__)) return -EINVAL;                  if (!IsCorrectDomain(manager, __FUNCTION__)) return -EINVAL;
700                  is_domain = 1;                  is_domain = true;
701          } else {          } else {
702                  if (!IsCorrectPath(manager, 1, -1, -1, __FUNCTION__)) return -EINVAL;                  if (!IsCorrectPath(manager, 1, -1, -1, __FUNCTION__)) return -EINVAL;
703          }          }
# Line 727  static int AddManagerEntry(const char *m Line 728  static int AddManagerEntry(const char *m
728  static int AddManagerPolicy(struct io_buffer *head)  static int AddManagerPolicy(struct io_buffer *head)
729  {  {
730          const char *data = head->write_buf;          const char *data = head->write_buf;
731          bool is_delete = 0;          bool is_delete = false;
         if (!isRoot()) return -EPERM;  
732          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
733                  data += KEYWORD_DELETE_LEN;                  data += KEYWORD_DELETE_LEN;
734                  is_delete = 1;                  is_delete = true;
735            }
736            if (strcmp(data, "manage_by_non_root") == 0) {
737                    manage_by_non_root = is_delete;
738                    return 0;
739          }          }
740          return AddManagerEntry(data, is_delete);          return AddManagerEntry(data, is_delete);
741  }  }
# Line 740  static int ReadManagerPolicy(struct io_b Line 744  static int ReadManagerPolicy(struct io_b
744  {  {
745          struct list1_head *pos;          struct list1_head *pos;
746          if (head->read_eof) return 0;          if (head->read_eof) return 0;
         if (!isRoot()) return -EPERM;  
747          list1_for_each_cookie(pos, head->read_var2, &policy_manager_list) {          list1_for_each_cookie(pos, head->read_var2, &policy_manager_list) {
748                  struct policy_manager_entry *ptr;                  struct policy_manager_entry *ptr;
749                  ptr = list1_entry(pos, struct policy_manager_entry, list);                  ptr = list1_entry(pos, struct policy_manager_entry, list);
750                  if (ptr->is_deleted) continue;                  if (ptr->is_deleted) continue;
751                  if (io_printf(head, "%s\n", ptr->manager->name)) return 0;                  if (io_printf(head, "%s\n", ptr->manager->name)) return 0;
752          }          }
753          head->read_eof = 1;          head->read_eof = true;
754          return 0;          return 0;
755  }  }
756    
757  /* Check whether the current process is a policy manager. */  /* Check whether the current process is a policy manager. */
758  static int IsPolicyManager(void)  static bool IsPolicyManager(void)
759  {  {
760          struct policy_manager_entry *ptr;          struct policy_manager_entry *ptr;
761          const char *exe;          const char *exe;
762          const struct path_info *domainname = current->domain_info->domainname;          const struct task_struct *task = current;
763          bool found = 0;          const struct path_info *domainname = task->domain_info->domainname;
764          if (!sbin_init_started) return 1;          bool found = false;
765            if (!sbin_init_started) return true;
766            if (!manage_by_non_root && (task->uid || task->euid)) return false;
767          list1_for_each_entry(ptr, &policy_manager_list, list) {          list1_for_each_entry(ptr, &policy_manager_list, list) {
768                  if (!ptr->is_deleted && ptr->is_domain && !pathcmp(domainname, ptr->manager)) return 1;                  if (!ptr->is_deleted && ptr->is_domain && !pathcmp(domainname, ptr->manager)) return true;
769          }          }
770          if ((exe = GetEXE()) == NULL) return 0;          if ((exe = GetEXE()) == NULL) return false;
771          list1_for_each_entry(ptr, &policy_manager_list, list) {          list1_for_each_entry(ptr, &policy_manager_list, list) {
772                  if (!ptr->is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) {                  if (!ptr->is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) {
773                          found = 1;                          found = true;
774                          break;                          break;
775                  }                  }
776          }          }
# Line 791  static char *FindConditionPart(char *dat Line 796  static char *FindConditionPart(char *dat
796          if (cp) {          if (cp) {
797                  while ((cp2 = strstr(cp + 3, " if ")) != NULL) cp = cp2;                  while ((cp2 = strstr(cp + 3, " if ")) != NULL) cp = cp2;
798                  *cp++ = '\0';                  *cp++ = '\0';
799            } else if ((cp = strstr(data, " ; set ")) != NULL) {
800                    *cp++ = '\0';
801          }          }
802          return cp;          return cp;
803  }  }
# Line 799  static int AddDomainPolicy(struct io_buf Line 806  static int AddDomainPolicy(struct io_buf
806  {  {
807          char *data = head->write_buf;          char *data = head->write_buf;
808          struct domain_info *domain = head->write_var1;          struct domain_info *domain = head->write_var1;
809          bool is_delete = 0, is_select = 0, is_undelete = 0;          bool is_delete = false, is_select = false, is_undelete = false;
810          unsigned int profile;          unsigned int profile;
811          const struct condition_list *cond = NULL;          const struct condition_list *cond = NULL;
812          char *cp;                char *cp;
         if (!isRoot()) return -EPERM;  
813          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
814                  data += KEYWORD_DELETE_LEN;                  data += KEYWORD_DELETE_LEN;
815                  is_delete = 1;                  is_delete = true;
816          } else if (strncmp(data, KEYWORD_SELECT, KEYWORD_SELECT_LEN) == 0) {          } else if (strncmp(data, KEYWORD_SELECT, KEYWORD_SELECT_LEN) == 0) {
817                  data += KEYWORD_SELECT_LEN;                  data += KEYWORD_SELECT_LEN;
818                  is_select = 1;                  is_select = true;
819          } else if (strncmp(data, KEYWORD_UNDELETE, KEYWORD_UNDELETE_LEN) == 0) {          } else if (strncmp(data, KEYWORD_UNDELETE, KEYWORD_UNDELETE_LEN) == 0) {
820                  data += KEYWORD_UNDELETE_LEN;                  data += KEYWORD_UNDELETE_LEN;
821                  is_undelete = 1;                  is_undelete = true;
822          }          }
         UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);  
823          if (IsDomainDef(data)) {          if (IsDomainDef(data)) {
824                  if (is_delete) {                  if (is_delete) {
825                          DeleteDomain(data);                          DeleteDomain(data);
# Line 827  static int AddDomainPolicy(struct io_buf Line 832  static int AddDomainPolicy(struct io_buf
832                          domain = FindOrAssignNewDomain(data, 0);                          domain = FindOrAssignNewDomain(data, 0);
833                  }                  }
834                  head->write_var1 = domain;                  head->write_var1 = domain;
835                    UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
836                  return 0;                  return 0;
837          }          }
838          if (!domain) return -EINVAL;          if (!domain) return -EINVAL;
# Line 835  static int AddDomainPolicy(struct io_buf Line 841  static int AddDomainPolicy(struct io_buf
841                  if (profile_ptr[profile] || !sbin_init_started) domain->profile = (u8) profile;                  if (profile_ptr[profile] || !sbin_init_started) domain->profile = (u8) profile;
842                  return 0;                  return 0;
843          }          }
844            if (strcmp(data, KEYWORD_IGNORE_GLOBAL_ALLOW_READ) == 0) {
845                    if (!is_delete) domain->flags |= DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ;
846                    else domain->flags &= ~DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ;
847                    return 0;
848            }
849            if (strcmp(data, KEYWORD_IGNORE_GLOBAL_ALLOW_ENV) == 0) {
850                    if (!is_delete) domain->flags |= DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_ENV;
851                    else domain->flags &= ~DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_ENV;
852                    return 0;
853            }
854          cp = FindConditionPart(data);          cp = FindConditionPart(data);
855          if (cp && (cond = FindOrAssignNewCondition(cp)) == NULL) return -EINVAL;          if (cp && (cond = FindOrAssignNewCondition(cp)) == NULL) return -EINVAL;
856          if (strncmp(data, KEYWORD_ALLOW_CAPABILITY, KEYWORD_ALLOW_CAPABILITY_LEN) == 0) {          if (strncmp(data, KEYWORD_ALLOW_CAPABILITY, KEYWORD_ALLOW_CAPABILITY_LEN) == 0) {
# Line 853  static int AddDomainPolicy(struct io_buf Line 869  static int AddDomainPolicy(struct io_buf
869          return -EINVAL;          return -EINVAL;
870  }  }
871    
872    static bool print_single_path_acl(struct io_buffer *head, struct single_path_acl_record *ptr, const struct condition_list *cond)
873    {
874            int pos;
875            u8 bit;
876            const bool b = ptr->u_is_group;
877            const u16 perm = ptr->perm;
878            for (bit = head->read_bit; bit < MAX_SINGLE_PATH_OPERATION; bit++) {
879                    const char *msg;
880                    if (!(perm & (1 << bit))) continue;
881                    /* Print "read/write" instead of "read" and "write". */
882                    if ((bit == TYPE_READ_ACL || bit == TYPE_WRITE_ACL) && (perm & (1 << TYPE_READ_WRITE_ACL))) continue;
883                    msg = sp_operation2keyword(bit);
884                    pos = head->read_avail;
885                    if (b && io_printf(head, "allow_%s @%s ", msg, ptr->u.group->group_name->name)) goto out;
886                    if (!b && io_printf(head, "allow_%s %s ", msg, ptr->u.filename->name)) goto out;
887                    if (DumpCondition(head, cond)) goto out;
888            }
889            head->read_bit = 0;
890            return true;
891     out:
892            head->read_bit = bit;
893            head->read_avail = pos;
894            return false;
895    }
896    
897    static bool print_double_path_acl(struct io_buffer *head, struct double_path_acl_record *ptr, const struct condition_list *cond)
898    {
899            int pos;
900            const bool b0 = ptr->u1_is_group, b1 = ptr->u2_is_group;
901            const u8 perm = ptr->perm;
902            u8 bit;
903            for (bit = head->read_bit; bit < MAX_DOUBLE_PATH_OPERATION; bit++) {
904                    const char *msg;
905                    if (!(perm & (1 << bit))) continue;
906                    msg = dp_operation2keyword(bit);
907                    pos = head->read_avail;
908                    if (io_printf(head, "allow_%s ", msg)) goto out;
909                    if (b0 && io_printf(head, "@%s ", ptr->u1.group1->group_name->name)) goto out;
910                    if (!b0 && io_printf(head, "%s ", ptr->u1.filename1->name)) goto out;
911                    if (b1 && io_printf(head, "@%s", ptr->u2.group2->group_name->name)) goto out;
912                    if (!b1 && io_printf(head, "%s", ptr->u2.filename2->name)) goto out;
913                    if (DumpCondition(head, cond)) goto out;
914            }
915            head->read_bit = 0;
916            return true;
917     out:
918            head->read_bit = bit;
919            head->read_avail = pos;
920            return false;
921    }
922    
923    static bool print_argv0_acl(struct io_buffer *head, struct argv0_acl_record *ptr, const struct condition_list *cond)
924    {
925            int pos = head->read_avail;
926            if (io_printf(head, KEYWORD_ALLOW_ARGV0 "%s %s",
927                          ptr->filename->name, ptr->argv0->name)) goto out;
928            if (DumpCondition(head, cond)) goto out;
929            return true;
930     out:
931            head->read_avail = pos;
932            return false;
933    }
934    
935    static bool print_env_acl(struct io_buffer *head, struct env_acl_record *ptr, const struct condition_list *cond)
936    {
937            int pos = head->read_avail;
938            if (io_printf(head, KEYWORD_ALLOW_ENV "%s", ptr->env->name)) goto out;
939            if (DumpCondition(head, cond)) goto out;
940            return true;
941     out:
942            head->read_avail = pos;
943            return false;
944    }
945    
946    static bool print_capability_acl(struct io_buffer *head, struct capability_acl_record *ptr, const struct condition_list *cond)
947    {
948            int pos = head->read_avail;
949            if (io_printf(head, KEYWORD_ALLOW_CAPABILITY "%s", cap_operation2keyword(ptr->operation))) goto out;
950            if (DumpCondition(head, cond)) goto out;
951            return true;
952     out:
953            head->read_avail = pos;
954            return false;
955    }
956    
957    static bool print_network_acl(struct io_buffer *head, struct ip_network_acl_record *ptr, const struct condition_list *cond)
958    {
959            int pos = head->read_avail;
960            if (io_printf(head, KEYWORD_ALLOW_NETWORK "%s ", net_operation2keyword(ptr->operation_type))) goto out;
961            switch (ptr->record_type) {
962            case IP_RECORD_TYPE_ADDRESS_GROUP:
963                    if (io_printf(head, "@%s", ptr->u.group->group_name->name)) goto out;
964                    break;
965            case IP_RECORD_TYPE_IPv4:
966                    {
967                            const u32 min_address = ptr->u.ipv4.min, max_address = ptr->u.ipv4.max;
968                            if (io_printf(head, "%u.%u.%u.%u", HIPQUAD(min_address))) goto out;
969                            if (min_address != max_address && io_printf(head, "-%u.%u.%u.%u", HIPQUAD(max_address))) goto out;
970                    }
971                    break;
972            case IP_RECORD_TYPE_IPv6:
973                    {
974                            char buf[64];
975                            const struct in6_addr *min_address = ptr->u.ipv6.min, *max_address = ptr->u.ipv6.max;
976                            print_ipv6(buf, sizeof(buf), min_address);
977                            if (io_printf(head, "%s", buf)) goto out;
978                            if (min_address != max_address) {
979                                    print_ipv6(buf, sizeof(buf), max_address);
980                                    if (io_printf(head, "-%s", buf)) goto out;
981                            }
982                    }
983                    break;
984            }
985            {
986                    const u16 min_port = ptr->min_port, max_port = ptr->max_port;
987                    if (io_printf(head, " %u", min_port)) goto out;
988                    if (min_port != max_port && io_printf(head, "-%u", max_port)) goto out;
989            }
990            if (DumpCondition(head, cond)) goto out;
991            return true;
992     out:
993            head->read_avail = pos;
994            return false;
995    }
996    
997    static bool print_signal_acl(struct io_buffer *head, struct signal_acl_record *ptr, const struct condition_list *cond)
998    {
999            int pos = head->read_avail;
1000            if (io_printf(head, KEYWORD_ALLOW_SIGNAL "%u %s", ptr->sig, ptr->domainname->name)) goto out;
1001            if (DumpCondition(head, cond)) goto out;
1002            return true;
1003     out:
1004            head->read_avail = pos;
1005            return false;
1006    }
1007    
1008  static int ReadDomainPolicy(struct io_buffer *head)  static int ReadDomainPolicy(struct io_buffer *head)
1009  {  {
1010          struct list1_head *dpos;          struct list1_head *dpos;
1011          struct list1_head *apos;          struct list1_head *apos;
1012          if (head->read_eof) return 0;          if (head->read_eof) return 0;
1013          if (head->read_step == 0) {          if (head->read_step == 0) head->read_step = 1;
                 if (!isRoot()) return -EPERM;  
                 head->read_step = 1;  
         }  
1014          list1_for_each_cookie(dpos, head->read_var1, &domain_list) {          list1_for_each_cookie(dpos, head->read_var1, &domain_list) {
1015                  struct domain_info *domain;                  struct domain_info *domain;
1016                  domain = list1_entry(dpos, struct domain_info, list);                  domain = list1_entry(dpos, struct domain_info, list);
1017                  if (head->read_step != 1) goto acl_loop;                  if (head->read_step != 1) goto acl_loop;
1018                  if (domain->is_deleted) continue;                  if (domain->is_deleted) continue;
1019                  if (io_printf(head, "%s\n" KEYWORD_USE_PROFILE "%u\n%s\n", domain->domainname->name, domain->profile, domain->quota_warned ? "quota_exceeded\n" : "")) return 0;                  if (io_printf(head, "%s\n" KEYWORD_USE_PROFILE "%u\n%s\n%s%s", domain->domainname->name, domain->profile, domain->quota_warned ? "quota_exceeded\n" : "", domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ ? KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n" : "", domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_ENV ? KEYWORD_IGNORE_GLOBAL_ALLOW_ENV "\n" : "")) return 0;
1020                  head->read_step = 2;                  head->read_step = 2;
1021          acl_loop: ;          acl_loop: ;
1022                  if (head->read_step == 3) goto tail_mark;                  if (head->read_step == 3) goto tail_mark;
1023                  list1_for_each_cookie(apos, head->read_var2, &domain->acl_info_list) {                  list1_for_each_cookie(apos, head->read_var2, &domain->acl_info_list) {
1024                          struct acl_info *ptr;                          struct acl_info *ptr;
                         int pos;  
1025                          u8 acl_type;                          u8 acl_type;
1026                            const struct condition_list *cond;
1027                          ptr = list1_entry(apos, struct acl_info, list);                          ptr = list1_entry(apos, struct acl_info, list);
1028                          if (ptr->is_deleted) continue;                          cond = GetConditionPart(ptr);
1029                          pos = head->read_avail;                          acl_type = ptr->type & ~ACL_WITH_CONDITION;
1030                          acl_type = ptr->type;                          if (acl_type & ACL_DELETED) {
1031                          if (acl_type == TYPE_FILE_ACL) {                                  /* Deleted entry. */
1032                                  struct file_acl_record *ptr2 = container_of(ptr, struct file_acl_record, head);                          } else if (acl_type == TYPE_SINGLE_PATH_ACL) {
1033                                  const unsigned char b = ptr2->u_is_group;                                  if (!print_single_path_acl(head, container_of(ptr, struct single_path_acl_record, head), cond)) return 0;
1034                                  if (io_printf(head, "%d %s%s", ptr2->perm,                          } else if (acl_type == TYPE_DOUBLE_PATH_ACL) {
1035                                                b ? "@" : "",                                  if (!print_double_path_acl(head, container_of(ptr, struct double_path_acl_record, head), cond)) return 0;
                                               b ? ptr2->u.group->group_name->name : ptr2->u.filename->name)) goto print_acl_rollback;  
1036                          } else if (acl_type == TYPE_ARGV0_ACL) {                          } else if (acl_type == TYPE_ARGV0_ACL) {
1037                                  struct argv0_acl_record *ptr2 = container_of(ptr, struct argv0_acl_record, head);                                  if (!print_argv0_acl(head, container_of(ptr, struct argv0_acl_record, head), cond)) return 0;
                                 if (io_printf(head, KEYWORD_ALLOW_ARGV0 "%s %s",  
                                               ptr2->filename->name, ptr2->argv0->name)) goto print_acl_rollback;  
1038                          } else if (acl_type == TYPE_ENV_ACL) {                          } else if (acl_type == TYPE_ENV_ACL) {
1039                                  struct env_acl_record *ptr2 = container_of(ptr, struct env_acl_record, head);                                  if (!print_env_acl(head, container_of(ptr, struct env_acl_record, head), cond)) return 0;
                                 if (io_printf(head, KEYWORD_ALLOW_ENV "%s", ptr2->env->name)) goto print_acl_rollback;  
1040                          } else if (acl_type == TYPE_CAPABILITY_ACL) {                          } else if (acl_type == TYPE_CAPABILITY_ACL) {
1041                                  struct capability_acl_record *ptr2 = container_of(ptr, struct capability_acl_record, head);                                  if (!print_capability_acl(head, container_of(ptr, struct capability_acl_record, head), cond)) return 0;
                                 if (io_printf(head, KEYWORD_ALLOW_CAPABILITY "%s", capability2keyword(ptr2->capability))) goto print_acl_rollback;  
1042                          } else if (acl_type == TYPE_IP_NETWORK_ACL) {                          } else if (acl_type == TYPE_IP_NETWORK_ACL) {
1043                                  struct ip_network_acl_record *ptr2 = container_of(ptr, struct ip_network_acl_record, head);                                  if (!print_network_acl(head, container_of(ptr, struct ip_network_acl_record, head), cond)) return 0;
                                 if (io_printf(head, KEYWORD_ALLOW_NETWORK "%s ", network2keyword(ptr2->operation_type))) goto print_acl_rollback;  
                                 switch (ptr2->record_type) {  
                                 case IP_RECORD_TYPE_ADDRESS_GROUP:  
                                         if (io_printf(head, "@%s", ptr2->u.group->group_name->name)) goto print_acl_rollback;  
                                         break;  
                                 case IP_RECORD_TYPE_IPv4:  
                                         {  
                                                 const u32 min_address = ptr2->u.ipv4.min, max_address = ptr2->u.ipv4.max;  
                                                 if (io_printf(head, "%u.%u.%u.%u", HIPQUAD(min_address))) goto print_acl_rollback;  
                                                 if (min_address != max_address && io_printf(head, "-%u.%u.%u.%u", HIPQUAD(max_address))) goto print_acl_rollback;  
                                         }  
                                         break;  
                                 case IP_RECORD_TYPE_IPv6:  
                                         {  
                                                 char buf[64];  
                                                 const struct in6_addr *min_address = ptr2->u.ipv6.min, *max_address = ptr2->u.ipv6.max;  
                                                 print_ipv6(buf, sizeof(buf), min_address);  
                                                 if (io_printf(head, "%s", buf)) goto print_acl_rollback;  
                                                 if (min_address != max_address) {  
                                                         print_ipv6(buf, sizeof(buf), max_address);  
                                                         if (io_printf(head, "-%s", buf)) goto print_acl_rollback;  
                                                 }  
                                         }  
                                         break;  
                                 }  
                                 {  
                                         const u16 min_port = ptr2->min_port, max_port = ptr2->max_port;  
                                         if (io_printf(head, " %u", min_port)) goto print_acl_rollback;  
                                         if (min_port != max_port && io_printf(head, "-%u", max_port)) goto print_acl_rollback;  
                                 }  
1044                          } else if (acl_type == TYPE_SIGNAL_ACL) {                          } else if (acl_type == TYPE_SIGNAL_ACL) {
1045                                  struct signal_acl_record *ptr2 = container_of(ptr, struct signal_acl_record, head);                                  if (!print_signal_acl(head, container_of(ptr, struct signal_acl_record, head), cond)) return 0;
                                 if (io_printf(head, KEYWORD_ALLOW_SIGNAL "%u %s", ptr2->sig, ptr2->domainname->name)) goto print_acl_rollback;  
1046                          } else {                          } else {
1047                                  const char *keyword = acltype2keyword(acl_type);                                  BUG();
                                 if (!keyword) continue;  
                                 if (acltype2paths(acl_type) == 2) {  
                                         struct double_acl_record *ptr2 = container_of(ptr, struct double_acl_record, head);  
                                         const bool b0 = ptr2->u1_is_group, b1 = ptr2->u2_is_group;  
                                         if (io_printf(head, "allow_%s %s%s %s%s", keyword,  
                                                       b0 ? "@" : "", b0 ? ptr2->u1.group1->group_name->name : ptr2->u1.filename1->name,  
                                                       b1 ? "@" : "", b1 ? ptr2->u2.group2->group_name->name : ptr2->u2.filename2->name)) goto print_acl_rollback;  
                                 } else {  
                                         struct single_acl_record *ptr2 = container_of(ptr, struct single_acl_record, head);  
                                         const bool b = ptr2->u_is_group;  
                                         if (io_printf(head, "allow_%s %s%s", keyword,  
                                                       b ? "@" : "", b ? ptr2->u.group->group_name->name : ptr2->u.filename->name)) goto print_acl_rollback;  
                                 }  
                         }  
                         if (DumpCondition(head, ptr->cond)) {  
                         print_acl_rollback: ;  
                         head->read_avail = pos;  
                         return 0;  
1048                          }                          }
1049                  }                  }
1050                  head->read_step = 3;                  head->read_step = 3;
# Line 957  static int ReadDomainPolicy(struct io_bu Line 1052  static int ReadDomainPolicy(struct io_bu
1052                  if (io_printf(head, "\n")) return 0;                  if (io_printf(head, "\n")) return 0;
1053                  head->read_step = 1;                  head->read_step = 1;
1054          }          }
1055          head->read_eof = 1;          head->read_eof = true;
1056          return 0;          return 0;
1057  }  }
1058    
# Line 969  static int UpdateDomainProfile(struct io Line 1064  static int UpdateDomainProfile(struct io
1064          char *cp = strchr(data, ' ');          char *cp = strchr(data, ' ');
1065          struct domain_info *domain;          struct domain_info *domain;
1066          unsigned int profile;          unsigned int profile;
         if (!isRoot()) return -EPERM;  
1067          if (!cp) return -EINVAL;          if (!cp) return -EINVAL;
1068          *cp = '\0';          *cp = '\0';
1069          domain = FindDomain(cp + 1);          domain = FindDomain(cp + 1);
# Line 983  static int ReadDomainProfile(struct io_b Line 1077  static int ReadDomainProfile(struct io_b
1077  {  {
1078          struct list1_head *pos;          struct list1_head *pos;
1079          if (head->read_eof) return 0;          if (head->read_eof) return 0;
         if (!isRoot()) return -EPERM;  
1080          list1_for_each_cookie(pos, head->read_var1, &domain_list) {          list1_for_each_cookie(pos, head->read_var1, &domain_list) {
1081                  struct domain_info *domain;                  struct domain_info *domain;
1082                  domain = list1_entry(pos, struct domain_info, list);                  domain = list1_entry(pos, struct domain_info, list);
1083                  if (domain->is_deleted) continue;                  if (domain->is_deleted) continue;
1084                  if (io_printf(head, "%u %s\n", domain->profile, domain->domainname->name)) return 0;                  if (io_printf(head, "%u %s\n", domain->profile, domain->domainname->name)) return 0;
1085          }          }
1086          head->read_eof = 1;          head->read_eof = true;
1087          return 0;          return 0;
1088  }  }
1089    
1090  static int WritePID(struct io_buffer *head)  static int WritePID(struct io_buffer *head)
1091  {  {
1092          head->read_step = (int) simple_strtoul(head->write_buf, NULL, 10);          head->read_step = (int) simple_strtoul(head->write_buf, NULL, 10);
1093          head->read_eof = 0;          head->read_eof = false;
1094          return 0;          return 0;
1095  }  }
1096    
# Line 1014  static int ReadPID(struct io_buffer *hea Line 1107  static int ReadPID(struct io_buffer *hea
1107                  read_unlock(&tasklist_lock);                  read_unlock(&tasklist_lock);
1108                  /***** CRITICAL SECTION END *****/                  /***** CRITICAL SECTION END *****/
1109                  if (domain) io_printf(head, "%d %u %s", pid, domain->profile, domain->domainname->name);                  if (domain) io_printf(head, "%d %u %s", pid, domain->profile, domain->domainname->name);
1110                  head->read_eof = 1;                  head->read_eof = true;
1111          }          }
1112          return 0;          return 0;
1113  }  }
# Line 1026  static int ReadPID(struct io_buffer *hea Line 1119  static int ReadPID(struct io_buffer *hea
1119  static int AddExceptionPolicy(struct io_buffer *head)  static int AddExceptionPolicy(struct io_buffer *head)
1120  {  {
1121          char *data = head->write_buf;          char *data = head->write_buf;
1122          bool is_delete = 0;          bool is_delete = false;
         if (!isRoot()) return -EPERM;  
1123          UpdateCounter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);          UpdateCounter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY);
1124          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
1125                  data += KEYWORD_DELETE_LEN;                  data += KEYWORD_DELETE_LEN;
1126                  is_delete = 1;                  is_delete = true;
1127          }          }
1128          if (strncmp(data, KEYWORD_KEEP_DOMAIN, KEYWORD_KEEP_DOMAIN_LEN) == 0) {          if (strncmp(data, KEYWORD_KEEP_DOMAIN, KEYWORD_KEEP_DOMAIN_LEN) == 0) {
1129                  return AddDomainKeeperPolicy(data + KEYWORD_KEEP_DOMAIN_LEN, 0, is_delete);                  return AddDomainKeeperPolicy(data + KEYWORD_KEEP_DOMAIN_LEN, 0, is_delete);
# Line 1050  static int AddExceptionPolicy(struct io_ Line 1142  static int AddExceptionPolicy(struct io_
1142          } else if (strncmp(data, KEYWORD_ALLOW_ENV, KEYWORD_ALLOW_ENV_LEN) == 0) {          } else if (strncmp(data, KEYWORD_ALLOW_ENV, KEYWORD_ALLOW_ENV_LEN) == 0) {
1143                  return AddGloballyUsableEnvPolicy(data + KEYWORD_ALLOW_ENV_LEN, is_delete);                  return AddGloballyUsableEnvPolicy(data + KEYWORD_ALLOW_ENV_LEN, is_delete);
1144          } else if (strncmp(data, KEYWORD_FILE_PATTERN, KEYWORD_FILE_PATTERN_LEN) == 0) {          } else if (strncmp(data, KEYWORD_FILE_PATTERN, KEYWORD_FILE_PATTERN_LEN) == 0) {
1145                  return AddPatternPolicy(data + KEYWORD_FILE_PATTERN_LEN, is_delete);                  return AddFilePatternPolicy(data + KEYWORD_FILE_PATTERN_LEN, is_delete);
1146          } else if (strncmp(data, KEYWORD_PATH_GROUP, KEYWORD_PATH_GROUP_LEN) == 0) {          } else if (strncmp(data, KEYWORD_PATH_GROUP, KEYWORD_PATH_GROUP_LEN) == 0) {
1147                  return AddPathGroupPolicy(data + KEYWORD_PATH_GROUP_LEN, is_delete);                  return AddPathGroupPolicy(data + KEYWORD_PATH_GROUP_LEN, is_delete);
1148          } else if (strncmp(data, KEYWORD_DENY_REWRITE, KEYWORD_DENY_REWRITE_LEN) == 0) {          } else if (strncmp(data, KEYWORD_DENY_REWRITE, KEYWORD_DENY_REWRITE_LEN) == 0) {
# Line 1066  static int ReadExceptionPolicy(struct io Line 1158  static int ReadExceptionPolicy(struct io
1158          if (!head->read_eof) {          if (!head->read_eof) {
1159                  switch (head->read_step) {                  switch (head->read_step) {
1160                  case 0:                  case 0:
                         if (!isRoot()) return -EPERM;  
1161                          head->read_var2 = NULL; head->read_step = 1;                          head->read_var2 = NULL; head->read_step = 1;
1162                  case 1:                  case 1:
1163                          if (ReadDomainKeeperPolicy(head)) break;                          if (ReadDomainKeeperPolicy(head)) break;
# Line 1087  static int ReadExceptionPolicy(struct io Line 1178  static int ReadExceptionPolicy(struct io
1178                          if (ReadAggregatorPolicy(head)) break;                          if (ReadAggregatorPolicy(head)) break;
1179                          head->read_var2 = NULL; head->read_step = 7;                          head->read_var2 = NULL; head->read_step = 7;
1180                  case 7:                  case 7:
1181                          if (ReadPatternPolicy(head)) break;                          if (ReadFilePatternPolicy(head)) break;
1182                          head->read_var2 = NULL; head->read_step = 8;                          head->read_var2 = NULL; head->read_step = 8;
1183                  case 8:                  case 8:
1184                          if (ReadNoRewritePolicy(head)) break;                          if (ReadNoRewritePolicy(head)) break;
# Line 1097  static int ReadExceptionPolicy(struct io Line 1188  static int ReadExceptionPolicy(struct io
1188                          head->read_var1 = head->read_var2 = NULL; head->read_step = 10;                          head->read_var1 = head->read_var2 = NULL; head->read_step = 10;
1189                  case 10:                  case 10:
1190                          if (ReadAddressGroupPolicy(head)) break;                          if (ReadAddressGroupPolicy(head)) break;
1191                          head->read_eof = 1;                          head->read_eof = true;
1192                          break;                          break;
1193                  default:                  default:
1194                          return -EINVAL;                          return -EINVAL;
# Line 1115  static int ReadExceptionPolicy(struct io Line 1206  static int ReadExceptionPolicy(struct io
1206  static int AddSystemPolicy(struct io_buffer *head)  static int AddSystemPolicy(struct io_buffer *head)
1207  {  {
1208          char *data = head->write_buf;          char *data = head->write_buf;
1209          bool is_delete = 0;          bool is_delete = false;
         if (!isRoot()) return -EPERM;  
1210          UpdateCounter(CCS_UPDATES_COUNTER_SYSTEM_POLICY);          UpdateCounter(CCS_UPDATES_COUNTER_SYSTEM_POLICY);
1211          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {          if (strncmp(data, KEYWORD_DELETE, KEYWORD_DELETE_LEN) == 0) {
1212                  data += KEYWORD_DELETE_LEN;                  data += KEYWORD_DELETE_LEN;
1213                  is_delete = 1;                  is_delete = true;
1214          }          }
1215          if (strncmp(data, KEYWORD_ALLOW_MOUNT, KEYWORD_ALLOW_MOUNT_LEN) == 0)          if (strncmp(data, KEYWORD_ALLOW_MOUNT, KEYWORD_ALLOW_MOUNT_LEN) == 0)
1216                  return AddMountPolicy(data + KEYWORD_ALLOW_MOUNT_LEN, is_delete);                  return AddMountPolicy(data + KEYWORD_ALLOW_MOUNT_LEN, is_delete);
# Line 1140  static int ReadSystemPolicy(struct io_bu Line 1230  static int ReadSystemPolicy(struct io_bu
1230          if (!head->read_eof) {          if (!head->read_eof) {
1231                  switch (head->read_step) {                  switch (head->read_step) {
1232                  case 0:                  case 0:
                         if (!isRoot()) return -EPERM;  
1233                          head->read_var2 = NULL; head->read_step = 1;                          head->read_var2 = NULL; head->read_step = 1;
1234                  case 1:                  case 1:
1235                          if (ReadMountPolicy(head)) break;                          if (ReadMountPolicy(head)) break;
# Line 1156  static int ReadSystemPolicy(struct io_bu Line 1245  static int ReadSystemPolicy(struct io_bu
1245                          head->read_var2 = NULL; head->read_step = 5;                          head->read_var2 = NULL; head->read_step = 5;
1246                  case 5:                  case 5:
1247                          if (ReadReservedPortPolicy(head)) break;                          if (ReadReservedPortPolicy(head)) break;
1248                          head->read_eof = 1;                          head->read_eof = true;
1249                          break;                          break;
1250                  default:                  default:
1251                          return -EINVAL;                          return -EINVAL;
# Line 1169  static int ReadSystemPolicy(struct io_bu Line 1258  static int ReadSystemPolicy(struct io_bu
1258    
1259  /*************************  POLICY LOADER  *************************/  /*************************  POLICY LOADER  *************************/
1260    
1261  static int profile_loaded = 0;  static bool profile_loaded = false;
1262    
1263  static const char *ccs_loader = NULL;  static const char *ccs_loader = NULL;
1264    
# Line 1204  void CCS_LoadPolicy(const char *filename Line 1293  void CCS_LoadPolicy(const char *filename
1293                          printk("Not activating Mandatory Access Control now since %s doesn't exist.\n", ccs_loader);                          printk("Not activating Mandatory Access Control now since %s doesn't exist.\n", ccs_loader);
1294                          return;                          return;
1295                  }                  }
1296    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
1297                    path_put(&nd.path);
1298    #else
1299                  path_release(&nd);                  path_release(&nd);
1300    #endif
1301          }          }
1302          if (!profile_loaded) {          if (!profile_loaded) {
1303                  char *argv[2], *envp[3];                  char *argv[2], *envp[3];
# Line 1225  void CCS_LoadPolicy(const char *filename Line 1318  void CCS_LoadPolicy(const char *filename
1318                  }                  }
1319          }          }
1320  #ifdef CONFIG_SAKURA  #ifdef CONFIG_SAKURA
1321          printk("SAKURA: 1.5.3-pre   2007/12/18\n");          printk("SAKURA: 1.6.0-pre   2008/02/26\n");
1322  #endif  #endif
1323  #ifdef CONFIG_TOMOYO  #ifdef CONFIG_TOMOYO
1324          printk("TOMOYO: 1.5.3-pre   2007/12/17\n");          printk("TOMOYO: 1.6.0-pre   2008/02/28\n");
1325  #endif  #endif
         //if (!profile_loaded) panic("No profiles loaded. Run policy loader using 'init=' option.\n");  
1326          printk("Mandatory Access Control activated.\n");          printk("Mandatory Access Control activated.\n");
1327          sbin_init_started = 1;          sbin_init_started = true;
1328          ccs_log_level = KERN_WARNING;          ccs_log_level = KERN_WARNING;
1329          { /* Check all profiles currently assigned to domains are defined. */          { /* Check all profiles currently assigned to domains are defined. */
1330                  struct domain_info *domain;                  struct domain_info *domain;
# Line 1472  static int ReadUpdatesCounter(struct io_ Line 1564  static int ReadUpdatesCounter(struct io_
1564                                    counter[CCS_UPDATES_COUNTER_MANAGER],                                    counter[CCS_UPDATES_COUNTER_MANAGER],
1565                                    counter[CCS_UPDATES_COUNTER_GRANT_LOG],                                    counter[CCS_UPDATES_COUNTER_GRANT_LOG],
1566                                    counter[CCS_UPDATES_COUNTER_REJECT_LOG]);                                    counter[CCS_UPDATES_COUNTER_REJECT_LOG]);
1567                  head->read_eof = 1;                  head->read_eof = true;
1568          }          }
1569          return 0;          return 0;
1570  }  }
# Line 1480  static int ReadUpdatesCounter(struct io_ Line 1572  static int ReadUpdatesCounter(struct io_
1572  static int ReadVersion(struct io_buffer *head)  static int ReadVersion(struct io_buffer *head)
1573  {  {
1574          if (!head->read_eof) {          if (!head->read_eof) {
1575                  if (io_printf(head, "1.5.3-pre") == 0) head->read_eof = 1;                  if (io_printf(head, "1.6.0-pre") == 0) head->read_eof = true;
1576          }          }
1577          return 0;          return 0;
1578  }  }
# Line 1489  static int ReadMemoryCounter(struct io_b Line 1581  static int ReadMemoryCounter(struct io_b
1581  {  {
1582          if (!head->read_eof) {          if (!head->read_eof) {
1583                  const int shared = GetMemoryUsedForSaveName(), private = GetMemoryUsedForElements(), dynamic = GetMemoryUsedForDynamic();                  const int shared = GetMemoryUsedForSaveName(), private = GetMemoryUsedForElements(), dynamic = GetMemoryUsedForDynamic();
1584                  if (io_printf(head, "Shared:  %10u\nPrivate: %10u\nDynamic: %10u\nTotal:   %10u\n", shared, private, dynamic, shared + private + dynamic) == 0) head->read_eof = 1;                  if (io_printf(head, "Shared:  %10u\nPrivate: %10u\nDynamic: %10u\nTotal:   %10u\n", shared, private, dynamic, shared + private + dynamic) == 0) head->read_eof = true;
1585          }          }
1586          return 0;          return 0;
1587  }  }
# Line 1498  static int ReadSelfDomain(struct io_buff Line 1590  static int ReadSelfDomain(struct io_buff
1590  {  {
1591          if (!head->read_eof) {          if (!head->read_eof) {
1592                  io_printf(head, "%s", current->domain_info->domainname->name);                  io_printf(head, "%s", current->domain_info->domainname->name);
1593                  head->read_eof = 1;                  head->read_eof = true;
1594          }          }
1595          return 0;          return 0;
1596  }  }
1597    
1598  int CCS_OpenControl(const int type, struct file *file)  int CCS_OpenControl(const u8 type, struct file *file)
1599  {  {
1600          struct io_buffer *head = ccs_alloc(sizeof(*head));          struct io_buffer *head = ccs_alloc(sizeof(*head));
1601          if (!head) return -ENOMEM;          if (!head) return -ENOMEM;
# Line 1632  int CCS_WriteControl(struct file *file, Line 1724  int CCS_WriteControl(struct file *file,
1724          char *cp0 = head->write_buf;          char *cp0 = head->write_buf;
1725          if (!head->write) return -ENOSYS;          if (!head->write) return -ENOSYS;
1726          if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT;          if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT;
         if (!isRoot()) return -EPERM;  
1727          if (head->write != WritePID && !IsPolicyManager()) {          if (head->write != WritePID && !IsPolicyManager()) {
1728                  return -EPERM; /* Forbid updating policies for non manager programs. */                  return -EPERM; /* Forbid updating policies for non manager programs. */
1729          }          }
# Line 1663  int CCS_CloseControl(struct file *file) Line 1754  int CCS_CloseControl(struct file *file)
1754  {  {
1755          struct io_buffer *head = file->private_data;          struct io_buffer *head = file->private_data;
1756          if (head->write == WriteAnswer) atomic_dec(&queryd_watcher);          if (head->write == WriteAnswer) atomic_dec(&queryd_watcher);
1757          else if (head->read == ReadMemoryCounter) profile_loaded = 1;          else if (head->read == ReadMemoryCounter) profile_loaded = true;
1758          ccs_free(head->read_buf); head->read_buf = NULL;          ccs_free(head->read_buf); head->read_buf = NULL;
1759          ccs_free(head->write_buf); head->write_buf = NULL;          ccs_free(head->write_buf); head->write_buf = NULL;
1760          ccs_free(head); head = NULL;          ccs_free(head); head = NULL;
1761          file->private_data = NULL;          file->private_data = NULL;
1762          return 0;          return 0;
1763  }  }
1764    
1765    void *alloc_acl_element(const u8 acl_type, const struct condition_list *condition)
1766    {
1767            int len;
1768            struct acl_info *ptr;
1769            switch (acl_type) {
1770            case TYPE_SINGLE_PATH_ACL:
1771                    len = sizeof(struct single_path_acl_record);
1772                    break;
1773            case TYPE_DOUBLE_PATH_ACL:
1774                    len = sizeof(struct double_path_acl_record);
1775                    break;
1776            case TYPE_ARGV0_ACL:
1777                    len = sizeof(struct argv0_acl_record);
1778                    break;
1779            case TYPE_ENV_ACL:
1780                    len = sizeof(struct env_acl_record);
1781                    break;
1782            case TYPE_CAPABILITY_ACL:
1783                    len = sizeof(struct capability_acl_record);
1784                    break;
1785            case TYPE_IP_NETWORK_ACL:
1786                    len = sizeof(struct ip_network_acl_record);
1787                    break;
1788            case TYPE_SIGNAL_ACL:
1789                    len = sizeof(struct signal_acl_record);
1790                    break;
1791            default:
1792                    return NULL;
1793            }
1794            if (!condition) len -= sizeof(ptr->cond);
1795            ptr = alloc_element(len);
1796            if (!ptr) return NULL;
1797            if (condition) {
1798                    ptr->cond = condition;
1799                    ptr->type = acl_type | ACL_WITH_CONDITION;
1800                    return ptr;
1801            }
1802            ptr = (void *) (((u8 *) ptr) - sizeof(ptr->cond));
1803            ptr->type = acl_type;
1804            return ptr;
1805    }
1806    
1807    const struct condition_list *GetConditionPart(const struct acl_info *acl)
1808    {
1809            return (acl->type & ACL_WITH_CONDITION) ? acl->cond : NULL;
1810    }

Legend:
Removed from v.813  
changed lines
  Added in v.1007

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