Commit 2ca9bf453bdd478bcb6c01aa2d0bd4c2f4350563

Authored by Tetsuo Handa
Committed by James Morris
1 parent 8761afd49e

TOMOYO: Allow using executable's realpath and symlink's target as conditions.

This patch adds support for permission checks using executable file's realpath
upon execve() and symlink's target upon symlink(). Hooks are in the last patch
of this pathset.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>

Showing 5 changed files with 228 additions and 4 deletions Side-by-side Diff

security/tomoyo/audit.c
... ... @@ -140,6 +140,8 @@
140 140 {
141 141 char *buf = NULL;
142 142 const char *header = NULL;
  143 + char *realpath = NULL;
  144 + const char *symlink = NULL;
143 145 int pos;
144 146 const char *domainname = r->domain->domainname->name;
145 147 header = tomoyo_print_header(r);
146 148  
147 149  
... ... @@ -147,15 +149,34 @@
147 149 return NULL;
148 150 /* +10 is for '\n' etc. and '\0'. */
149 151 len += strlen(domainname) + strlen(header) + 10;
  152 + if (r->ee) {
  153 + struct file *file = r->ee->bprm->file;
  154 + realpath = tomoyo_realpath_from_path(&file->f_path);
  155 + if (!realpath)
  156 + goto out;
  157 + /* +80 is for " exec={ realpath=\"%s\" }" */
  158 + len += strlen(realpath) + 80;
  159 + } else if (r->obj && r->obj->symlink_target) {
  160 + symlink = r->obj->symlink_target->name;
  161 + /* +18 is for " symlink.target=\"%s\"" */
  162 + len += 18 + strlen(symlink);
  163 + }
150 164 len = tomoyo_round2(len);
151 165 buf = kzalloc(len, GFP_NOFS);
152 166 if (!buf)
153 167 goto out;
154 168 len--;
155 169 pos = snprintf(buf, len, "%s", header);
  170 + if (realpath) {
  171 + pos += snprintf(buf + pos, len - pos,
  172 + " exec={ realpath=\"%s\" }", realpath);
  173 + } else if (symlink)
  174 + pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
  175 + symlink);
156 176 pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
157 177 vsnprintf(buf + pos, len - pos, fmt, args);
158 178 out:
  179 + kfree(realpath);
159 180 kfree(header);
160 181 return buf;
161 182 }
security/tomoyo/common.c
... ... @@ -79,6 +79,8 @@
79 79 [TOMOYO_MODE_OTHERS_READ] = "others_read",
80 80 [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
81 81 [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
  82 + [TOMOYO_EXEC_REALPATH] = "exec.realpath",
  83 + [TOMOYO_SYMLINK_TARGET] = "symlink.target",
82 84 [TOMOYO_PATH1_UID] = "path1.uid",
83 85 [TOMOYO_PATH1_GID] = "path1.gid",
84 86 [TOMOYO_PATH1_INO] = "path1.ino",
... ... @@ -353,6 +355,27 @@
353 355 }
354 356  
355 357 /**
  358 + * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
  359 + *
  360 + * @head: Pointer to "struct tomoyo_io_buffer".
  361 + * @ptr: Pointer to "struct tomoyo_name_union".
  362 + *
  363 + * Returns nothing.
  364 + */
  365 +static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
  366 + const struct tomoyo_name_union *ptr)
  367 +{
  368 + if (ptr->group) {
  369 + tomoyo_set_string(head, "@");
  370 + tomoyo_set_string(head, ptr->group->group_name->name);
  371 + } else {
  372 + tomoyo_set_string(head, "\"");
  373 + tomoyo_set_string(head, ptr->filename->name);
  374 + tomoyo_set_string(head, "\"");
  375 + }
  376 +}
  377 +
  378 +/**
356 379 * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
357 380 *
358 381 * @head: Pointer to "struct tomoyo_io_buffer".
... ... @@ -1101,6 +1124,9 @@
1101 1124 (typeof(condp)) (cond + 1);
1102 1125 const struct tomoyo_number_union *numbers_p =
1103 1126 (typeof(numbers_p)) (condp + condc);
  1127 + const struct tomoyo_name_union *names_p =
  1128 + (typeof(names_p))
  1129 + (numbers_p + cond->numbers_count);
1104 1130 u16 skip;
1105 1131 for (skip = 0; skip < head->r.cond_index; skip++) {
1106 1132 const u8 left = condp->left;
... ... @@ -1112,6 +1138,9 @@
1112 1138 break;
1113 1139 }
1114 1140 switch (right) {
  1141 + case TOMOYO_NAME_UNION:
  1142 + names_p++;
  1143 + break;
1115 1144 case TOMOYO_NUMBER_UNION:
1116 1145 numbers_p++;
1117 1146 break;
... ... @@ -1138,6 +1167,10 @@
1138 1167 }
1139 1168 tomoyo_set_string(head, match ? "=" : "!=");
1140 1169 switch (right) {
  1170 + case TOMOYO_NAME_UNION:
  1171 + tomoyo_print_name_union_quoted
  1172 + (head, names_p++);
  1173 + break;
1141 1174 case TOMOYO_NUMBER_UNION:
1142 1175 tomoyo_print_number_union_nospace
1143 1176 (head, numbers_p++);
... ... @@ -1666,6 +1699,22 @@
1666 1699 static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
1667 1700  
1668 1701 /**
  1702 + * tomoyo_truncate - Truncate a line.
  1703 + *
  1704 + * @str: String to truncate.
  1705 + *
  1706 + * Returns length of truncated @str.
  1707 + */
  1708 +static int tomoyo_truncate(char *str)
  1709 +{
  1710 + char *start = str;
  1711 + while (*(unsigned char *) str > (unsigned char) ' ')
  1712 + str++;
  1713 + *str = '\0';
  1714 + return strlen(start) + 1;
  1715 +}
  1716 +
  1717 +/**
1669 1718 * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
1670 1719 *
1671 1720 * @domain: Pointer to "struct tomoyo_domain_info".
... ... @@ -1676,6 +1725,8 @@
1676 1725 static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
1677 1726 {
1678 1727 char *buffer;
  1728 + char *realpath = NULL;
  1729 + char *symlink = NULL;
1679 1730 char *cp = strchr(header, '\n');
1680 1731 int len;
1681 1732 if (!cp)
1682 1733  
... ... @@ -1685,10 +1736,25 @@
1685 1736 return;
1686 1737 *cp++ = '\0';
1687 1738 len = strlen(cp) + 1;
  1739 + /* strstr() will return NULL if ordering is wrong. */
  1740 + if (*cp == 'f') {
  1741 + realpath = strstr(header, " exec={ realpath=\"");
  1742 + if (realpath) {
  1743 + realpath += 8;
  1744 + len += tomoyo_truncate(realpath) + 6;
  1745 + }
  1746 + symlink = strstr(header, " symlink.target=\"");
  1747 + if (symlink)
  1748 + len += tomoyo_truncate(symlink + 1) + 1;
  1749 + }
