aboutsummaryrefslogtreecommitdiffstats
path: root/security/tomoyo/tomoyo.c
diff options
context:
space:
mode:
authorKentaro Takeda <takedakn@nttdata.co.jp>2009-02-05 03:18:16 -0500
committerJames Morris <jmorris@namei.org>2009-02-11 23:15:05 -0500
commitf7433243770c77979c396b4c7449a10e9b3521db (patch)
tree8bcb3d92ddb65b73f1802c5476d75f92814477d8 /security/tomoyo/tomoyo.c
parent26a2a1c9eb88d9aca8891575b3b986812e073872 (diff)
LSM adapter functions.
DAC's permissions and TOMOYO's permissions are not one-to-one mapping. Regarding DAC, there are "read", "write", "execute" permissions. Regarding TOMOYO, there are "allow_read", "allow_write", "allow_read/write", "allow_execute", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", "allow_link", "allow_rename" permissions. +----------------------------------+----------------------------------+ | requested operation | required TOMOYO's permission | +----------------------------------+----------------------------------+ | sys_open(O_RDONLY) | allow_read | +----------------------------------+----------------------------------+ | sys_open(O_WRONLY) | allow_write | +----------------------------------+----------------------------------+ | sys_open(O_RDWR) | allow_read/write | +----------------------------------+----------------------------------+ | open_exec() from do_execve() | allow_execute | +----------------------------------+----------------------------------+ | open_exec() from !do_execve() | allow_read | +----------------------------------+----------------------------------+ | sys_read() | (none) | +----------------------------------+----------------------------------+ | sys_write() | (none) | +----------------------------------+----------------------------------+ | sys_mmap() | (none) | +----------------------------------+----------------------------------+ | sys_uselib() | allow_read | +----------------------------------+----------------------------------+ | sys_open(O_CREAT) | allow_create | +----------------------------------+----------------------------------+ | sys_open(O_TRUNC) | allow_truncate | +----------------------------------+----------------------------------+ | sys_truncate() | allow_truncate | +----------------------------------+----------------------------------+ | sys_ftruncate() | allow_truncate | +----------------------------------+----------------------------------+ | sys_open() without O_APPEND | allow_rewrite | +----------------------------------+----------------------------------+ | setfl() without O_APPEND | allow_rewrite | +----------------------------------+----------------------------------+ | sys_sysctl() for writing | allow_write | +----------------------------------+----------------------------------+ | sys_sysctl() for reading | allow_read | +----------------------------------+----------------------------------+ | sys_unlink() | allow_unlink | +----------------------------------+----------------------------------+ | sys_mknod(S_IFREG) | allow_create | +----------------------------------+----------------------------------+ | sys_mknod(0) | allow_create | +----------------------------------+----------------------------------+ | sys_mknod(S_IFIFO) | allow_mkfifo | +----------------------------------+----------------------------------+ | sys_mknod(S_IFSOCK) | allow_mksock | +----------------------------------+----------------------------------+ | sys_bind(AF_UNIX) | allow_mksock | +----------------------------------+----------------------------------+ | sys_mknod(S_IFBLK) | allow_mkblock | +----------------------------------+----------------------------------+ | sys_mknod(S_IFCHR) | allow_mkchar | +----------------------------------+----------------------------------+ | sys_symlink() | allow_symlink | +----------------------------------+----------------------------------+ | sys_mkdir() | allow_mkdir | +----------------------------------+----------------------------------+ | sys_rmdir() | allow_rmdir | +----------------------------------+----------------------------------+ | sys_link() | allow_link | +----------------------------------+----------------------------------+ | sys_rename() | allow_rename | +----------------------------------+----------------------------------+ TOMOYO requires "allow_execute" permission of a pathname passed to do_execve() but does not require "allow_read" permission of that pathname. Let's consider 3 patterns (statically linked, dynamically linked, shell script). This description is to some degree simplified. $ cat hello.c #include <stdio.h> int main() { printf("Hello\n"); return 0; } $ cat hello.sh #! /bin/sh echo "Hello" $ gcc -static -o hello-static hello.c $ gcc -o hello-dynamic hello.c $ chmod 755 hello.sh Case 1 -- Executing hello-static from bash. (1) The bash process calls fork() and the child process requests do_execve("hello-static"). (2) The kernel checks "allow_execute hello-static" from "bash" domain. (3) The kernel calculates "bash hello-static" as the domain to transit to. (4) The kernel overwrites the child process by "hello-static". (5) The child process transits to "bash hello-static" domain. (6) The "hello-static" starts and finishes. Case 2 -- Executing hello-dynamic from bash. (1) The bash process calls fork() and the child process requests do_execve("hello-dynamic"). (2) The kernel checks "allow_execute hello-dynamic" from "bash" domain. (3) The kernel calculates "bash hello-dynamic" as the domain to transit to. (4) The kernel checks "allow_read ld-linux.so" from "bash hello-dynamic" domain. I think permission to access ld-linux.so should be charged hello-dynamic program, for "hello-dynamic needs ld-linux.so" is not a fault of bash program. (5) The kernel overwrites the child process by "hello-dynamic". (6) The child process transits to "bash hello-dynamic" domain. (7) The "hello-dynamic" starts and finishes. Case 3 -- Executing hello.sh from bash. (1) The bash process calls fork() and the child process requests do_execve("hello.sh"). (2) The kernel checks "allow_execute hello.sh" from "bash" domain. (3) The kernel calculates "bash hello.sh" as the domain to transit to. (4) The kernel checks "allow_read /bin/sh" from "bash hello.sh" domain. I think permission to access /bin/sh should be charged hello.sh program, for "hello.sh needs /bin/sh" is not a fault of bash program. (5) The kernel overwrites the child process by "/bin/sh". (6) The child process transits to "bash hello.sh" domain. (7) The "/bin/sh" requests open("hello.sh"). (8) The kernel checks "allow_read hello.sh" from "bash hello.sh" domain. (9) The "/bin/sh" starts and finishes. Whether a file is interpreted as a program or not depends on an application. The kernel cannot know whether the file is interpreted as a program or not. Thus, TOMOYO treats "hello-static" "hello-dynamic" "ld-linux.so" "hello.sh" "/bin/sh" equally as merely files; no distinction between executable and non-executable. Therefore, TOMOYO doesn't check DAC's execute permission. TOMOYO checks "allow_read" permission instead. Calling do_execve() is a bold gesture that an old program's instance (i.e. current process) is ready to be overwritten by a new program and is ready to transfer control to the new program. To split purview of programs, TOMOYO requires "allow_execute" permission of the new program against the old program's instance and performs domain transition. If do_execve() succeeds, the old program is no longer responsible against the consequence of the new program's behavior. Only the new program is responsible for all consequences. But TOMOYO doesn't require "allow_read" permission of the new program. If TOMOYO requires "allow_read" permission of the new program, TOMOYO will allow an attacker (who hijacked the old program's instance) to open the new program and steal data from the new program. Requiring "allow_read" permission will widen purview of the old program. Not requiring "allow_read" permission of the new program against the old program's instance is my design for reducing purview of the old program. To be able to know whether the current process is in do_execve() or not, I want to add in_execve flag to "task_struct". Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/tomoyo.c')
-rw-r--r--security/tomoyo/tomoyo.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
new file mode 100644
index 000000000000..f3ab20758c0f
--- /dev/null
+++ b/security/tomoyo/tomoyo.c
@@ -0,0 +1,293 @@
1/*
2 * security/tomoyo/tomoyo.c
3 *
4 * LSM hooks for TOMOYO Linux.
5 *
6 * Copyright (C) 2005-2009 NTT DATA CORPORATION
7 *
8 * Version: 2.2.0-pre 2009/02/01
9 *
10 */
11
12#include <linux/security.h>
13#include "common.h"
14#include "tomoyo.h"
15#include "realpath.h"
16
17static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
18 gfp_t gfp)
19{
20 /*
21 * Since "struct tomoyo_domain_info *" is a sharable pointer,
22 * we don't need to duplicate.
23 */
24 new->security = old->security;
25 return 0;
26}
27
28static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
29{
30 /*
31 * Do only if this function is called for the first time of an execve
32 * operation.
33 */
34 if (bprm->cred_prepared)
35 return 0;
36 /*
37 * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
38 * for the first time.
39 */
40 if (!tomoyo_policy_loaded)
41 tomoyo_load_policy(bprm->filename);
42 /*
43 * Tell tomoyo_bprm_check_security() is called for the first time of an
44 * execve operation.
45 */
46 bprm->cred->security = NULL;
47 return 0;
48}
49
50static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
51{
52 struct tomoyo_domain_info *domain = bprm->cred->security;
53
54 /*
55 * Execute permission is checked against pathname passed to do_execve()
56 * using current domain.
57 */
58 if (!domain) {
59 struct tomoyo_domain_info *next_domain = NULL;
60 int retval = tomoyo_find_next_domain(bprm, &next_domain);
61
62 if (!retval)
63 bprm->cred->security = next_domain;
64 return retval;
65 }
66 /*
67 * Read permission is checked against interpreters using next domain.
68 * '1' is the result of open_to_namei_flags(O_RDONLY).
69 */
70 return tomoyo_check_open_permission(domain, &bprm->file->f_path, 1);
71}
72
73#ifdef CONFIG_SYSCTL
74
75static int tomoyo_prepend(char **buffer, int *buflen, const char *str)
76{
77 int namelen = strlen(str);
78
79 if (*buflen < namelen)
80 return -ENOMEM;
81 *buflen -= namelen;
82 *buffer -= namelen;
83 memcpy(*buffer, str, namelen);
84 return 0;
85}
86
87/**
88 * tomoyo_sysctl_path - return the realpath of a ctl_table.
89 * @table: pointer to "struct ctl_table".
90 *
91 * Returns realpath(3) of the @table on success.
92 * Returns NULL on failure.
93 *
94 * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
95 * if this function didn't return NULL.
96 */
97static char *tomoyo_sysctl_path(struct ctl_table *table)
98{
99 int buflen = TOMOYO_MAX_PATHNAME_LEN;
100 char *buf = tomoyo_alloc(buflen);
101 char *end = buf + buflen;
102 int error = -ENOMEM;
103
104 if (!buf)
105 return NULL;
106
107 *--end = '\0';
108 buflen--;
109 while (table) {
110 char buf[32];
111 const char *sp = table->procname;
112
113 if (!sp) {
114 memset(buf, 0, sizeof(buf));
115 snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name);
116 sp = buf;
117 }
118 if (tomoyo_prepend(&end, &buflen, sp) ||
119 tomoyo_prepend(&end, &buflen, "/"))
120 goto out;
121 table = table->parent;
122 }
123 if (tomoyo_prepend(&end, &buflen, "/proc/sys"))
124 goto out;
125 error = tomoyo_encode(buf, end - buf, end);
126 out:
127 if (!error)
128 return buf;
129 tomoyo_free(buf);
130 return NULL;
131}
132
133static int tomoyo_sysctl(struct ctl_table *table, int op)
134{
135 int error;
136 char *name;
137
138 op &= MAY_READ | MAY_WRITE;
139 if (!op)
140 return 0;
141 name = tomoyo_sysctl_path(table);
142 if (!name)
143 return -ENOMEM;
144 error = tomoyo_check_file_perm(tomoyo_domain(), name, op);
145 tomoyo_free(name);
146 return error;
147}
148#endif
149
150static int tomoyo_path_truncate(struct path *path, loff_t length,
151 unsigned int time_attrs)
152{
153 return tomoyo_check_1path_perm(tomoyo_domain(),
154 TOMOYO_TYPE_TRUNCATE_ACL,
155 path);
156}
157
158static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
159{
160 struct path path = { parent->mnt, dentry };
161 return tomoyo_check_1path_perm(tomoyo_domain(),
162 TOMOYO_TYPE_UNLINK_ACL,
163 &path);
164}
165
166static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
167 int mode)
168{
169 struct path path = { parent->mnt, dentry };
170 return tomoyo_check_1path_perm(tomoyo_domain(),
171 TOMOYO_TYPE_MKDIR_ACL,
172 &path);
173}
174
175static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
176{
177 struct path path = { parent->mnt, dentry };
178 return tomoyo_check_1path_perm(tomoyo_domain(),
179 TOMOYO_TYPE_RMDIR_ACL,
180 &path);
181}
182
183static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
184 const char *old_name)
185{
186 struct path path = { parent->mnt, dentry };
187 return tomoyo_check_1path_perm(tomoyo_domain(),
188 TOMOYO_TYPE_SYMLINK_ACL,
189 &path);
190}
191
192static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
193 int mode, unsigned int dev)
194{
195 struct path path = { parent->mnt, dentry };
196 int type = TOMOYO_TYPE_CREATE_ACL;
197
198 switch (mode & S_IFMT) {
199 case S_IFCHR:
200 type = TOMOYO_TYPE_MKCHAR_ACL;
201 break;
202 case S_IFBLK:
203 type = TOMOYO_TYPE_MKBLOCK_ACL;
204 break;
205 case S_IFIFO:
206 type = TOMOYO_TYPE_MKFIFO_ACL;
207 break;
208 case S_IFSOCK:
209 type = TOMOYO_TYPE_MKSOCK_ACL;
210 break;
211 }
212 return tomoyo_check_1path_perm(tomoyo_domain(),
213 type, &path);
214}
215
216static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
217 struct dentry *new_dentry)
218{
219 struct path path1 = { new_dir->mnt, old_dentry };
220 struct path path2 = { new_dir->mnt, new_dentry };
221 return tomoyo_check_2path_perm(tomoyo_domain(),
222 TOMOYO_TYPE_LINK_ACL,
223 &path1, &path2);
224}
225
226static int tomoyo_path_rename(struct path *old_parent,
227 struct dentry *old_dentry,
228 struct path *new_parent,
229 struct dentry *new_dentry)
230{
231 struct path path1 = { old_parent->mnt, old_dentry };
232 struct path path2 = { new_parent->mnt, new_dentry };
233 return tomoyo_check_2path_perm(tomoyo_domain(),
234 TOMOYO_TYPE_RENAME_ACL,
235 &path1, &path2);
236}
237
238static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
239 unsigned long arg)
240{
241 if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
242 return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
243 return 0;
244}
245
246static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
247{
248 int flags = f->f_flags;
249
250 if ((flags + 1) & O_ACCMODE)
251 flags++;
252 flags |= f->f_flags & (O_APPEND | O_TRUNC);
253 /* Don't check read permission here if called from do_execve(). */
254 if (current->in_execve)
255 return 0;
256 return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
257}
258
259static struct security_operations tomoyo_security_ops = {
260 .name = "tomoyo",
261 .cred_prepare = tomoyo_cred_prepare,
262 .bprm_set_creds = tomoyo_bprm_set_creds,
263 .bprm_check_security = tomoyo_bprm_check_security,
264#ifdef CONFIG_SYSCTL
265 .sysctl = tomoyo_sysctl,
266#endif
267 .file_fcntl = tomoyo_file_fcntl,
268 .dentry_open = tomoyo_dentry_open,
269 .path_truncate = tomoyo_path_truncate,
270 .path_unlink = tomoyo_path_unlink,
271 .path_mkdir = tomoyo_path_mkdir,
272 .path_rmdir = tomoyo_path_rmdir,
273 .path_symlink = tomoyo_path_symlink,
274 .path_mknod = tomoyo_path_mknod,
275 .path_link = tomoyo_path_link,
276 .path_rename = tomoyo_path_rename,
277};
278
279static int __init tomoyo_init(void)
280{
281 struct cred *cred = (struct cred *) current_cred();
282
283 if (!security_module_enable(&tomoyo_security_ops))
284 return 0;
285 /* register ourselves with the security framework */
286 if (register_security(&tomoyo_security_ops))
287 panic("Failure registering TOMOYO Linux");
288 printk(KERN_INFO "TOMOYO Linux initialized\n");
289 cred->security = &tomoyo_kernel_domain;
290 return 0;
291}
292
293security_initcall(tomoyo_init);