Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
util.c
Go to the documentation of this file.
1 /*
2  * security/tomoyo/util.c
3  *
4  * Copyright (C) 2005-2011 NTT DATA CORPORATION
5  */
6 
7 #include <linux/slab.h>
8 #include "common.h"
9 
10 /* Lock for protecting policy. */
11 DEFINE_MUTEX(tomoyo_policy_lock);
12 
13 /* Has /sbin/init started? */
15 
16 /*
17  * Mapping table from "enum tomoyo_mac_index" to
18  * "enum tomoyo_mac_category_index".
19  */
21  /* CONFIG::file group */
45  /* CONFIG::network group */
76  /* CONFIG::misc group */
78 };
79 
91 {
92  static const u16 tomoyo_eom[2][12] = {
93  { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
94  { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
95  };
96  u16 y;
97  u8 m;
98  bool r;
99  stamp->sec = time % 60;
100  time /= 60;
101  stamp->min = time % 60;
102  time /= 60;
103  stamp->hour = time % 24;
104  time /= 24;
105  for (y = 1970; ; y++) {
106  const unsigned short days = (y & 3) ? 365 : 366;
107  if (time < days)
108  break;
109  time -= days;
110  }
111  r = (y & 3) == 0;
112  for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++)
113  ;
114  if (m)
115  time -= tomoyo_eom[r][m - 1];
116  stamp->year = y;
117  stamp->month = ++m;
118  stamp->day = ++time;
119 }
120 
131 bool tomoyo_permstr(const char *string, const char *keyword)
132 {
133  const char *cp = strstr(string, keyword);
134  if (cp)
135  return cp == string || *(cp - 1) == '/';
136  return false;
137 }
138 
150 {
151  char *pos = param->data;
152  char *del = strchr(pos, ' ');
153  if (del)
154  *del++ = '\0';
155  else
156  del = pos + strlen(pos);
157  param->data = del;
158  return pos;
159 }
160 
170 {
171  char *start = param->data;
172  char *pos = start;
173  while (*pos) {
174  if (*pos++ != ' ' || *pos++ == '/')
175  continue;
176  pos -= 2;
177  *pos++ = '\0';
178  break;
179  }
180  param->data = pos;
181  if (tomoyo_correct_domain(start))
182  return tomoyo_get_name(start);
183  return NULL;
184 }
185 
197 u8 tomoyo_parse_ulong(unsigned long *result, char **str)
198 {
199  const char *cp = *str;
200  char *ep;
201  int base = 10;
202  if (*cp == '0') {
203  char c = *(cp + 1);
204  if (c == 'x' || c == 'X') {
205  base = 16;
206  cp += 2;
207  } else if (c >= '0' && c <= '7') {
208  base = 8;
209  cp++;
210  }
211  }
212  *result = simple_strtoul(cp, &ep, base);
213  if (cp == ep)
215  *str = ep;
216  switch (base) {
217  case 16:
219  case 8:
221  default:
223  }
224 }
225 
236 void tomoyo_print_ulong(char *buffer, const int buffer_len,
237  const unsigned long value, const u8 type)
238 {
239  if (type == TOMOYO_VALUE_TYPE_DECIMAL)
240  snprintf(buffer, buffer_len, "%lu", value);
241  else if (type == TOMOYO_VALUE_TYPE_OCTAL)
242  snprintf(buffer, buffer_len, "0%lo", value);
243  else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL)
244  snprintf(buffer, buffer_len, "0x%lX", value);
245  else
246  snprintf(buffer, buffer_len, "type(%u)", type);
247 }
248 
258  struct tomoyo_name_union *ptr)
259 {
260  char *filename;
261  if (param->data[0] == '@') {
262  param->data++;
264  return ptr->group != NULL;
265  }
266  filename = tomoyo_read_token(param);
267  if (!tomoyo_correct_word(filename))
268  return false;
269  ptr->filename = tomoyo_get_name(filename);
270  return ptr->filename != NULL;
271 }
272 
282  struct tomoyo_number_union *ptr)
283 {
284  char *data;
285  u8 type;
286  unsigned long v;
287  memset(ptr, 0, sizeof(*ptr));
288  if (param->data[0] == '@') {
289  param->data++;
291  return ptr->group != NULL;
292  }
293  data = tomoyo_read_token(param);
294  type = tomoyo_parse_ulong(&v, &data);
295  if (type == TOMOYO_VALUE_TYPE_INVALID)
296  return false;
297  ptr->values[0] = v;
298  ptr->value_type[0] = type;
299  if (!*data) {
300  ptr->values[1] = v;
301  ptr->value_type[1] = type;
302  return true;
303  }
304  if (*data++ != '-')
305  return false;
306  type = tomoyo_parse_ulong(&v, &data);
307  if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
308  return false;
309  ptr->values[1] = v;
310  ptr->value_type[1] = type;
311  return true;
312 }
313 
324 static inline bool tomoyo_byte_range(const char *str)
325 {
326  return *str >= '0' && *str++ <= '3' &&
327  *str >= '0' && *str++ <= '7' &&
328  *str >= '0' && *str <= '7';
329 }
330 
338 static inline bool tomoyo_alphabet_char(const char c)
339 {
340  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
341 }
342 
352 static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
353 {
354  return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
355 }
356 
364 static inline bool tomoyo_valid(const unsigned char c)
365 {
366  return c > ' ' && c < 127;
367 }
368 
376 static inline bool tomoyo_invalid(const unsigned char c)
377 {
378  return c && (c <= ' ' || c >= 127);
379 }
380 
392 bool tomoyo_str_starts(char **src, const char *find)
393 {
394  const int len = strlen(find);
395  char *tmp = *src;
396 
397  if (strncmp(tmp, find, len))
398  return false;
399  tmp += len;
400  *src = tmp;
401  return true;
402 }
403 
414 void tomoyo_normalize_line(unsigned char *buffer)
415 {
416  unsigned char *sp = buffer;
417  unsigned char *dp = buffer;
418  bool first = true;
419 
420  while (tomoyo_invalid(*sp))
421  sp++;
422  while (*sp) {
423  if (!first)
424  *dp++ = ' ';
425  first = false;
426  while (tomoyo_valid(*sp))
427  *dp++ = *sp++;
428  while (tomoyo_invalid(*sp))
429  sp++;
430  }
431  *dp = '\0';
432 }
433 
443 static bool tomoyo_correct_word2(const char *string, size_t len)
444 {
445  const char *const start = string;
446  bool in_repetition = false;
447  unsigned char c;
448  unsigned char d;
449  unsigned char e;
450  if (!len)
451  goto out;
452  while (len--) {
453  c = *string++;
454  if (c == '\\') {
455  if (!len--)
456  goto out;
457  c = *string++;
458  switch (c) {
459  case '\\': /* "\\" */
460  continue;
461  case '$': /* "\$" */
462  case '+': /* "\+" */
463  case '?': /* "\?" */
464  case '*': /* "\*" */
465  case '@': /* "\@" */
466  case 'x': /* "\x" */
467  case 'X': /* "\X" */
468  case 'a': /* "\a" */
469  case 'A': /* "\A" */
470  case '-': /* "\-" */
471  continue;
472  case '{': /* "/\{" */
473  if (string - 3 < start || *(string - 3) != '/')
474  break;
475  in_repetition = true;
476  continue;
477  case '}': /* "\}/" */
478  if (*string != '/')
479  break;
480  if (!in_repetition)
481  break;
482  in_repetition = false;
483  continue;
484  case '0': /* "\ooo" */
485  case '1':
486  case '2':
487  case '3':
488  if (!len-- || !len--)
489  break;
490  d = *string++;
491  e = *string++;
492  if (d < '0' || d > '7' || e < '0' || e > '7')
493  break;
494  c = tomoyo_make_byte(c, d, e);
495  if (c <= ' ' || c >= 127)
496  continue;
497  }
498  goto out;
499  } else if (in_repetition && c == '/') {
500  goto out;
501  } else if (c <= ' ' || c >= 127) {
502  goto out;
503  }
504  }
505  if (in_repetition)
506  goto out;
507  return true;
508  out:
509  return false;
510 }
511 
520 bool tomoyo_correct_word(const char *string)
521 {
522  return tomoyo_correct_word2(string, strlen(string));
523 }
524 
533 bool tomoyo_correct_path(const char *filename)
534 {
535  return *filename == '/' && tomoyo_correct_word(filename);
536 }
537 
545 bool tomoyo_correct_domain(const unsigned char *domainname)
546 {
547  if (!domainname || !tomoyo_domain_def(domainname))
548  return false;
549  domainname = strchr(domainname, ' ');
550  if (!domainname++)
551  return true;
552  while (1) {
553  const unsigned char *cp = strchr(domainname, ' ');
554  if (!cp)
555  break;
556  if (*domainname != '/' ||
557  !tomoyo_correct_word2(domainname, cp - domainname))
558  return false;
559  domainname = cp + 1;
560  }
561  return tomoyo_correct_path(domainname);
562 }
563 
571 bool tomoyo_domain_def(const unsigned char *buffer)
572 {
573  const unsigned char *cp;
574  int len;
575  if (*buffer != '<')
576  return false;
577  cp = strchr(buffer, ' ');
578  if (!cp)
579  len = strlen(buffer);
580  else
581  len = cp - buffer;
582  if (buffer[len - 1] != '>' ||
583  !tomoyo_correct_word2(buffer + 1, len - 2))
584  return false;
585  return true;
586 }
587 
598 {
599  struct tomoyo_domain_info *domain;
600  struct tomoyo_path_info name;
601 
602  name.name = domainname;
603  tomoyo_fill_path_info(&name);
604  list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
605  if (!domain->is_deleted &&
606  !tomoyo_pathcmp(&name, domain->domainname))
607  return domain;
608  }
609  return NULL;
610 }
611 
619 static int tomoyo_const_part_length(const char *filename)
620 {
621  char c;
622  int len = 0;
623 
624  if (!filename)
625  return 0;
626  while ((c = *filename++) != '\0') {
627  if (c != '\\') {
628  len++;
629  continue;
630  }
631  c = *filename++;
632  switch (c) {
633  case '\\': /* "\\" */
634  len += 2;
635  continue;
636  case '0': /* "\ooo" */
637  case '1':
638  case '2':
639  case '3':
640  c = *filename++;
641  if (c < '0' || c > '7')
642  break;
643  c = *filename++;
644  if (c < '0' || c > '7')
645  break;
646  len += 4;
647  continue;
648  }
649  break;
650  }
651  return len;
652 }
653 
662 {
663  const char *name = ptr->name;
664  const int len = strlen(name);
665 
666  ptr->const_len = tomoyo_const_part_length(name);
667  ptr->is_dir = len && (name[len - 1] == '/');
668  ptr->is_patterned = (ptr->const_len < len);
669  ptr->hash = full_name_hash(name, len);
670 }
671 
682 static bool tomoyo_file_matches_pattern2(const char *filename,
683  const char *filename_end,
684  const char *pattern,
685  const char *pattern_end)
686 {
687  while (filename < filename_end && pattern < pattern_end) {
688  char c;
689  if (*pattern != '\\') {
690  if (*filename++ != *pattern++)
691  return false;
692  continue;
693  }
694  c = *filename;
695  pattern++;
696  switch (*pattern) {
697  int i;
698  int j;
699  case '?':
700  if (c == '/') {
701  return false;
702  } else if (c == '\\') {
703  if (filename[1] == '\\')
704  filename++;
705  else if (tomoyo_byte_range(filename + 1))
706  filename += 3;
707  else
708  return false;
709  }
710  break;
711  case '\\':
712  if (c != '\\')
713  return false;
714  if (*++filename != '\\')
715  return false;
716  break;
717  case '+':
718  if (!isdigit(c))
719  return false;
720  break;
721  case 'x':
722  if (!isxdigit(c))
723  return false;
724  break;
725  case 'a':
726  if (!tomoyo_alphabet_char(c))
727  return false;
728  break;
729  case '0':
730  case '1':
731  case '2':
732  case '3':
733  if (c == '\\' && tomoyo_byte_range(filename + 1)
734  && strncmp(filename + 1, pattern, 3) == 0) {
735  filename += 3;
736  pattern += 2;
737  break;
738  }
739  return false; /* Not matched. */
740  case '*':
741  case '@':
742  for (i = 0; i <= filename_end - filename; i++) {
743  if (tomoyo_file_matches_pattern2(
744  filename + i, filename_end,
745  pattern + 1, pattern_end))
746  return true;
747  c = filename[i];
748  if (c == '.' && *pattern == '@')
749  break;
750  if (c != '\\')
751  continue;
752  if (filename[i + 1] == '\\')
753  i++;
754  else if (tomoyo_byte_range(filename + i + 1))
755  i += 3;
756  else
757  break; /* Bad pattern. */
758  }
759  return false; /* Not matched. */
760  default:
761  j = 0;
762  c = *pattern;
763  if (c == '$') {
764  while (isdigit(filename[j]))
765  j++;
766  } else if (c == 'X') {
767  while (isxdigit(filename[j]))
768  j++;
769  } else if (c == 'A') {
770  while (tomoyo_alphabet_char(filename[j]))
771  j++;
772  }
773  for (i = 1; i <= j; i++) {
774  if (tomoyo_file_matches_pattern2(
775  filename + i, filename_end,
776  pattern + 1, pattern_end))
777  return true;
778  }
779  return false; /* Not matched or bad pattern. */
780  }
781  filename++;
782  pattern++;
783  }
784  while (*pattern == '\\' &&
785  (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
786  pattern += 2;
787  return filename == filename_end && pattern == pattern_end;
788 }
789 
800 static bool tomoyo_file_matches_pattern(const char *filename,
801  const char *filename_end,
802  const char *pattern,
803  const char *pattern_end)
804 {
805  const char *pattern_start = pattern;
806  bool first = true;
807  bool result;
808 
809  while (pattern < pattern_end - 1) {
810  /* Split at "\-" pattern. */
811  if (*pattern++ != '\\' || *pattern++ != '-')
812  continue;
813  result = tomoyo_file_matches_pattern2(filename,
814  filename_end,
815  pattern_start,
816  pattern - 2);
817  if (first)
818  result = !result;
819  if (result)
820  return false;
821  first = false;
822  pattern_start = pattern;
823  }
824  result = tomoyo_file_matches_pattern2(filename, filename_end,
825  pattern_start, pattern_end);
826  return first ? result : !result;
827 }
828 
837 static bool tomoyo_path_matches_pattern2(const char *f, const char *p)
838 {
839  const char *f_delimiter;
840  const char *p_delimiter;
841 
842  while (*f && *p) {
843  f_delimiter = strchr(f, '/');
844  if (!f_delimiter)
845  f_delimiter = f + strlen(f);
846  p_delimiter = strchr(p, '/');
847  if (!p_delimiter)
848  p_delimiter = p + strlen(p);
849  if (*p == '\\' && *(p + 1) == '{')
850  goto recursive;
851  if (!tomoyo_file_matches_pattern(f, f_delimiter, p,
852  p_delimiter))
853  return false;
854  f = f_delimiter;
855  if (*f)
856  f++;
857  p = p_delimiter;
858  if (*p)
859  p++;
860  }
861  /* Ignore trailing "\*" and "\@" in @pattern. */
862  while (*p == '\\' &&
863  (*(p + 1) == '*' || *(p + 1) == '@'))
864  p += 2;
865  return !*f && !*p;
866  recursive:
867  /*
868  * The "\{" pattern is permitted only after '/' character.
869  * This guarantees that below "*(p - 1)" is safe.
870  * Also, the "\}" pattern is permitted only before '/' character
871  * so that "\{" + "\}" pair will not break the "\-" operator.
872  */
873  if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
874  *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
875  return false; /* Bad pattern. */
876  do {
877  /* Compare current component with pattern. */
878  if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2,
879  p_delimiter - 2))
880  break;
881  /* Proceed to next component. */
882  f = f_delimiter;
883  if (!*f)
884  break;
885  f++;
886  /* Continue comparison. */
887  if (tomoyo_path_matches_pattern2(f, p_delimiter + 1))
888  return true;
889  f_delimiter = strchr(f, '/');
890  } while (f_delimiter);
891  return false; /* Not matched. */
892 }
893 
920 bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
921  const struct tomoyo_path_info *pattern)
922 {
923  const char *f = filename->name;
924  const char *p = pattern->name;
925  const int len = pattern->const_len;
926 
927  /* If @pattern doesn't contain pattern, I can use strcmp(). */
928  if (!pattern->is_patterned)
929  return !tomoyo_pathcmp(filename, pattern);
930  /* Don't compare directory and non-directory. */
931  if (filename->is_dir != pattern->is_dir)
932  return false;
933  /* Compare the initial length without patterns. */
934  if (strncmp(f, p, len))
935  return false;
936  f += len;
937  p += len;
938  return tomoyo_path_matches_pattern2(f, p);
939 }
940 
949 const char *tomoyo_get_exe(void)
950 {
951  struct mm_struct *mm = current->mm;
952  const char *cp = NULL;
953 
954  if (!mm)
955  return NULL;
956  down_read(&mm->mmap_sem);
957  if (mm->exe_file)
958  cp = tomoyo_realpath_from_path(&mm->exe_file->f_path);
959  up_read(&mm->mmap_sem);
960  return cp;
961 }
962 
973  const u8 index)
974 {
975  u8 mode;
976  struct tomoyo_profile *p;
977 
979  return TOMOYO_CONFIG_DISABLED;
980  p = tomoyo_profile(ns, profile);
981  mode = p->config[index];
982  if (mode == TOMOYO_CONFIG_USE_DEFAULT)
985  if (mode == TOMOYO_CONFIG_USE_DEFAULT)
986  mode = p->default_config;
987  return mode & 3;
988 }
989 
1000  struct tomoyo_domain_info *domain, const u8 index)
1001 {
1002  u8 profile;
1003  memset(r, 0, sizeof(*r));
1004  if (!domain)
1005  domain = tomoyo_domain();
1006  r->domain = domain;
1007  profile = domain->profile;
1008  r->profile = profile;
1009  r->type = index;
1010  r->mode = tomoyo_get_mode(domain->ns, profile, index);
1011  return r->mode;
1012 }
1013 
1024 {
1025  unsigned int count = 0;
1026  struct tomoyo_domain_info *domain = r->domain;
1027  struct tomoyo_acl_info *ptr;
1028 
1029  if (r->mode != TOMOYO_CONFIG_LEARNING)
1030  return false;
1031  if (!domain)
1032  return true;
1033  list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1034  u16 perm;
1035  u8 i;
1036  if (ptr->is_deleted)
1037  continue;
1038  switch (ptr->type) {
1039  case TOMOYO_TYPE_PATH_ACL:
1040  perm = container_of(ptr, struct tomoyo_path_acl, head)
1041  ->perm;
1042  break;
1043  case TOMOYO_TYPE_PATH2_ACL:
1044  perm = container_of(ptr, struct tomoyo_path2_acl, head)
1045  ->perm;
1046  break;
1048  perm = container_of(ptr, struct tomoyo_path_number_acl,
1049  head)->perm;
1050  break;
1051  case TOMOYO_TYPE_MKDEV_ACL:
1052  perm = container_of(ptr, struct tomoyo_mkdev_acl,
1053  head)->perm;
1054  break;
1055  case TOMOYO_TYPE_INET_ACL:
1056  perm = container_of(ptr, struct tomoyo_inet_acl,
1057  head)->perm;
1058  break;
1059  case TOMOYO_TYPE_UNIX_ACL:
1060  perm = container_of(ptr, struct tomoyo_unix_acl,
1061  head)->perm;
1062  break;
1064  perm = 0;
1065  break;
1066  default:
1067  perm = 1;
1068  }
1069  for (i = 0; i < 16; i++)
1070  if (perm & (1 << i))
1071  count++;
1072  }
1073  if (count < tomoyo_profile(domain->ns, domain->profile)->
1075  return true;
1076  if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) {
1077  domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true;
1078  /* r->granted = false; */
1080  printk(KERN_WARNING "WARNING: "
1081  "Domain '%s' has too many ACLs to hold. "
1082  "Stopped learning mode.\n", domain->domainname->name);
1083  }
1084  return false;
1085 }