1688 1750 buffer = kmalloc(len, GFP_NOFS);
1689 1751 if (!buffer)
1690 1752 return;
1691 1753 snprintf(buffer, len - 1, "%s", cp);
  1754 + if (realpath)
  1755 + tomoyo_addprintf(buffer, len, " exec.%s", realpath);
  1756 + if (symlink)
  1757 + tomoyo_addprintf(buffer, len, "%s", symlink);
1692 1758 tomoyo_normalize_line(buffer);
1693 1759 if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
1694 1760 false))
security/tomoyo/common.h
... ... @@ -73,6 +73,8 @@
73 73 TOMOYO_MODE_OTHERS_READ, /* S_IROTH */
74 74 TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */
75 75 TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */
  76 + TOMOYO_EXEC_REALPATH,
  77 + TOMOYO_SYMLINK_TARGET,
76 78 TOMOYO_PATH1_UID,
77 79 TOMOYO_PATH1_GID,
78 80 TOMOYO_PATH1_INO,
... ... @@ -101,6 +103,7 @@
101 103 TOMOYO_PATH2_PARENT_PERM,
102 104 TOMOYO_MAX_CONDITION_KEYWORD,
103 105 TOMOYO_NUMBER_UNION,
  106 + TOMOYO_NAME_UNION,
104 107 };
105 108  
106 109  
... ... @@ -351,6 +354,11 @@
351 354 * NULL if not dealing files.
352 355 */
353 356 struct tomoyo_obj_info *obj;
  357 + /*
  358 + * For holding parameters specific to execve() request.
  359 + * NULL if not dealing do_execve().
  360 + */
  361 + struct tomoyo_execve *ee;
354 362 struct tomoyo_domain_info *domain;
355 363 /* For holding parameters. */
356 364 union {
357 365  
... ... @@ -476,8 +484,22 @@
476 484 * parent directory.
477 485 */
478 486 struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
  487 + /*
  488 + * Content of symbolic link to be created. NULL for operations other
  489 + * than symlink().
  490 + */
  491 + struct tomoyo_path_info *symlink_target;
479 492 };
480 493  
  494 +/* Structure for execve() operation. */
  495 +struct tomoyo_execve {
  496 + struct tomoyo_request_info r;
  497 + struct tomoyo_obj_info obj;
  498 + struct linux_binprm *bprm;
  499 + /* For temporary use. */
  500 + char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
  501 +};
  502 +
481 503 /* Structure for entries which follows "struct tomoyo_condition". */
482 504 struct tomoyo_condition_element {
483 505 /* Left hand operand. */
484 506  
... ... @@ -494,9 +516,11 @@
494 516 u32 size; /* Memory size allocated for this entry. */
495 517 u16 condc; /* Number of conditions in this struct. */
496 518 u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
  519 + u16 names_count; /* Number of "struct tomoyo_name_union names". */
497 520 /*
498 521 * struct tomoyo_condition_element condition[condc];
499 522 * struct tomoyo_number_union values[numbers_count];
  523 + * struct tomoyo_name_union names[names_count];
500 524 */
501 525 };
502 526  
security/tomoyo/condition.c
... ... @@ -11,6 +11,68 @@
11 11 LIST_HEAD(tomoyo_condition_list);
12 12  
13 13 /**
  14 + * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
  15 + *
  16 + * @file: Pointer to "struct file".
  17 + * @ptr: Pointer to "struct tomoyo_name_union".
  18 + * @match: True if "exec.realpath=", false if "exec.realpath!=".
  19 + *
  20 + * Returns true on success, false otherwise.
  21 + */
  22 +static bool tomoyo_scan_exec_realpath(struct file *file,
  23 + const struct tomoyo_name_union *ptr,
  24 + const bool match)
  25 +{
  26 + bool result;
  27 + struct tomoyo_path_info exe;
  28 + if (!file)
  29 + return false;
  30 + exe.name = tomoyo_realpath_from_path(&file->f_path);
  31 + if (!exe.name)
  32 + return false;
  33 + tomoyo_fill_path_info(&exe);
  34 + result = tomoyo_compare_name_union(&exe, ptr);
  35 + kfree(exe.name);
  36 + return result == match;
  37 +}
  38 +
  39 +/**
  40 + * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
  41 + *
  42 + * @start: String to save.
  43 + *
  44 + * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
  45 + */
  46 +static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
  47 +{
  48 + char *cp = start + strlen(start) - 1;
  49 + if (cp == start || *start++ != '"' || *cp != '"')
  50 + return NULL;
  51 + *cp = '\0';
  52 + if (*start && !tomoyo_correct_word(start))
  53 + return NULL;
  54 + return tomoyo_get_name(start);
  55 +}
  56 +
  57 +/**
  58 + * tomoyo_parse_name_union_quoted - Parse a quoted word.
  59 + *
  60 + * @param: Pointer to "struct tomoyo_acl_param".
  61 + * @ptr: Pointer to "struct tomoyo_name_union".
  62 + *
  63 + * Returns true on success, false otherwise.
  64 + */
  65 +static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
  66 + struct tomoyo_name_union *ptr)
  67 +{
  68 + char *filename = param->data;
  69 + if (*filename == '@')
  70 + return tomoyo_parse_name_union(param, ptr);
  71 + ptr->filename = tomoyo_get_dqword(filename);
  72 + return ptr->filename != NULL;
  73 +}
  74 +
  75 +/**
14 76 * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
15 77 *
16 78 * @a: Pointer to "struct tomoyo_condition".
... ... @@ -23,6 +85,7 @@
23 85 {
24 86 return a->size == b->size && a->condc == b->condc &&
25 87 a->numbers_count == b->numbers_count &&
  88 + a->names_count == b->names_count &&
26 89 !memcmp(a + 1, b + 1, a->size - sizeof(*a));
27 90 }
28 91  
... ... @@ -114,6 +177,7 @@
114 177 struct tomoyo_condition *entry = NULL;
115 178 struct tomoyo_condition_element *condp = NULL;
116 179 struct tomoyo_number_union *numbers_p = NULL;
  180 + struct tomoyo_name_union *names_p = NULL;
117 181 struct tomoyo_condition e = { };
118 182 char * const start_of_string = param->data;
119 183 char * const end_of_string = start_of_string + strlen(start_of_string);
... ... @@ -178,6 +242,20 @@
178 242 e.condc++;
179 243 else
180 244 e.condc--;
  245 + if (left == TOMOYO_EXEC_REALPATH ||
  246 + left == TOMOYO_SYMLINK_TARGET) {
  247 + if (!names_p) {
  248 + e.names_count++;
  249 + } else {
  250 + e.names_count--;
  251 + right = TOMOYO_NAME_UNION;
  252 + param->data = right_word;
  253 + if (!tomoyo_parse_name_union_quoted(param,
  254 + names_p++))
  255 + goto out;
  256 + }
  257 + goto store_value;
  258 + }
181 259 right = tomoyo_condition_type(right_word);
182 260 if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
183 261 if (!numbers_p) {
... ... @@ -191,6 +269,7 @@
191 269 goto out;
192 270 }
193 271 }
  272 +store_value:
194 273 if (!condp) {
195 274 dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
196 275 "match=%u\n", __LINE__, left, right, !is_not);
197 276  
198 277  
199 278  
... ... @@ -204,21 +283,23 @@
204 283 condp->equals);
205 284 condp++;
206 285 }
207   - dprintk(KERN_INFO "%u: cond=%u numbers=%u\n",
208   - __LINE__, e.condc, e.numbers_count);
  286 + dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u\n",
  287 + __LINE__, e.condc, e.numbers_count, e.names_count);
209 288 if (entry) {
210   - BUG_ON(e.numbers_count | e.condc);
  289 + BUG_ON(e.names_count | e.numbers_count | e.condc);
211 290 return tomoyo_commit_condition(entry);
212 291 }
213 292 e.size = sizeof(*entry)
214 293 + e.condc * sizeof(struct tomoyo_condition_element)
215   - + e.numbers_count * sizeof(struct tomoyo_number_union);
  294 + + e.numbers_count * sizeof(struct tomoyo_number_union)
  295 + + e.names_count * sizeof(struct tomoyo_name_union);
216 296 entry = kzalloc(e.size, GFP_NOFS);
217 297 if (!entry)
218 298 return NULL;
219 299 *entry = e;
220 300 condp = (struct tomoyo_condition_element *) (entry + 1);
221 301 numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
  302 + names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
222 303 {
223 304 bool flag = false;
224 305 for (pos = start_of_string; pos < end_of_string; pos++) {
... ... @@ -309,6 +390,7 @@
309 390 unsigned long max_v[2] = { 0, 0 };
310 391 const struct tomoyo_condition_element *condp;
311 392 const struct tomoyo_number_union *numbers_p;
  393 + const struct tomoyo_name_union *names_p;
312 394 struct tomoyo_obj_info *obj;
313 395 u16 condc;
314 396 if (!cond)
... ... @@ -317,6 +399,8 @@
317 399 obj = r->obj;
318 400 condp = (struct tomoyo_condition_element *) (cond + 1);
319 401 numbers_p = (const struct tomoyo_number_union *) (condp + condc);
  402 + names_p = (const struct tomoyo_name_union *)
  403 + (numbers_p + cond->numbers_count);
320 404 for (i = 0; i < condc; i++) {
321 405 const bool match = condp->equals;
322 406 const u8 left = condp->left;
... ... @@ -324,6 +408,30 @@
324 408 bool is_bitop[2] = { false, false };
325 409 u8 j;
326 410 condp++;
  411 + /* Check string expressions. */
  412 + if (right == TOMOYO_NAME_UNION) {
  413 + const struct tomoyo_name_union *ptr = names_p++;
  414 + switch (left) {
  415 + struct tomoyo_path_info *symlink;
  416 + struct tomoyo_execve *ee;
  417 + struct file *file;
  418 + case TOMOYO_SYMLINK_TARGET:
  419 + symlink = obj ? obj->symlink_target : NULL;
  420 + if (!symlink ||
  421 + !tomoyo_compare_name_union(symlink, ptr)
  422 + == match)
  423 + goto out;
  424 + break;
  425 + case TOMOYO_EXEC_REALPATH:
  426 + ee = r->ee;
  427 + file = ee ? ee->bprm->file : NULL;
  428 + if (!tomoyo_scan_exec_realpath(file, ptr,
  429 + match))
  430 + goto out;
  431 + break;
  432 + }
  433 + continue;
  434 + }
327 435 /* Check numeric or bit-op expressions. */
328 436 for (j = 0; j < 2; j++) {
329 437 const u8 index = j ? right : left;
security/tomoyo/gc.c
... ... @@ -357,13 +357,18 @@
357 357 head.list);
358 358 const u16 condc = cond->condc;
359 359 const u16 numbers_count = cond->numbers_count;
  360 + const u16 names_count = cond->names_count;
360 361 unsigned int i;
361 362 const struct tomoyo_condition_element *condp
362 363 = (const struct tomoyo_condition_element *) (cond + 1);
363 364 struct tomoyo_number_union *numbers_p
364 365 = (struct tomoyo_number_union *) (condp + condc);
  366 + struct tomoyo_name_union *names_p
  367 + = (struct tomoyo_name_union *) (numbers_p + numbers_count);
365 368 for (i = 0; i < numbers_count; i++)
366 369 tomoyo_put_number_union(numbers_p++);
  370 + for (i = 0; i < names_count; i++)
  371 + tomoyo_put_name_union(names_p++);
367 372 }
368 373  
369 374 /**