diff options
Diffstat (limited to 'security')
74 files changed, 13186 insertions, 4571 deletions
diff --git a/security/Kconfig b/security/Kconfig index 226b9556b25..bd72ae62349 100644 --- a/security/Kconfig +++ b/security/Kconfig | |||
@@ -140,6 +140,7 @@ config LSM_MMAP_MIN_ADDR | |||
140 | source security/selinux/Kconfig | 140 | source security/selinux/Kconfig |
141 | source security/smack/Kconfig | 141 | source security/smack/Kconfig |
142 | source security/tomoyo/Kconfig | 142 | source security/tomoyo/Kconfig |
143 | source security/apparmor/Kconfig | ||
143 | 144 | ||
144 | source security/integrity/ima/Kconfig | 145 | source security/integrity/ima/Kconfig |
145 | 146 | ||
@@ -148,6 +149,7 @@ choice | |||
148 | default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX | 149 | default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX |
149 | default DEFAULT_SECURITY_SMACK if SECURITY_SMACK | 150 | default DEFAULT_SECURITY_SMACK if SECURITY_SMACK |
150 | default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO | 151 | default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO |
152 | default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR | ||
151 | default DEFAULT_SECURITY_DAC | 153 | default DEFAULT_SECURITY_DAC |
152 | 154 | ||
153 | help | 155 | help |
@@ -163,6 +165,9 @@ choice | |||
163 | config DEFAULT_SECURITY_TOMOYO | 165 | config DEFAULT_SECURITY_TOMOYO |
164 | bool "TOMOYO" if SECURITY_TOMOYO=y | 166 | bool "TOMOYO" if SECURITY_TOMOYO=y |
165 | 167 | ||
168 | config DEFAULT_SECURITY_APPARMOR | ||
169 | bool "AppArmor" if SECURITY_APPARMOR=y | ||
170 | |||
166 | config DEFAULT_SECURITY_DAC | 171 | config DEFAULT_SECURITY_DAC |
167 | bool "Unix Discretionary Access Controls" | 172 | bool "Unix Discretionary Access Controls" |
168 | 173 | ||
@@ -173,6 +178,7 @@ config DEFAULT_SECURITY | |||
173 | default "selinux" if DEFAULT_SECURITY_SELINUX | 178 | default "selinux" if DEFAULT_SECURITY_SELINUX |
174 | default "smack" if DEFAULT_SECURITY_SMACK | 179 | default "smack" if DEFAULT_SECURITY_SMACK |
175 | default "tomoyo" if DEFAULT_SECURITY_TOMOYO | 180 | default "tomoyo" if DEFAULT_SECURITY_TOMOYO |
181 | default "apparmor" if DEFAULT_SECURITY_APPARMOR | ||
176 | default "" if DEFAULT_SECURITY_DAC | 182 | default "" if DEFAULT_SECURITY_DAC |
177 | 183 | ||
178 | endmenu | 184 | endmenu |
diff --git a/security/Makefile b/security/Makefile index da20a193c8d..8bb0fe9e1ca 100644 --- a/security/Makefile +++ b/security/Makefile | |||
@@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ | |||
6 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux | 6 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux |
7 | subdir-$(CONFIG_SECURITY_SMACK) += smack | 7 | subdir-$(CONFIG_SECURITY_SMACK) += smack |
8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo | 8 | subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo |
9 | subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor | ||
9 | 10 | ||
10 | # always enable default capabilities | 11 | # always enable default capabilities |
11 | obj-y += commoncap.o | 12 | obj-y += commoncap.o |
@@ -19,6 +20,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o | |||
19 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o | 20 | obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o |
20 | obj-$(CONFIG_AUDIT) += lsm_audit.o | 21 | obj-$(CONFIG_AUDIT) += lsm_audit.o |
21 | obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o | 22 | obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o |
23 | obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o | ||
22 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | 24 | obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o |
23 | 25 | ||
24 | # Object integrity file lists | 26 | # Object integrity file lists |
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore new file mode 100644 index 00000000000..0a0a99f3b08 --- /dev/null +++ b/security/apparmor/.gitignore | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Generated include files | ||
3 | # | ||
4 | af_names.h | ||
5 | capability_names.h | ||
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig new file mode 100644 index 00000000000..9b9013b2e32 --- /dev/null +++ b/security/apparmor/Kconfig | |||
@@ -0,0 +1,31 @@ | |||
1 | config SECURITY_APPARMOR | ||
2 | bool "AppArmor support" | ||
3 | depends on SECURITY && NET | ||
4 | select AUDIT | ||
5 | select SECURITY_PATH | ||
6 | select SECURITYFS | ||
7 | select SECURITY_NETWORK | ||
8 | default n | ||
9 | help | ||
10 | This enables the AppArmor security module. | ||
11 | Required userspace tools (if they are not included in your | ||
12 | distribution) and further information may be found at | ||
13 | http://apparmor.wiki.kernel.org | ||
14 | |||
15 | If you are unsure how to answer this question, answer N. | ||
16 | |||
17 | config SECURITY_APPARMOR_BOOTPARAM_VALUE | ||
18 | int "AppArmor boot parameter default value" | ||
19 | depends on SECURITY_APPARMOR | ||
20 | range 0 1 | ||
21 | default 1 | ||
22 | help | ||
23 | This option sets the default value for the kernel parameter | ||
24 | 'apparmor', which allows AppArmor to be enabled or disabled | ||
25 | at boot. If this option is set to 0 (zero), the AppArmor | ||
26 | kernel parameter will default to 0, disabling AppArmor at | ||
27 | boot. If this option is set to 1 (one), the AppArmor | ||
28 | kernel parameter will default to 1, enabling AppArmor at | ||
29 | boot. | ||
30 | |||
31 | If you are unsure how to answer this question, answer 1. | ||
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile new file mode 100644 index 00000000000..f204869399e --- /dev/null +++ b/security/apparmor/Makefile | |||
@@ -0,0 +1,24 @@ | |||
1 | # Makefile for AppArmor Linux Security Module | ||
2 | # | ||
3 | obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | ||
4 | |||
5 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | ||
6 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | ||
7 | resource.o sid.o file.o | ||
8 | |||
9 | clean-files: capability_names.h af_names.h | ||
10 | |||
11 | quiet_cmd_make-caps = GEN $@ | ||
12 | cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ | ||
13 | |||
14 | quiet_cmd_make-rlim = GEN $@ | ||
15 | cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ | ||
16 | |||
17 | $(obj)/capability.o : $(obj)/capability_names.h | ||
18 | $(obj)/resource.o : $(obj)/rlim_names.h | ||
19 | $(obj)/capability_names.h : $(srctree)/include/linux/capability.h | ||
20 | $(call cmd,make-caps) | ||
21 | $(obj)/af_names.h : $(srctree)/include/linux/socket.h | ||
22 | $(call cmd,make-af) | ||
23 | $(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h | ||
24 | $(call cmd,make-rlim) | ||
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c new file mode 100644 index 00000000000..7320331b44a --- /dev/null +++ b/security/apparmor/apparmorfs.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /sys/kernel/security/apparmor interface functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/security.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/seq_file.h> | ||
19 | #include <linux/uaccess.h> | ||
20 | #include <linux/namei.h> | ||
21 | |||
22 | #include "include/apparmor.h" | ||
23 | #include "include/apparmorfs.h" | ||
24 | #include "include/audit.h" | ||
25 | #include "include/context.h" | ||
26 | #include "include/policy.h" | ||
27 | |||
28 | /** | ||
29 | * aa_simple_write_to_buffer - common routine for getting policy from user | ||
30 | * @op: operation doing the user buffer copy | ||
31 | * @userbuf: user buffer to copy data from (NOT NULL) | ||
32 | * @alloc_size: size of user buffer | ||
33 | * @copy_size: size of data to copy from user buffer | ||
34 | * @pos: position write is at in the file (NOT NULL) | ||
35 | * | ||
36 | * Returns: kernel buffer containing copy of user buffer data or an | ||
37 | * ERR_PTR on failure. | ||
38 | */ | ||
39 | static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, | ||
40 | size_t alloc_size, size_t copy_size, | ||
41 | loff_t *pos) | ||
42 | { | ||
43 | char *data; | ||
44 | |||
45 | if (*pos != 0) | ||
46 | /* only writes from pos 0, that is complete writes */ | ||
47 | return ERR_PTR(-ESPIPE); | ||
48 | |||
49 | /* | ||
50 | * Don't allow profile load/replace/remove from profiles that don't | ||
51 | * have CAP_MAC_ADMIN | ||
52 | */ | ||
53 | if (!aa_may_manage_policy(op)) | ||
54 | return ERR_PTR(-EACCES); | ||
55 | |||
56 | /* freed by caller to simple_write_to_buffer */ | ||
57 | data = kvmalloc(alloc_size); | ||
58 | if (data == NULL) | ||
59 | return ERR_PTR(-ENOMEM); | ||
60 | |||
61 | if (copy_from_user(data, userbuf, copy_size)) { | ||
62 | kvfree(data); | ||
63 | return ERR_PTR(-EFAULT); | ||
64 | } | ||
65 | |||
66 | return data; | ||
67 | } | ||
68 | |||
69 | |||
70 | /* .load file hook fn to load policy */ | ||
71 | static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, | ||
72 | loff_t *pos) | ||
73 | { | ||
74 | char *data; | ||
75 | ssize_t error; | ||
76 | |||
77 | data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); | ||
78 | |||
79 | error = PTR_ERR(data); | ||
80 | if (!IS_ERR(data)) { | ||
81 | error = aa_replace_profiles(data, size, PROF_ADD); | ||
82 | kvfree(data); | ||
83 | } | ||
84 | |||
85 | return error; | ||
86 | } | ||
87 | |||
88 | static const struct file_operations aa_fs_profile_load = { | ||
89 | .write = profile_load | ||
90 | }; | ||
91 | |||
92 | /* .replace file hook fn to load and/or replace policy */ | ||
93 | static ssize_t profile_replace(struct file *f, const char __user *buf, | ||
94 | size_t size, loff_t *pos) | ||
95 | { | ||
96 | char *data; | ||
97 | ssize_t error; | ||
98 | |||
99 | data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); | ||
100 | error = PTR_ERR(data); | ||
101 | if (!IS_ERR(data)) { | ||
102 | error = aa_replace_profiles(data, size, PROF_REPLACE); | ||
103 | kvfree(data); | ||
104 | } | ||
105 | |||
106 | return error; | ||
107 | } | ||
108 | |||
109 | static const struct file_operations aa_fs_profile_replace = { | ||
110 | .write = profile_replace | ||
111 | }; | ||
112 | |||
113 | /* .remove file hook fn to remove loaded policy */ | ||
114 | static ssize_t profile_remove(struct file *f, const char __user *buf, | ||
115 | size_t size, loff_t *pos) | ||
116 | { | ||
117 | char *data; | ||
118 | ssize_t error; | ||
119 | |||
120 | /* | ||
121 | * aa_remove_profile needs a null terminated string so 1 extra | ||
122 | * byte is allocated and the copied data is null terminated. | ||
123 | */ | ||
124 | data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); | ||
125 | |||
126 | error = PTR_ERR(data); | ||
127 | if (!IS_ERR(data)) { | ||
128 | data[size] = 0; | ||
129 | error = aa_remove_profiles(data, size); | ||
130 | kvfree(data); | ||
131 | } | ||
132 | |||
133 | return error; | ||
134 | } | ||
135 | |||
136 | static const struct file_operations aa_fs_profile_remove = { | ||
137 | .write = profile_remove | ||
138 | }; | ||
139 | |||
140 | /** Base file system setup **/ | ||
141 | |||
142 | static struct dentry *aa_fs_dentry __initdata; | ||
143 | |||
144 | static void __init aafs_remove(const char *name) | ||
145 | { | ||
146 | struct dentry *dentry; | ||
147 | |||
148 | dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); | ||
149 | if (!IS_ERR(dentry)) { | ||
150 | securityfs_remove(dentry); | ||
151 | dput(dentry); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * aafs_create - create an entry in the apparmor filesystem | ||
157 | * @name: name of the entry (NOT NULL) | ||
158 | * @mask: file permission mask of the file | ||
159 | * @fops: file operations for the file (NOT NULL) | ||
160 | * | ||
161 | * Used aafs_remove to remove entries created with this fn. | ||
162 | */ | ||
163 | static int __init aafs_create(const char *name, int mask, | ||
164 | const struct file_operations *fops) | ||
165 | { | ||
166 | struct dentry *dentry; | ||
167 | |||
168 | dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, | ||
169 | NULL, fops); | ||
170 | |||
171 | return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * aa_destroy_aafs - cleanup and free aafs | ||
176 | * | ||
177 | * releases dentries allocated by aa_create_aafs | ||
178 | */ | ||
179 | void __init aa_destroy_aafs(void) | ||
180 | { | ||
181 | if (aa_fs_dentry) { | ||
182 | aafs_remove(".remove"); | ||
183 | aafs_remove(".replace"); | ||
184 | aafs_remove(".load"); | ||
185 | |||
186 | securityfs_remove(aa_fs_dentry); | ||
187 | aa_fs_dentry = NULL; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * aa_create_aafs - create the apparmor security filesystem | ||
193 | * | ||
194 | * dentries created here are released by aa_destroy_aafs | ||
195 | * | ||
196 | * Returns: error on failure | ||
197 | */ | ||
198 | int __init aa_create_aafs(void) | ||
199 | { | ||
200 | int error; | ||
201 | |||
202 | if (!apparmor_initialized) | ||
203 | return 0; | ||
204 | |||
205 | if (aa_fs_dentry) { | ||
206 | AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); | ||
207 | return -EEXIST; | ||
208 | } | ||
209 | |||
210 | aa_fs_dentry = securityfs_create_dir("apparmor", NULL); | ||
211 | if (IS_ERR(aa_fs_dentry)) { | ||
212 | error = PTR_ERR(aa_fs_dentry); | ||
213 | aa_fs_dentry = NULL; | ||
214 | goto error; | ||
215 | } | ||
216 | |||
217 | error = aafs_create(".load", 0640, &aa_fs_profile_load); | ||
218 | if (error) | ||
219 | goto error; | ||
220 | error = aafs_create(".replace", 0640, &aa_fs_profile_replace); | ||
221 | if (error) | ||
222 | goto error; | ||
223 | error = aafs_create(".remove", 0640, &aa_fs_profile_remove); | ||
224 | if (error) | ||
225 | goto error; | ||
226 | |||
227 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | ||
228 | |||
229 | /* Report that AppArmor fs is enabled */ | ||
230 | aa_info_message("AppArmor Filesystem Enabled"); | ||
231 | return 0; | ||
232 | |||
233 | error: | ||
234 | aa_destroy_aafs(); | ||
235 | AA_ERROR("Error creating AppArmor securityfs\n"); | ||
236 | return error; | ||
237 | } | ||
238 | |||
239 | fs_initcall(aa_create_aafs); | ||
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c new file mode 100644 index 00000000000..96502b22b26 --- /dev/null +++ b/security/apparmor/audit.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor auditing functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/audit.h> | ||
16 | #include <linux/socket.h> | ||
17 | |||
18 | #include "include/apparmor.h" | ||
19 | #include "include/audit.h" | ||
20 | #include "include/policy.h" | ||
21 | |||
22 | const char *op_table[] = { | ||
23 | "null", | ||
24 | |||
25 | "sysctl", | ||
26 | "capable", | ||
27 | |||
28 | "unlink", | ||
29 | "mkdir", | ||
30 | "rmdir", | ||
31 | "mknod", | ||
32 | "truncate", | ||
33 | "link", | ||
34 | "symlink", | ||
35 | "rename_src", | ||
36 | "rename_dest", | ||
37 | "chmod", | ||
38 | "chown", | ||
39 | "getattr", | ||
40 | "open", | ||
41 | |||
42 | "file_perm", | ||
43 | "file_lock", | ||
44 | "file_mmap", | ||
45 | "file_mprotect", | ||
46 | |||
47 | "create", | ||
48 | "post_create", | ||
49 | "bind", | ||
50 | "connect", | ||
51 | "listen", | ||
52 | "accept", | ||
53 | "sendmsg", | ||
54 | "recvmsg", | ||
55 | "getsockname", | ||
56 | "getpeername", | ||
57 | "getsockopt", | ||
58 | "setsockopt", | ||
59 | "socket_shutdown", | ||
60 | |||
61 | "ptrace", | ||
62 | |||
63 | "exec", | ||
64 | "change_hat", | ||
65 | "change_profile", | ||
66 | "change_onexec", | ||
67 | |||
68 | "setprocattr", | ||
69 | "setrlimit", | ||
70 | |||
71 | "profile_replace", | ||
72 | "profile_load", | ||
73 | "profile_remove" | ||
74 | }; | ||
75 | |||
76 | const char *audit_mode_names[] = { | ||
77 | "normal", | ||
78 | "quiet_denied", | ||
79 | "quiet", | ||
80 | "noquiet", | ||
81 | "all" | ||
82 | }; | ||
83 | |||
84 | static char *aa_audit_type[] = { | ||
85 | "AUDIT", | ||
86 | "ALLOWED", | ||
87 | "DENIED", | ||
88 | "HINT", | ||
89 | "STATUS", | ||
90 | "ERROR", | ||
91 | "KILLED" | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * Currently AppArmor auditing is fed straight into the audit framework. | ||
96 | * | ||
97 | * TODO: | ||
98 | * netlink interface for complain mode | ||
99 | * user auditing, - send user auditing to netlink interface | ||
100 | * system control of whether user audit messages go to system log | ||
101 | */ | ||
102 | |||
103 | /** | ||
104 | * audit_base - core AppArmor function. | ||
105 | * @ab: audit buffer to fill (NOT NULL) | ||
106 | * @ca: audit structure containing data to audit (NOT NULL) | ||
107 | * | ||
108 | * Record common AppArmor audit data from @sa | ||
109 | */ | ||
110 | static void audit_pre(struct audit_buffer *ab, void *ca) | ||
111 | { | ||
112 | struct common_audit_data *sa = ca; | ||
113 | struct task_struct *tsk = sa->tsk ? sa->tsk : current; | ||
114 | |||
115 | if (aa_g_audit_header) { | ||
116 | audit_log_format(ab, "apparmor="); | ||
117 | audit_log_string(ab, aa_audit_type[sa->aad.type]); | ||
118 | } | ||
119 | |||
120 | if (sa->aad.op) { | ||
121 | audit_log_format(ab, " operation="); | ||
122 | audit_log_string(ab, op_table[sa->aad.op]); | ||
123 | } | ||
124 | |||
125 | if (sa->aad.info) { | ||
126 | audit_log_format(ab, " info="); | ||
127 | audit_log_string(ab, sa->aad.info); | ||
128 | if (sa->aad.error) | ||
129 | audit_log_format(ab, " error=%d", sa->aad.error); | ||
130 | } | ||
131 | |||
132 | if (sa->aad.profile) { | ||
133 | struct aa_profile *profile = sa->aad.profile; | ||
134 | pid_t pid; | ||
135 | rcu_read_lock(); | ||
136 | pid = tsk->real_parent->pid; | ||
137 | rcu_read_unlock(); | ||
138 | audit_log_format(ab, " parent=%d", pid); | ||
139 | if (profile->ns != root_ns) { | ||
140 | audit_log_format(ab, " namespace="); | ||
141 | audit_log_untrustedstring(ab, profile->ns->base.hname); | ||
142 | } | ||
143 | audit_log_format(ab, " profile="); | ||
144 | audit_log_untrustedstring(ab, profile->base.hname); | ||
145 | } | ||
146 | |||
147 | if (sa->aad.name) { | ||
148 | audit_log_format(ab, " name="); | ||
149 | audit_log_untrustedstring(ab, sa->aad.name); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * aa_audit_msg - Log a message to the audit subsystem | ||
155 | * @sa: audit event structure (NOT NULL) | ||
156 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
157 | */ | ||
158 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
159 | void (*cb) (struct audit_buffer *, void *)) | ||
160 | { | ||
161 | sa->aad.type = type; | ||
162 | sa->lsm_pre_audit = audit_pre; | ||
163 | sa->lsm_post_audit = cb; | ||
164 | common_lsm_audit(sa); | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * aa_audit - Log a profile based audit event to the audit subsystem | ||
169 | * @type: audit type for the message | ||
170 | * @profile: profile to check against (NOT NULL) | ||
171 | * @gfp: allocation flags to use | ||
172 | * @sa: audit event (NOT NULL) | ||
173 | * @cb: optional callback fn for type specific fields (MAYBE NULL) | ||
174 | * | ||
175 | * Handle default message switching based off of audit mode flags | ||
176 | * | ||
177 | * Returns: error on failure | ||
178 | */ | ||
179 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
180 | struct common_audit_data *sa, | ||
181 | void (*cb) (struct audit_buffer *, void *)) | ||
182 | { | ||
183 | BUG_ON(!profile); | ||
184 | |||
185 | if (type == AUDIT_APPARMOR_AUTO) { | ||
186 | if (likely(!sa->aad.error)) { | ||
187 | if (AUDIT_MODE(profile) != AUDIT_ALL) | ||
188 | return 0; | ||
189 | type = AUDIT_APPARMOR_AUDIT; | ||
190 | } else if (COMPLAIN_MODE(profile)) | ||
191 | type = AUDIT_APPARMOR_ALLOWED; | ||
192 | else | ||
193 | type = AUDIT_APPARMOR_DENIED; | ||
194 | } | ||
195 | if (AUDIT_MODE(profile) == AUDIT_QUIET || | ||
196 | (type == AUDIT_APPARMOR_DENIED && | ||
197 | AUDIT_MODE(profile) == AUDIT_QUIET)) | ||
198 | return sa->aad.error; | ||
199 | |||
200 | if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) | ||
201 | type = AUDIT_APPARMOR_KILL; | ||
202 | |||
203 | if (!unconfined(profile)) | ||
204 | sa->aad.profile = profile; | ||
205 | |||
206 | aa_audit_msg(type, sa, cb); | ||
207 | |||
208 | if (sa->aad.type == AUDIT_APPARMOR_KILL) | ||
209 | (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); | ||
210 | |||
211 | if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) | ||
212 | return complain_error(sa->aad.error); | ||
213 | |||
214 | return sa->aad.error; | ||
215 | } | ||
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c new file mode 100644 index 00000000000..9982c48def4 --- /dev/null +++ b/security/apparmor/capability.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor capability mediation functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/capability.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/gfp.h> | ||
18 | |||
19 | #include "include/apparmor.h" | ||
20 | #include "include/capability.h" | ||
21 | #include "include/context.h" | ||
22 | #include "include/policy.h" | ||
23 | #include "include/audit.h" | ||
24 | |||
25 | /* | ||
26 | * Table of capability names: we generate it from capabilities.h. | ||
27 | */ | ||
28 | #include "capability_names.h" | ||
29 | |||
30 | struct audit_cache { | ||
31 | struct aa_profile *profile; | ||
32 | kernel_cap_t caps; | ||
33 | }; | ||
34 | |||
35 | static DEFINE_PER_CPU(struct audit_cache, audit_cache); | ||
36 | |||
37 | /** | ||
38 | * audit_cb - call back for capability components of audit struct | ||
39 | * @ab - audit buffer (NOT NULL) | ||
40 | * @va - audit struct to audit data from (NOT NULL) | ||
41 | */ | ||
42 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
43 | { | ||
44 | struct common_audit_data *sa = va; | ||
45 | audit_log_format(ab, " capname="); | ||
46 | audit_log_untrustedstring(ab, capability_names[sa->u.cap]); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * audit_caps - audit a capability | ||
51 | * @profile: profile confining task (NOT NULL) | ||
52 | * @task: task capability test was performed against (NOT NULL) | ||
53 | * @cap: capability tested | ||
54 | * @error: error code returned by test | ||
55 | * | ||
56 | * Do auditing of capability and handle, audit/complain/kill modes switching | ||
57 | * and duplicate message elimination. | ||
58 | * | ||
59 | * Returns: 0 or sa->error on success, error code on failure | ||
60 | */ | ||
61 | static int audit_caps(struct aa_profile *profile, struct task_struct *task, | ||
62 | int cap, int error) | ||
63 | { | ||
64 | struct audit_cache *ent; | ||
65 | int type = AUDIT_APPARMOR_AUTO; | ||
66 | struct common_audit_data sa; | ||
67 | COMMON_AUDIT_DATA_INIT(&sa, CAP); | ||
68 | sa.tsk = task; | ||
69 | sa.u.cap = cap; | ||
70 | sa.aad.op = OP_CAPABLE; | ||
71 | sa.aad.error = error; | ||
72 | |||
73 | if (likely(!error)) { | ||
74 | /* test if auditing is being forced */ | ||
75 | if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && | ||
76 | !cap_raised(profile->caps.audit, cap))) | ||
77 | return 0; | ||
78 | type = AUDIT_APPARMOR_AUDIT; | ||
79 | } else if (KILL_MODE(profile) || | ||
80 | cap_raised(profile->caps.kill, cap)) { | ||
81 | type = AUDIT_APPARMOR_KILL; | ||
82 | } else if (cap_raised(profile->caps.quiet, cap) && | ||
83 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
84 | AUDIT_MODE(profile) != AUDIT_ALL) { | ||
85 | /* quiet auditing */ | ||
86 | return error; | ||
87 | } | ||
88 | |||
89 | /* Do simple duplicate message elimination */ | ||
90 | ent = &get_cpu_var(audit_cache); | ||
91 | if (profile == ent->profile && cap_raised(ent->caps, cap)) { | ||
92 | put_cpu_var(audit_cache); | ||
93 | if (COMPLAIN_MODE(profile)) | ||
94 | return complain_error(error); | ||
95 | return error; | ||
96 | } else { | ||
97 | aa_put_profile(ent->profile); | ||
98 | ent->profile = aa_get_profile(profile); | ||
99 | cap_raise(ent->caps, cap); | ||
100 | } | ||
101 | put_cpu_var(audit_cache); | ||
102 | |||
103 | return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * profile_capable - test if profile allows use of capability @cap | ||
108 | * @profile: profile being enforced (NOT NULL, NOT unconfined) | ||
109 | * @cap: capability to test if allowed | ||
110 | * | ||
111 | * Returns: 0 if allowed else -EPERM | ||
112 | */ | ||
113 | static int profile_capable(struct aa_profile *profile, int cap) | ||
114 | { | ||
115 | return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * aa_capable - test permission to use capability | ||
120 | * @task: task doing capability test against (NOT NULL) | ||
121 | * @profile: profile confining @task (NOT NULL) | ||
122 | * @cap: capability to be tested | ||
123 | * @audit: whether an audit record should be generated | ||
124 | * | ||
125 | * Look up capability in profile capability set. | ||
126 | * | ||
127 | * Returns: 0 on success, or else an error code. | ||
128 | */ | ||
129 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
130 | int audit) | ||
131 | { | ||
132 | int error = profile_capable(profile, cap); | ||
133 | |||
134 | if (!audit) { | ||
135 | if (COMPLAIN_MODE(profile)) | ||
136 | return complain_error(error); | ||
137 | return error; | ||
138 | } | ||
139 | |||
140 | return audit_caps(profile, task, cap, error); | ||
141 | } | ||
diff --git a/security/apparmor/context.c b/security/apparmor/context.c new file mode 100644 index 00000000000..8a9b5027c81 --- /dev/null +++ b/security/apparmor/context.c | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor functions used to manipulate object security | ||
5 | * contexts. | ||
6 | * | ||
7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
8 | * Copyright 2009-2010 Canonical Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2 of the | ||
13 | * License. | ||
14 | * | ||
15 | * | ||
16 | * AppArmor sets confinement on every task, via the the aa_task_cxt and | ||
17 | * the aa_task_cxt.profile, both of which are required and are not allowed | ||
18 | * to be NULL. The aa_task_cxt is not reference counted and is unique | ||
19 | * to each cred (which is reference count). The profile pointed to by | ||
20 | * the task_cxt is reference counted. | ||
21 | * | ||
22 | * TODO | ||
23 | * If a task uses change_hat it currently does not return to the old | ||
24 | * cred or task context but instead creates a new one. Ideally the task | ||
25 | * should return to the previous cred if it has not been modified. | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include "include/context.h" | ||
30 | #include "include/policy.h" | ||
31 | |||
32 | /** | ||
33 | * aa_alloc_task_context - allocate a new task_cxt | ||
34 | * @flags: gfp flags for allocation | ||
35 | * | ||
36 | * Returns: allocated buffer or NULL on failure | ||
37 | */ | ||
38 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) | ||
39 | { | ||
40 | return kzalloc(sizeof(struct aa_task_cxt), flags); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * aa_free_task_context - free a task_cxt | ||
45 | * @cxt: task_cxt to free (MAYBE NULL) | ||
46 | */ | ||
47 | void aa_free_task_context(struct aa_task_cxt *cxt) | ||
48 | { | ||
49 | if (cxt) { | ||
50 | aa_put_profile(cxt->profile); | ||
51 | aa_put_profile(cxt->previous); | ||
52 | aa_put_profile(cxt->onexec); | ||
53 | |||
54 | kzfree(cxt); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_dup_task_context - duplicate a task context, incrementing reference counts | ||
60 | * @new: a blank task context (NOT NULL) | ||
61 | * @old: the task context to copy (NOT NULL) | ||
62 | */ | ||
63 | void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) | ||
64 | { | ||
65 | *new = *old; | ||
66 | aa_get_profile(new->profile); | ||
67 | aa_get_profile(new->previous); | ||
68 | aa_get_profile(new->onexec); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * aa_replace_current_profile - replace the current tasks profiles | ||
73 | * @profile: new profile (NOT NULL) | ||
74 | * | ||
75 | * Returns: 0 or error on failure | ||
76 | */ | ||
77 | int aa_replace_current_profile(struct aa_profile *profile) | ||
78 | { | ||
79 | struct aa_task_cxt *cxt = current_cred()->security; | ||
80 | struct cred *new; | ||
81 | BUG_ON(!profile); | ||
82 | |||
83 | if (cxt->profile == profile) | ||
84 | return 0; | ||
85 | |||
86 | new = prepare_creds(); | ||
87 | if (!new) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | cxt = new->security; | ||
91 | if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { | ||
92 | /* if switching to unconfined or a different profile namespace | ||
93 | * clear out context state | ||
94 | */ | ||
95 | aa_put_profile(cxt->previous); | ||
96 | aa_put_profile(cxt->onexec); | ||
97 | cxt->previous = NULL; | ||
98 | cxt->onexec = NULL; | ||
99 | cxt->token = 0; | ||
100 | } | ||
101 | /* be careful switching cxt->profile, when racing replacement it | ||
102 | * is possible that cxt->profile->replacedby is the reference keeping | ||
103 | * @profile valid, so make sure to get its reference before dropping | ||
104 | * the reference on cxt->profile */ | ||
105 | aa_get_profile(profile); | ||
106 | aa_put_profile(cxt->profile); | ||
107 | cxt->profile = profile; | ||
108 | |||
109 | commit_creds(new); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * aa_set_current_onexec - set the tasks change_profile to happen onexec | ||
115 | * @profile: system profile to set at exec (MAYBE NULL to clear value) | ||
116 | * | ||
117 | * Returns: 0 or error on failure | ||
118 | */ | ||
119 | int aa_set_current_onexec(struct aa_profile *profile) | ||
120 | { | ||
121 | struct aa_task_cxt *cxt; | ||
122 | struct cred *new = prepare_creds(); | ||
123 | if (!new) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | cxt = new->security; | ||
127 | aa_get_profile(profile); | ||
128 | aa_put_profile(cxt->onexec); | ||
129 | cxt->onexec = profile; | ||
130 | |||
131 | commit_creds(new); | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * aa_set_current_hat - set the current tasks hat | ||
137 | * @profile: profile to set as the current hat (NOT NULL) | ||
138 | * @token: token value that must be specified to change from the hat | ||
139 | * | ||
140 | * Do switch of tasks hat. If the task is currently in a hat | ||
141 | * validate the token to match. | ||
142 | * | ||
143 | * Returns: 0 or error on failure | ||
144 | */ | ||
145 | int aa_set_current_hat(struct aa_profile *profile, u64 token) | ||
146 | { | ||
147 | struct aa_task_cxt *cxt; | ||
148 | struct cred *new = prepare_creds(); | ||
149 | if (!new) | ||
150 | return -ENOMEM; | ||
151 | BUG_ON(!profile); | ||
152 | |||
153 | cxt = new->security; | ||
154 | if (!cxt->previous) { | ||
155 | /* transfer refcount */ | ||
156 | cxt->previous = cxt->profile; | ||
157 | cxt->token = token; | ||
158 | } else if (cxt->token == token) { | ||
159 | aa_put_profile(cxt->profile); | ||
160 | } else { | ||
161 | /* previous_profile && cxt->token != token */ | ||
162 | abort_creds(new); | ||
163 | return -EACCES; | ||
164 | } | ||
165 | cxt->profile = aa_get_profile(aa_newest_version(profile)); | ||
166 | /* clear exec on switching context */ | ||
167 | aa_put_profile(cxt->onexec); | ||
168 | cxt->onexec = NULL; | ||
169 | |||
170 | commit_creds(new); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * aa_restore_previous_profile - exit from hat context restoring the profile | ||
176 | * @token: the token that must be matched to exit hat context | ||
177 | * | ||
178 | * Attempt to return out of a hat to the previous profile. The token | ||
179 | * must match the stored token value. | ||
180 | * | ||
181 | * Returns: 0 or error of failure | ||
182 | */ | ||
183 | int aa_restore_previous_profile(u64 token) | ||
184 | { | ||
185 | struct aa_task_cxt *cxt; | ||
186 | struct cred *new = prepare_creds(); | ||
187 | if (!new) | ||
188 | return -ENOMEM; | ||
189 | |||
190 | cxt = new->security; | ||
191 | if (cxt->token != token) { | ||
192 | abort_creds(new); | ||
193 | return -EACCES; | ||
194 | } | ||
195 | /* ignore restores when there is no saved profile */ | ||
196 | if (!cxt->previous) { | ||
197 | abort_creds(new); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | aa_put_profile(cxt->profile); | ||
202 | cxt->profile = aa_newest_version(cxt->previous); | ||
203 | BUG_ON(!cxt->profile); | ||
204 | if (unlikely(cxt->profile != cxt->previous)) { | ||
205 | aa_get_profile(cxt->profile); | ||
206 | aa_put_profile(cxt->previous); | ||
207 | } | ||
208 | /* clear exec && prev information when restoring to previous context */ | ||
209 | cxt->previous = NULL; | ||
210 | cxt->token = 0; | ||
211 | aa_put_profile(cxt->onexec); | ||
212 | cxt->onexec = NULL; | ||
213 | |||
214 | commit_creds(new); | ||
215 | return 0; | ||
216 | } | ||
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c new file mode 100644 index 00000000000..c825c6e0b63 --- /dev/null +++ b/security/apparmor/domain.c | |||
@@ -0,0 +1,823 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy attachment and domain transitions | ||
5 | * | ||
6 | * Copyright (C) 2002-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/errno.h> | ||
16 | #include <linux/fdtable.h> | ||
17 | #include <linux/file.h> | ||
18 | #include <linux/mount.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/tracehook.h> | ||
21 | #include <linux/personality.h> | ||
22 | |||
23 | #include "include/audit.h" | ||
24 | #include "include/apparmorfs.h" | ||
25 | #include "include/context.h" | ||
26 | #include "include/domain.h" | ||
27 | #include "include/file.h" | ||
28 | #include "include/ipc.h" | ||
29 | #include "include/match.h" | ||
30 | #include "include/path.h" | ||
31 | #include "include/policy.h" | ||
32 | |||
33 | /** | ||
34 | * aa_free_domain_entries - free entries in a domain table | ||
35 | * @domain: the domain table to free (MAYBE NULL) | ||
36 | */ | ||
37 | void aa_free_domain_entries(struct aa_domain *domain) | ||
38 | { | ||
39 | int i; | ||
40 | if (domain) { | ||
41 | if (!domain->table) | ||
42 | return; | ||
43 | |||
44 | for (i = 0; i < domain->size; i++) | ||
45 | kzfree(domain->table[i]); | ||
46 | kzfree(domain->table); | ||
47 | domain->table = NULL; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * may_change_ptraced_domain - check if can change profile on ptraced task | ||
53 | * @task: task we want to change profile of (NOT NULL) | ||
54 | * @to_profile: profile to change to (NOT NULL) | ||
55 | * | ||
56 | * Check if the task is ptraced and if so if the tracing task is allowed | ||
57 | * to trace the new domain | ||
58 | * | ||
59 | * Returns: %0 or error if change not allowed | ||
60 | */ | ||
61 | static int may_change_ptraced_domain(struct task_struct *task, | ||
62 | struct aa_profile *to_profile) | ||
63 | { | ||
64 | struct task_struct *tracer; | ||
65 | const struct cred *cred = NULL; | ||
66 | struct aa_profile *tracerp = NULL; | ||
67 | int error = 0; | ||
68 | |||
69 | rcu_read_lock(); | ||
70 | tracer = tracehook_tracer_task(task); | ||
71 | if (tracer) { | ||
72 | /* released below */ | ||
73 | cred = get_task_cred(tracer); | ||
74 | tracerp = aa_cred_profile(cred); | ||
75 | } | ||
76 | rcu_read_unlock(); | ||
77 | |||
78 | /* not ptraced */ | ||
79 | if (!tracer || unconfined(tracerp)) | ||
80 | goto out; | ||
81 | |||
82 | error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); | ||
83 | |||
84 | out: | ||
85 | if (cred) | ||
86 | put_cred(cred); | ||
87 | |||
88 | return error; | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * change_profile_perms - find permissions for change_profile | ||
93 | * @profile: the current profile (NOT NULL) | ||
94 | * @ns: the namespace being switched to (NOT NULL) | ||
95 | * @name: the name of the profile to change to (NOT NULL) | ||
96 | * @request: requested perms | ||
97 | * @start: state to start matching in | ||
98 | * | ||
99 | * Returns: permission set | ||
100 | */ | ||
101 | static struct file_perms change_profile_perms(struct aa_profile *profile, | ||
102 | struct aa_namespace *ns, | ||
103 | const char *name, u32 request, | ||
104 | unsigned int start) | ||
105 | { | ||
106 | struct file_perms perms; | ||
107 | struct path_cond cond = { }; | ||
108 | unsigned int state; | ||
109 | |||
110 | if (unconfined(profile)) { | ||
111 | perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; | ||
112 | perms.audit = perms.quiet = perms.kill = 0; | ||
113 | return perms; | ||
114 | } else if (!profile->file.dfa) { | ||
115 | return nullperms; | ||
116 | } else if ((ns == profile->ns)) { | ||
117 | /* try matching against rules with out namespace prepended */ | ||
118 | aa_str_perms(profile->file.dfa, start, name, &cond, &perms); | ||
119 | if (COMBINED_PERM_MASK(perms) & request) | ||
120 | return perms; | ||
121 | } | ||
122 | |||
123 | /* try matching with namespace name and then profile */ | ||
124 | state = aa_dfa_match(profile->file.dfa, start, ns->base.name); | ||
125 | state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); | ||
126 | aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
127 | |||
128 | return perms; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * __attach_match_ - find an attachment match | ||
133 | * @name - to match against (NOT NULL) | ||
134 | * @head - profile list to walk (NOT NULL) | ||
135 | * | ||
136 | * Do a linear search on the profiles in the list. There is a matching | ||
137 | * preference where an exact match is preferred over a name which uses | ||
138 | * expressions to match, and matching expressions with the greatest | ||
139 | * xmatch_len are preferred. | ||
140 | * | ||
141 | * Requires: @head not be shared or have appropriate locks held | ||
142 | * | ||
143 | * Returns: profile or NULL if no match found | ||
144 | */ | ||
145 | static struct aa_profile *__attach_match(const char *name, | ||
146 | struct list_head *head) | ||
147 | { | ||
148 | int len = 0; | ||
149 | struct aa_profile *profile, *candidate = NULL; | ||
150 | |||
151 | list_for_each_entry(profile, head, base.list) { | ||
152 | if (profile->flags & PFLAG_NULL) | ||
153 | continue; | ||
154 | if (profile->xmatch && profile->xmatch_len > len) { | ||
155 | unsigned int state = aa_dfa_match(profile->xmatch, | ||
156 | DFA_START, name); | ||
157 | u32 perm = dfa_user_allow(profile->xmatch, state); | ||
158 | /* any accepting state means a valid match. */ | ||
159 | if (perm & MAY_EXEC) { | ||
160 | candidate = profile; | ||
161 | len = profile->xmatch_len; | ||
162 | } | ||
163 | } else if (!strcmp(profile->base.name, name)) | ||
164 | /* exact non-re match, no more searching required */ | ||
165 | return profile; | ||
166 | } | ||
167 | |||
168 | return candidate; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * find_attach - do attachment search for unconfined processes | ||
173 | * @ns: the current namespace (NOT NULL) | ||
174 | * @list: list to search (NOT NULL) | ||
175 | * @name: the executable name to match against (NOT NULL) | ||
176 | * | ||
177 | * Returns: profile or NULL if no match found | ||
178 | */ | ||
179 | static struct aa_profile *find_attach(struct aa_namespace *ns, | ||
180 | struct list_head *list, const char *name) | ||
181 | { | ||
182 | struct aa_profile *profile; | ||
183 | |||
184 | read_lock(&ns->lock); | ||
185 | profile = aa_get_profile(__attach_match(name, list)); | ||
186 | read_unlock(&ns->lock); | ||
187 | |||
188 | return profile; | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * separate_fqname - separate the namespace and profile names | ||
193 | * @fqname: the fqname name to split (NOT NULL) | ||
194 | * @ns_name: the namespace name if it exists (NOT NULL) | ||
195 | * | ||
196 | * This is the xtable equivalent routine of aa_split_fqname. It finds the | ||
197 | * split in an xtable fqname which contains an embedded \0 instead of a : | ||
198 | * if a namespace is specified. This is done so the xtable is constant and | ||
199 | * isn't re-split on every lookup. | ||
200 | * | ||
201 | * Either the profile or namespace name may be optional but if the namespace | ||
202 | * is specified the profile name termination must be present. This results | ||
203 | * in the following possible encodings: | ||
204 | * profile_name\0 | ||
205 | * :ns_name\0profile_name\0 | ||
206 | * :ns_name\0\0 | ||
207 | * | ||
208 | * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table | ||
209 | * | ||
210 | * Returns: profile name if it is specified else NULL | ||
211 | */ | ||
212 | static const char *separate_fqname(const char *fqname, const char **ns_name) | ||
213 | { | ||
214 | const char *name; | ||
215 | |||
216 | if (fqname[0] == ':') { | ||
217 | /* In this case there is guaranteed to be two \0 terminators | ||
218 | * in the string. They are verified at load time by | ||
219 | * by unpack_trans_table | ||
220 | */ | ||
221 | *ns_name = fqname + 1; /* skip : */ | ||
222 | name = *ns_name + strlen(*ns_name) + 1; | ||
223 | if (!*name) | ||
224 | name = NULL; | ||
225 | } else { | ||
226 | *ns_name = NULL; | ||
227 | name = fqname; | ||
228 | } | ||
229 | |||
230 | return name; | ||
231 | } | ||
232 | |||
233 | static const char *next_name(int xtype, const char *name) | ||
234 | { | ||
235 | return NULL; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * x_table_lookup - lookup an x transition name via transition table | ||
240 | * @profile: current profile (NOT NULL) | ||
241 | * @xindex: index into x transition table | ||
242 | * | ||
243 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | ||
244 | */ | ||
245 | static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | ||
246 | { | ||
247 | struct aa_profile *new_profile = NULL; | ||
248 | struct aa_namespace *ns = profile->ns; | ||
249 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
250 | int index = xindex & AA_X_INDEX_MASK; | ||
251 | const char *name; | ||
252 | |||
253 | /* index is guaranteed to be in range, validated at load time */ | ||
254 | for (name = profile->file.trans.table[index]; !new_profile && name; | ||
255 | name = next_name(xtype, name)) { | ||
256 | struct aa_namespace *new_ns; | ||
257 | const char *xname = NULL; | ||
258 | |||
259 | new_ns = NULL; | ||
260 | if (xindex & AA_X_CHILD) { | ||
261 | /* release by caller */ | ||
262 | new_profile = aa_find_child(profile, name); | ||
263 | continue; | ||
264 | } else if (*name == ':') { | ||
265 | /* switching namespace */ | ||
266 | const char *ns_name; | ||
267 | xname = name = separate_fqname(name, &ns_name); | ||
268 | if (!xname) | ||
269 | /* no name so use profile name */ | ||
270 | xname = profile->base.hname; | ||
271 | if (*ns_name == '@') { | ||
272 | /* TODO: variable support */ | ||
273 | ; | ||
274 | } | ||
275 | /* released below */ | ||
276 | new_ns = aa_find_namespace(ns, ns_name); | ||
277 | if (!new_ns) | ||
278 | continue; | ||
279 | } else if (*name == '@') { | ||
280 | /* TODO: variable support */ | ||
281 | continue; | ||
282 | } else { | ||
283 | /* basic namespace lookup */ | ||
284 | xname = name; | ||
285 | } | ||
286 | |||
287 | /* released by caller */ | ||
288 | new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); | ||
289 | aa_put_namespace(new_ns); | ||
290 | } | ||
291 | |||
292 | /* released by caller */ | ||
293 | return new_profile; | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * x_to_profile - get target profile for a given xindex | ||
298 | * @profile: current profile (NOT NULL) | ||
299 | * @name: name to lookup (NOT NULL) | ||
300 | * @xindex: index into x transition table | ||
301 | * | ||
302 | * find profile for a transition index | ||
303 | * | ||
304 | * Returns: refcounted profile or NULL if not found available | ||
305 | */ | ||
306 | static struct aa_profile *x_to_profile(struct aa_profile *profile, | ||
307 | const char *name, u32 xindex) | ||
308 | { | ||
309 | struct aa_profile *new_profile = NULL; | ||
310 | struct aa_namespace *ns = profile->ns; | ||
311 | u32 xtype = xindex & AA_X_TYPE_MASK; | ||
312 | |||
313 | switch (xtype) { | ||
314 | case AA_X_NONE: | ||
315 | /* fail exec unless ix || ux fallback - handled by caller */ | ||
316 | return NULL; | ||
317 | case AA_X_NAME: | ||
318 | if (xindex & AA_X_CHILD) | ||
319 | /* released by caller */ | ||
320 | new_profile = find_attach(ns, &profile->base.profiles, | ||
321 | name); | ||
322 | else | ||
323 | /* released by caller */ | ||
324 | new_profile = find_attach(ns, &ns->base.profiles, | ||
325 | name); | ||
326 | break; | ||
327 | case AA_X_TABLE: | ||
328 | /* released by caller */ | ||
329 | new_profile = x_table_lookup(profile, xindex); | ||
330 | break; | ||
331 | } | ||
332 | |||
333 | /* released by caller */ | ||
334 | return new_profile; | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * apparmor_bprm_set_creds - set the new creds on the bprm struct | ||
339 | * @bprm: binprm for the exec (NOT NULL) | ||
340 | * | ||
341 | * Returns: %0 or error on failure | ||
342 | */ | ||
343 | int apparmor_bprm_set_creds(struct linux_binprm *bprm) | ||
344 | { | ||
345 | struct aa_task_cxt *cxt; | ||
346 | struct aa_profile *profile, *new_profile = NULL; | ||
347 | struct aa_namespace *ns; | ||
348 | char *buffer = NULL; | ||
349 | unsigned int state; | ||
350 | struct file_perms perms = {}; | ||
351 | struct path_cond cond = { | ||
352 | bprm->file->f_path.dentry->d_inode->i_uid, | ||
353 | bprm->file->f_path.dentry->d_inode->i_mode | ||
354 | }; | ||
355 | const char *name = NULL, *target = NULL, *info = NULL; | ||
356 | int error = cap_bprm_set_creds(bprm); | ||
357 | if (error) | ||
358 | return error; | ||
359 | |||
360 | if (bprm->cred_prepared) | ||
361 | return 0; | ||
362 | |||
363 | cxt = bprm->cred->security; | ||
364 | BUG_ON(!cxt); | ||
365 | |||
366 | profile = aa_get_profile(aa_newest_version(cxt->profile)); | ||
367 | /* | ||
368 | * get the namespace from the replacement profile as replacement | ||
369 | * can change the namespace | ||
370 | */ | ||
371 | ns = profile->ns; | ||
372 | state = profile->file.start; | ||
373 | |||
374 | /* buffer freed below, name is pointer into buffer */ | ||
375 | error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, | ||
376 | &name); | ||
377 | if (error) { | ||
378 | if (profile->flags & | ||
379 | (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) | ||
380 | error = 0; | ||
381 | info = "Exec failed name resolution"; | ||
382 | name = bprm->filename; | ||
383 | goto audit; | ||
384 | } | ||
385 | |||
386 | /* Test for onexec first as onexec directives override other | ||
387 | * x transitions. | ||
388 | */ | ||
389 | if (unconfined(profile)) { | ||
390 | /* unconfined task */ | ||
391 | if (cxt->onexec) | ||
392 | /* change_profile on exec already been granted */ | ||
393 | new_profile = aa_get_profile(cxt->onexec); | ||
394 | else | ||
395 | new_profile = find_attach(ns, &ns->base.profiles, name); | ||
396 | if (!new_profile) | ||
397 | goto cleanup; | ||
398 | goto apply; | ||
399 | } | ||
400 | |||
401 | /* find exec permissions for name */ | ||
402 | state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); | ||
403 | if (cxt->onexec) { | ||
404 | struct file_perms cp; | ||
405 | info = "change_profile onexec"; | ||
406 | if (!(perms.allow & AA_MAY_ONEXEC)) | ||
407 | goto audit; | ||
408 | |||
409 | /* test if this exec can be paired with change_profile onexec. | ||
410 | * onexec permission is linked to exec with a standard pairing | ||
411 | * exec\0change_profile | ||
412 | */ | ||
413 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
414 | cp = change_profile_perms(profile, cxt->onexec->ns, name, | ||
415 | AA_MAY_ONEXEC, state); | ||
416 | |||
417 | if (!(cp.allow & AA_MAY_ONEXEC)) | ||
418 | goto audit; | ||
419 | new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); | ||
420 | goto apply; | ||
421 | } | ||
422 | |||
423 | if (perms.allow & MAY_EXEC) { | ||
424 | /* exec permission determine how to transition */ | ||
425 | new_profile = x_to_profile(profile, name, perms.xindex); | ||
426 | if (!new_profile) { | ||
427 | if (perms.xindex & AA_X_INHERIT) { | ||
428 | /* (p|c|n)ix - don't change profile but do | ||
429 | * use the newest version, which was picked | ||
430 | * up above when getting profile | ||
431 | */ | ||
432 | info = "ix fallback"; | ||
433 | new_profile = aa_get_profile(profile); | ||
434 | goto x_clear; | ||
435 | } else if (perms.xindex & AA_X_UNCONFINED) { | ||
436 | new_profile = aa_get_profile(ns->unconfined); | ||
437 | info = "ux fallback"; | ||
438 | } else { | ||
439 | error = -ENOENT; | ||
440 | info = "profile not found"; | ||
441 | } | ||
442 | } | ||
443 | } else if (COMPLAIN_MODE(profile)) { | ||
444 | /* no exec permission - are we in learning mode */ | ||
445 | new_profile = aa_new_null_profile(profile, 0); | ||
446 | if (!new_profile) { | ||
447 | error = -ENOMEM; | ||
448 | info = "could not create null profile"; | ||
449 | } else { | ||
450 | error = -EACCES; | ||
451 | target = new_profile->base.hname; | ||
452 | } | ||
453 | perms.xindex |= AA_X_UNSAFE; | ||
454 | } else | ||
455 | /* fail exec */ | ||
456 | error = -EACCES; | ||
457 | |||
458 | if (!new_profile) | ||
459 | goto audit; | ||
460 | |||
461 | if (bprm->unsafe & LSM_UNSAFE_SHARE) { | ||
462 | /* FIXME: currently don't mediate shared state */ | ||
463 | ; | ||
464 | } | ||
465 | |||
466 | if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | ||
467 | error = may_change_ptraced_domain(current, new_profile); | ||
468 | if (error) { | ||
469 | aa_put_profile(new_profile); | ||
470 | goto audit; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | /* Determine if secure exec is needed. | ||
475 | * Can be at this point for the following reasons: | ||
476 | * 1. unconfined switching to confined | ||
477 | * 2. confined switching to different confinement | ||
478 | * 3. confined switching to unconfined | ||
479 | * | ||
480 | * Cases 2 and 3 are marked as requiring secure exec | ||
481 | * (unless policy specified "unsafe exec") | ||
482 | * | ||
483 | * bprm->unsafe is used to cache the AA_X_UNSAFE permission | ||
484 | * to avoid having to recompute in secureexec | ||
485 | */ | ||
486 | if (!(perms.xindex & AA_X_UNSAFE)) { | ||
487 | AA_DEBUG("scrubbing environment variables for %s profile=%s\n", | ||
488 | name, new_profile->base.hname); | ||
489 | bprm->unsafe |= AA_SECURE_X_NEEDED; | ||
490 | } | ||
491 | apply: | ||
492 | target = new_profile->base.hname; | ||
493 | /* when transitioning profiles clear unsafe personality bits */ | ||
494 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
495 | |||
496 | x_clear: | ||
497 | aa_put_profile(cxt->profile); | ||
498 | /* transfer new profile reference will be released when cxt is freed */ | ||
499 | cxt->profile = new_profile; | ||
500 | |||
501 | /* clear out all temporary/transitional state from the context */ | ||
502 | aa_put_profile(cxt->previous); | ||
503 | aa_put_profile(cxt->onexec); | ||
504 | cxt->previous = NULL; | ||
505 | cxt->onexec = NULL; | ||
506 | cxt->token = 0; | ||
507 | |||
508 | audit: | ||
509 | error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, | ||
510 | name, target, cond.uid, info, error); | ||
511 | |||
512 | cleanup: | ||
513 | aa_put_profile(profile); | ||
514 | kfree(buffer); | ||
515 | |||
516 | return error; | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * apparmor_bprm_secureexec - determine if secureexec is needed | ||
521 | * @bprm: binprm for exec (NOT NULL) | ||
522 | * | ||
523 | * Returns: %1 if secureexec is needed else %0 | ||
524 | */ | ||
525 | int apparmor_bprm_secureexec(struct linux_binprm *bprm) | ||
526 | { | ||
527 | int ret = cap_bprm_secureexec(bprm); | ||
528 | |||
529 | /* the decision to use secure exec is computed in set_creds | ||
530 | * and stored in bprm->unsafe. | ||
531 | */ | ||
532 | if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) | ||
533 | ret = 1; | ||
534 | |||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | /** | ||
539 | * apparmor_bprm_committing_creds - do task cleanup on committing new creds | ||
540 | * @bprm: binprm for the exec (NOT NULL) | ||
541 | */ | ||
542 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm) | ||
543 | { | ||
544 | struct aa_profile *profile = __aa_current_profile(); | ||
545 | struct aa_task_cxt *new_cxt = bprm->cred->security; | ||
546 | |||
547 | /* bail out if unconfined or not changing profile */ | ||
548 | if ((new_cxt->profile == profile) || | ||
549 | (unconfined(new_cxt->profile))) | ||
550 | return; | ||
551 | |||
552 | current->pdeath_signal = 0; | ||
553 | |||
554 | /* reset soft limits and set hard limits for the new profile */ | ||
555 | __aa_transition_rlimits(profile, new_cxt->profile); | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * apparmor_bprm_commited_cred - do cleanup after new creds committed | ||
560 | * @bprm: binprm for the exec (NOT NULL) | ||
561 | */ | ||
562 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm) | ||
563 | { | ||
564 | /* TODO: cleanup signals - ipc mediation */ | ||
565 | return; | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Functions for self directed profile change | ||
570 | */ | ||
571 | |||
572 | /** | ||
573 | * new_compound_name - create an hname with @n2 appended to @n1 | ||
574 | * @n1: base of hname (NOT NULL) | ||
575 | * @n2: name to append (NOT NULL) | ||
576 | * | ||
577 | * Returns: new name or NULL on error | ||
578 | */ | ||
579 | static char *new_compound_name(const char *n1, const char *n2) | ||
580 | { | ||
581 | char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); | ||
582 | if (name) | ||
583 | sprintf(name, "%s//%s", n1, n2); | ||
584 | return name; | ||
585 | } | ||
586 | |||
587 | /** | ||
588 | * aa_change_hat - change hat to/from subprofile | ||
589 | * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) | ||
590 | * @count: number of hat names in @hats | ||
591 | * @token: magic value to validate the hat change | ||
592 | * @permtest: true if this is just a permission test | ||
593 | * | ||
594 | * Change to the first profile specified in @hats that exists, and store | ||
595 | * the @hat_magic in the current task context. If the count == 0 and the | ||
596 | * @token matches that stored in the current task context, return to the | ||
597 | * top level profile. | ||
598 | * | ||
599 | * Returns %0 on success, error otherwise. | ||
600 | */ | ||
601 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) | ||
602 | { | ||
603 | const struct cred *cred; | ||
604 | struct aa_task_cxt *cxt; | ||
605 | struct aa_profile *profile, *previous_profile, *hat = NULL; | ||
606 | char *name = NULL; | ||
607 | int i; | ||
608 | struct file_perms perms = {}; | ||
609 | const char *target = NULL, *info = NULL; | ||
610 | int error = 0; | ||
611 | |||
612 | /* released below */ | ||
613 | cred = get_current_cred(); | ||
614 | cxt = cred->security; | ||
615 | profile = aa_cred_profile(cred); | ||
616 | previous_profile = cxt->previous; | ||
617 | |||
618 | if (unconfined(profile)) { | ||
619 | info = "unconfined"; | ||
620 | error = -EPERM; | ||
621 | goto audit; | ||
622 | } | ||
623 | |||
624 | if (count) { | ||
625 | /* attempting to change into a new hat or switch to a sibling */ | ||
626 | struct aa_profile *root; | ||
627 | root = PROFILE_IS_HAT(profile) ? profile->parent : profile; | ||
628 | |||
629 | /* find first matching hat */ | ||
630 | for (i = 0; i < count && !hat; i++) | ||
631 | /* released below */ | ||
632 | hat = aa_find_child(root, hats[i]); | ||
633 | if (!hat) { | ||
634 | if (!COMPLAIN_MODE(root) || permtest) { | ||
635 | if (list_empty(&root->base.profiles)) | ||
636 | error = -ECHILD; | ||
637 | else | ||
638 | error = -ENOENT; | ||
639 | goto out; | ||
640 | } | ||
641 | |||
642 | /* | ||
643 | * In complain mode and failed to match any hats. | ||
644 | * Audit the failure is based off of the first hat | ||
645 | * supplied. This is done due how userspace | ||
646 | * interacts with change_hat. | ||
647 | * | ||
648 | * TODO: Add logging of all failed hats | ||
649 | */ | ||
650 | |||
651 | /* freed below */ | ||
652 | name = new_compound_name(root->base.hname, hats[0]); | ||
653 | target = name; | ||
654 | /* released below */ | ||
655 | hat = aa_new_null_profile(profile, 1); | ||
656 | if (!hat) { | ||
657 | info = "failed null profile create"; | ||
658 | error = -ENOMEM; | ||
659 | goto audit; | ||
660 | } | ||
661 | } else { | ||
662 | target = hat->base.hname; | ||
663 | if (!PROFILE_IS_HAT(hat)) { | ||
664 | info = "target not hat"; | ||
665 | error = -EPERM; | ||
666 | goto audit; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | error = may_change_ptraced_domain(current, hat); | ||
671 | if (error) { | ||
672 | info = "ptraced"; | ||
673 | error = -EPERM; | ||
674 | goto audit; | ||
675 | } | ||
676 | |||
677 | if (!permtest) { | ||
678 | error = aa_set_current_hat(hat, token); | ||
679 | if (error == -EACCES) | ||
680 | /* kill task in case of brute force attacks */ | ||
681 | perms.kill = AA_MAY_CHANGEHAT; | ||
682 | else if (name && !error) | ||
683 | /* reset error for learning of new hats */ | ||
684 | error = -ENOENT; | ||
685 | } | ||
686 | } else if (previous_profile) { | ||
687 | /* Return to saved profile. Kill task if restore fails | ||
688 | * to avoid brute force attacks | ||
689 | */ | ||
690 | target = previous_profile->base.hname; | ||
691 | error = aa_restore_previous_profile(token); | ||
692 | perms.kill = AA_MAY_CHANGEHAT; | ||
693 | } else | ||
694 | /* ignore restores when there is no saved profile */ | ||
695 | goto out; | ||
696 | |||
697 | audit: | ||
698 | if (!permtest) | ||
699 | error = aa_audit_file(profile, &perms, GFP_KERNEL, | ||
700 | OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, | ||
701 | target, 0, info, error); | ||
702 | |||
703 | out: | ||
704 | aa_put_profile(hat); | ||
705 | kfree(name); | ||
706 | put_cred(cred); | ||
707 | |||
708 | return error; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * aa_change_profile - perform a one-way profile transition | ||
713 | * @ns_name: name of the profile namespace to change to (MAYBE NULL) | ||
714 | * @hname: name of profile to change to (MAYBE NULL) | ||
715 | * @onexec: whether this transition is to take place immediately or at exec | ||
716 | * @permtest: true if this is just a permission test | ||
717 | * | ||
718 | * Change to new profile @name. Unlike with hats, there is no way | ||
719 | * to change back. If @name isn't specified the current profile name is | ||
720 | * used. | ||
721 | * If @onexec then the transition is delayed until | ||
722 | * the next exec. | ||
723 | * | ||
724 | * Returns %0 on success, error otherwise. | ||
725 | */ | ||
726 | int aa_change_profile(const char *ns_name, const char *hname, bool onexec, | ||
727 | bool permtest) | ||
728 | { | ||
729 | const struct cred *cred; | ||
730 | struct aa_task_cxt *cxt; | ||
731 | struct aa_profile *profile, *target = NULL; | ||
732 | struct aa_namespace *ns = NULL; | ||
733 | struct file_perms perms = {}; | ||
734 | const char *name = NULL, *info = NULL; | ||
735 | int op, error = 0; | ||
736 | u32 request; | ||
737 | |||
738 | if (!hname && !ns_name) | ||
739 | return -EINVAL; | ||
740 | |||
741 | if (onexec) { | ||
742 | request = AA_MAY_ONEXEC; | ||
743 | op = OP_CHANGE_ONEXEC; | ||
744 | } else { | ||
745 | request = AA_MAY_CHANGE_PROFILE; | ||
746 | op = OP_CHANGE_PROFILE; | ||
747 | } | ||
748 | |||
749 | cred = get_current_cred(); | ||
750 | cxt = cred->security; | ||
751 | profile = aa_cred_profile(cred); | ||
752 | |||
753 | if (ns_name) { | ||
754 | /* released below */ | ||
755 | ns = aa_find_namespace(profile->ns, ns_name); | ||
756 | if (!ns) { | ||
757 | /* we don't create new namespace in complain mode */ | ||
758 | name = ns_name; | ||
759 | info = "namespace not found"; | ||
760 | error = -ENOENT; | ||
761 | goto audit; | ||
762 | } | ||
763 | } else | ||
764 | /* released below */ | ||
765 | ns = aa_get_namespace(profile->ns); | ||
766 | |||
767 | /* if the name was not specified, use the name of the current profile */ | ||
768 | if (!hname) { | ||
769 | if (unconfined(profile)) | ||
770 | hname = ns->unconfined->base.hname; | ||
771 | else | ||
772 | hname = profile->base.hname; | ||
773 | } | ||
774 | |||
775 | perms = change_profile_perms(profile, ns, hname, request, | ||
776 | profile->file.start); | ||
777 | if (!(perms.allow & request)) { | ||
778 | error = -EACCES; | ||
779 | goto audit; | ||
780 | } | ||
781 | |||
782 | /* released below */ | ||
783 | target = aa_lookup_profile(ns, hname); | ||
784 | if (!target) { | ||
785 | info = "profile not found"; | ||
786 | error = -ENOENT; | ||
787 | if (permtest || !COMPLAIN_MODE(profile)) | ||
788 | goto audit; | ||
789 | /* released below */ | ||
790 | target = aa_new_null_profile(profile, 0); | ||
791 | if (!target) { | ||
792 | info = "failed null profile create"; | ||
793 | error = -ENOMEM; | ||
794 | goto audit; | ||
795 | } | ||
796 | } | ||
797 | |||
798 | /* check if tracing task is allowed to trace target domain */ | ||
799 | error = may_change_ptraced_domain(current, target); | ||
800 | if (error) { | ||
801 | info = "ptrace prevents transition"; | ||
802 | goto audit; | ||
803 | } | ||
804 | |||
805 | if (permtest) | ||
806 | goto audit; | ||
807 | |||
808 | if (onexec) | ||
809 | error = aa_set_current_onexec(target); | ||
810 | else | ||
811 | error = aa_replace_current_profile(target); | ||
812 | |||
813 | audit: | ||
814 | if (!permtest) | ||
815 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, | ||
816 | name, hname, 0, info, error); | ||
817 | |||
818 | aa_put_namespace(ns); | ||
819 | aa_put_profile(target); | ||
820 | put_cred(cred); | ||
821 | |||
822 | return error; | ||
823 | } | ||
diff --git a/security/apparmor/file.c b/security/apparmor/file.c new file mode 100644 index 00000000000..7312db74121 --- /dev/null +++ b/security/apparmor/file.c | |||
@@ -0,0 +1,457 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor mediation of files | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include "include/apparmor.h" | ||
16 | #include "include/audit.h" | ||
17 | #include "include/file.h" | ||
18 | #include "include/match.h" | ||
19 | #include "include/path.h" | ||
20 | #include "include/policy.h" | ||
21 | |||
22 | struct file_perms nullperms; | ||
23 | |||
24 | |||
25 | /** | ||
26 | * audit_file_mask - convert mask to permission string | ||
27 | * @buffer: buffer to write string to (NOT NULL) | ||
28 | * @mask: permission mask to convert | ||
29 | */ | ||
30 | static void audit_file_mask(struct audit_buffer *ab, u32 mask) | ||
31 | { | ||
32 | char str[10]; | ||
33 | |||
34 | char *m = str; | ||
35 | |||
36 | if (mask & AA_EXEC_MMAP) | ||
37 | *m++ = 'm'; | ||
38 | if (mask & (MAY_READ | AA_MAY_META_READ)) | ||
39 | *m++ = 'r'; | ||
40 | if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD | | ||
41 | AA_MAY_CHOWN)) | ||
42 | *m++ = 'w'; | ||
43 | else if (mask & MAY_APPEND) | ||
44 | *m++ = 'a'; | ||
45 | if (mask & AA_MAY_CREATE) | ||
46 | *m++ = 'c'; | ||
47 | if (mask & AA_MAY_DELETE) | ||
48 | *m++ = 'd'; | ||
49 | if (mask & AA_MAY_LINK) | ||
50 | *m++ = 'l'; | ||
51 | if (mask & AA_MAY_LOCK) | ||
52 | *m++ = 'k'; | ||
53 | if (mask & MAY_EXEC) | ||
54 | *m++ = 'x'; | ||
55 | *m = '\0'; | ||
56 | |||
57 | audit_log_string(ab, str); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * file_audit_cb - call back for file specific audit fields | ||
62 | * @ab: audit_buffer (NOT NULL) | ||
63 | * @va: audit struct to audit values of (NOT NULL) | ||
64 | */ | ||
65 | static void file_audit_cb(struct audit_buffer *ab, void *va) | ||
66 | { | ||
67 | struct common_audit_data *sa = va; | ||
68 | uid_t fsuid = current_fsuid(); | ||
69 | |||
70 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
71 | audit_log_format(ab, " requested_mask="); | ||
72 | audit_file_mask(ab, sa->aad.fs.request); | ||
73 | } | ||
74 | if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { | ||
75 | audit_log_format(ab, " denied_mask="); | ||
76 | audit_file_mask(ab, sa->aad.fs.denied); | ||
77 | } | ||
78 | if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { | ||
79 | audit_log_format(ab, " fsuid=%d", fsuid); | ||
80 | audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); | ||
81 | } | ||
82 | |||
83 | if (sa->aad.fs.target) { | ||
84 | audit_log_format(ab, " target="); | ||
85 | audit_log_untrustedstring(ab, sa->aad.fs.target); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * aa_audit_file - handle the auditing of file operations | ||
91 | * @profile: the profile being enforced (NOT NULL) | ||
92 | * @perms: the permissions computed for the request (NOT NULL) | ||
93 | * @gfp: allocation flags | ||
94 | * @op: operation being mediated | ||
95 | * @request: permissions requested | ||
96 | * @name: name of object being mediated (MAYBE NULL) | ||
97 | * @target: name of target (MAYBE NULL) | ||
98 | * @ouid: object uid | ||
99 | * @info: extra information message (MAYBE NULL) | ||
100 | * @error: 0 if operation allowed else failure error code | ||
101 | * | ||
102 | * Returns: %0 or error on failure | ||
103 | */ | ||
104 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
105 | gfp_t gfp, int op, u32 request, const char *name, | ||
106 | const char *target, uid_t ouid, const char *info, int error) | ||
107 | { | ||
108 | int type = AUDIT_APPARMOR_AUTO; | ||
109 | struct common_audit_data sa; | ||
110 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
111 | sa.aad.op = op, | ||
112 | sa.aad.fs.request = request; | ||
113 | sa.aad.name = name; | ||
114 | sa.aad.fs.target = target; | ||
115 | sa.aad.fs.ouid = ouid; | ||
116 | sa.aad.info = info; | ||
117 | sa.aad.error = error; | ||
118 | |||
119 | if (likely(!sa.aad.error)) { | ||
120 | u32 mask = perms->audit; | ||
121 | |||
122 | if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | ||
123 | mask = 0xffff; | ||
124 | |||
125 | /* mask off perms that are not being force audited */ | ||
126 | sa.aad.fs.request &= mask; | ||
127 | |||
128 | if (likely(!sa.aad.fs.request)) | ||
129 | return 0; | ||
130 | type = AUDIT_APPARMOR_AUDIT; | ||
131 | } else { | ||
132 | /* only report permissions that were denied */ | ||
133 | sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; | ||
134 | |||
135 | if (sa.aad.fs.request & perms->kill) | ||
136 | type = AUDIT_APPARMOR_KILL; | ||
137 | |||
138 | /* quiet known rejects, assumes quiet and kill do not overlap */ | ||
139 | if ((sa.aad.fs.request & perms->quiet) && | ||
140 | AUDIT_MODE(profile) != AUDIT_NOQUIET && | ||
141 | AUDIT_MODE(profile) != AUDIT_ALL) | ||
142 | sa.aad.fs.request &= ~perms->quiet; | ||
143 | |||
144 | if (!sa.aad.fs.request) | ||
145 | return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; | ||
146 | } | ||
147 | |||
148 | sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; | ||
149 | return aa_audit(type, profile, gfp, &sa, file_audit_cb); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * map_old_perms - map old file perms layout to the new layout | ||
154 | * @old: permission set in old mapping | ||
155 | * | ||
156 | * Returns: new permission mapping | ||
157 | */ | ||
158 | static u32 map_old_perms(u32 old) | ||
159 | { | ||
160 | u32 new = old & 0xf; | ||
161 | if (old & MAY_READ) | ||
162 | new |= AA_MAY_META_READ; | ||
163 | if (old & MAY_WRITE) | ||
164 | new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE | | ||
165 | AA_MAY_CHMOD | AA_MAY_CHOWN; | ||
166 | if (old & 0x10) | ||
167 | new |= AA_MAY_LINK; | ||
168 | /* the old mapping lock and link_subset flags where overlaid | ||
169 | * and use was determined by part of a pair that they were in | ||
170 | */ | ||
171 | if (old & 0x20) | ||
172 | new |= AA_MAY_LOCK | AA_LINK_SUBSET; | ||
173 | if (old & 0x40) /* AA_EXEC_MMAP */ | ||
174 | new |= AA_EXEC_MMAP; | ||
175 | |||
176 | new |= AA_MAY_META_READ; | ||
177 | |||
178 | return new; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * compute_perms - convert dfa compressed perms to internal perms | ||
183 | * @dfa: dfa to compute perms for (NOT NULL) | ||
184 | * @state: state in dfa | ||
185 | * @cond: conditions to consider (NOT NULL) | ||
186 | * | ||
187 | * TODO: convert from dfa + state to permission entry, do computation conversion | ||
188 | * at load time. | ||
189 | * | ||
190 | * Returns: computed permission set | ||
191 | */ | ||
192 | static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, | ||
193 | struct path_cond *cond) | ||
194 | { | ||
195 | struct file_perms perms; | ||
196 | |||
197 | /* FIXME: change over to new dfa format | ||
198 | * currently file perms are encoded in the dfa, new format | ||
199 | * splits the permissions from the dfa. This mapping can be | ||
200 | * done at profile load | ||
201 | */ | ||
202 | perms.kill = 0; | ||
203 | |||
204 | if (current_fsuid() == cond->uid) { | ||
205 | perms.allow = map_old_perms(dfa_user_allow(dfa, state)); | ||
206 | perms.audit = map_old_perms(dfa_user_audit(dfa, state)); | ||
207 | perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); | ||
208 | perms.xindex = dfa_user_xindex(dfa, state); | ||
209 | } else { | ||
210 | perms.allow = map_old_perms(dfa_other_allow(dfa, state)); | ||
211 | perms.audit = map_old_perms(dfa_other_audit(dfa, state)); | ||
212 | perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); | ||
213 | perms.xindex = dfa_other_xindex(dfa, state); | ||
214 | } | ||
215 | |||
216 | /* change_profile wasn't determined by ownership in old mapping */ | ||
217 | if (ACCEPT_TABLE(dfa)[state] & 0x80000000) | ||
218 | perms.allow |= AA_MAY_CHANGE_PROFILE; | ||
219 | |||
220 | return perms; | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * aa_str_perms - find permission that match @name | ||
225 | * @dfa: to match against (MAYBE NULL) | ||
226 | * @state: state to start matching in | ||
227 | * @name: string to match against dfa (NOT NULL) | ||
228 | * @cond: conditions to consider for permission set computation (NOT NULL) | ||
229 | * @perms: Returns - the permissions found when matching @name | ||
230 | * | ||
231 | * Returns: the final state in @dfa when beginning @start and walking @name | ||
232 | */ | ||
233 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
234 | const char *name, struct path_cond *cond, | ||
235 | struct file_perms *perms) | ||
236 | { | ||
237 | unsigned int state; | ||
238 | if (!dfa) { | ||
239 | *perms = nullperms; | ||
240 | return DFA_NOMATCH; | ||
241 | } | ||
242 | |||
243 | state = aa_dfa_match(dfa, start, name); | ||
244 | *perms = compute_perms(dfa, state, cond); | ||
245 | |||
246 | return state; | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * is_deleted - test if a file has been completely unlinked | ||
251 | * @dentry: dentry of file to test for deletion (NOT NULL) | ||
252 | * | ||
253 | * Returns: %1 if deleted else %0 | ||
254 | */ | ||
255 | static inline bool is_deleted(struct dentry *dentry) | ||
256 | { | ||
257 | if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0) | ||
258 | return 1; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * aa_path_perm - do permissions check & audit for @path | ||
264 | * @op: operation being checked | ||
265 | * @profile: profile being enforced (NOT NULL) | ||
266 | * @path: path to check permissions of (NOT NULL) | ||
267 | * @flags: any additional path flags beyond what the profile specifies | ||
268 | * @request: requested permissions | ||
269 | * @cond: conditional info for this request (NOT NULL) | ||
270 | * | ||
271 | * Returns: %0 else error if access denied or other error | ||
272 | */ | ||
273 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
274 | int flags, u32 request, struct path_cond *cond) | ||
275 | { | ||
276 | char *buffer = NULL; | ||
277 | struct file_perms perms = {}; | ||
278 | const char *name, *info = NULL; | ||
279 | int error; | ||
280 | |||
281 | flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); | ||
282 | error = aa_get_name(path, flags, &buffer, &name); | ||
283 | if (error) { | ||
284 | if (error == -ENOENT && is_deleted(path->dentry)) { | ||
285 | /* Access to open files that are deleted are | ||
286 | * give a pass (implicit delegation) | ||
287 | */ | ||
288 | error = 0; | ||
289 | perms.allow = request; | ||
290 | } else if (error == -ENOENT) | ||
291 | info = "Failed name lookup - deleted entry"; | ||
292 | else if (error == -ESTALE) | ||
293 | info = "Failed name lookup - disconnected path"; | ||
294 | else if (error == -ENAMETOOLONG) | ||
295 | info = "Failed name lookup - name too long"; | ||
296 | else | ||
297 | info = "Failed name lookup"; | ||
298 | } else { | ||
299 | aa_str_perms(profile->file.dfa, profile->file.start, name, cond, | ||
300 | &perms); | ||
301 | if (request & ~perms.allow) | ||
302 | error = -EACCES; | ||
303 | } | ||
304 | error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, | ||
305 | NULL, cond->uid, info, error); | ||
306 | kfree(buffer); | ||
307 | |||
308 | return error; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * xindex_is_subset - helper for aa_path_link | ||
313 | * @link: link permission set | ||
314 | * @target: target permission set | ||
315 | * | ||
316 | * test target x permissions are equal OR a subset of link x permissions | ||
317 | * this is done as part of the subset test, where a hardlink must have | ||
318 | * a subset of permissions that the target has. | ||
319 | * | ||
320 | * Returns: %1 if subset else %0 | ||
321 | */ | ||
322 | static inline bool xindex_is_subset(u32 link, u32 target) | ||
323 | { | ||
324 | if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || | ||
325 | ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) | ||
326 | return 0; | ||
327 | |||
328 | return 1; | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * aa_path_link - Handle hard link permission check | ||
333 | * @profile: the profile being enforced (NOT NULL) | ||
334 | * @old_dentry: the target dentry (NOT NULL) | ||
335 | * @new_dir: directory the new link will be created in (NOT NULL) | ||
336 | * @new_dentry: the link being created (NOT NULL) | ||
337 | * | ||
338 | * Handle the permission test for a link & target pair. Permission | ||
339 | * is encoded as a pair where the link permission is determined | ||
340 | * first, and if allowed, the target is tested. The target test | ||
341 | * is done from the point of the link match (not start of DFA) | ||
342 | * making the target permission dependent on the link permission match. | ||
343 | * | ||
344 | * The subset test if required forces that permissions granted | ||
345 | * on link are a subset of the permission granted to target. | ||
346 | * | ||
347 | * Returns: %0 if allowed else error | ||
348 | */ | ||
349 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
350 | struct path *new_dir, struct dentry *new_dentry) | ||
351 | { | ||
352 | struct path link = { new_dir->mnt, new_dentry }; | ||
353 | struct path target = { new_dir->mnt, old_dentry }; | ||
354 | struct path_cond cond = { | ||
355 | old_dentry->d_inode->i_uid, | ||
356 | old_dentry->d_inode->i_mode | ||
357 | }; | ||
358 | char *buffer = NULL, *buffer2 = NULL; | ||
359 | const char *lname, *tname = NULL, *info = NULL; | ||
360 | struct file_perms lperms, perms; | ||
361 | u32 request = AA_MAY_LINK; | ||
362 | unsigned int state; | ||
363 | int error; | ||
364 | |||
365 | lperms = nullperms; | ||
366 | |||
367 | /* buffer freed below, lname is pointer in buffer */ | ||
368 | error = aa_get_name(&link, profile->path_flags, &buffer, &lname); | ||
369 | if (error) | ||
370 | goto audit; | ||
371 | |||
372 | /* buffer2 freed below, tname is pointer in buffer2 */ | ||
373 | error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); | ||
374 | if (error) | ||
375 | goto audit; | ||
376 | |||
377 | error = -EACCES; | ||
378 | /* aa_str_perms - handles the case of the dfa being NULL */ | ||
379 | state = aa_str_perms(profile->file.dfa, profile->file.start, lname, | ||
380 | &cond, &lperms); | ||
381 | |||
382 | if (!(lperms.allow & AA_MAY_LINK)) | ||
383 | goto audit; | ||
384 | |||
385 | /* test to see if target can be paired with link */ | ||
386 | state = aa_dfa_null_transition(profile->file.dfa, state); | ||
387 | aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); | ||
388 | |||
389 | /* force audit/quiet masks for link are stored in the second entry | ||
390 | * in the link pair. | ||
391 | */ | ||
392 | lperms.audit = perms.audit; | ||
393 | lperms.quiet = perms.quiet; | ||
394 | lperms.kill = perms.kill; | ||
395 | |||
396 | if (!(perms.allow & AA_MAY_LINK)) { | ||
397 | info = "target restricted"; | ||
398 | goto audit; | ||
399 | } | ||
400 | |||
401 | /* done if link subset test is not required */ | ||
402 | if (!(perms.allow & AA_LINK_SUBSET)) | ||
403 | goto done_tests; | ||
404 | |||
405 | /* Do link perm subset test requiring allowed permission on link are a | ||
406 | * subset of the allowed permissions on target. | ||
407 | */ | ||
408 | aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond, | ||
409 | &perms); | ||
410 | |||
411 | /* AA_MAY_LINK is not considered in the subset test */ | ||
412 | request = lperms.allow & ~AA_MAY_LINK; | ||
413 | lperms.allow &= perms.allow | AA_MAY_LINK; | ||
414 | |||
415 | request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); | ||
416 | if (request & ~lperms.allow) { | ||
417 | goto audit; | ||
418 | } else if ((lperms.allow & MAY_EXEC) && | ||
419 | !xindex_is_subset(lperms.xindex, perms.xindex)) { | ||
420 | lperms.allow &= ~MAY_EXEC; | ||
421 | request |= MAY_EXEC; | ||
422 | info = "link not subset of target"; | ||
423 | goto audit; | ||
424 | } | ||
425 | |||
426 | done_tests: | ||
427 | error = 0; | ||
428 | |||
429 | audit: | ||
430 | error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, | ||
431 | lname, tname, cond.uid, info, error); | ||
432 | kfree(buffer); | ||
433 | kfree(buffer2); | ||
434 | |||
435 | return error; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * aa_file_perm - do permission revalidation check & audit for @file | ||
440 | * @op: operation being checked | ||
441 | * @profile: profile being enforced (NOT NULL) | ||
442 | * @file: file to revalidate access permissions on (NOT NULL) | ||
443 | * @request: requested permissions | ||
444 | * | ||
445 | * Returns: %0 if access allowed else error | ||
446 | */ | ||
447 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
448 | u32 request) | ||
449 | { | ||
450 | struct path_cond cond = { | ||
451 | .uid = file->f_path.dentry->d_inode->i_uid, | ||
452 | .mode = file->f_path.dentry->d_inode->i_mode | ||
453 | }; | ||
454 | |||
455 | return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED, | ||
456 | request, &cond); | ||
457 | } | ||
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 00000000000..38ccaea0820 --- /dev/null +++ b/security/apparmor/include/apparmor.h | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor basic global and lib definitions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __APPARMOR_H | ||
16 | #define __APPARMOR_H | ||
17 | |||
18 | #include <linux/fs.h> | ||
19 | |||
20 | #include "match.h" | ||
21 | |||
22 | /* Control parameters settable through module/boot flags */ | ||
23 | extern enum audit_mode aa_g_audit; | ||
24 | extern int aa_g_audit_header; | ||
25 | extern int aa_g_debug; | ||
26 | extern int aa_g_lock_policy; | ||
27 | extern int aa_g_logsyscall; | ||
28 | extern int aa_g_paranoid_load; | ||
29 | extern unsigned int aa_g_path_max; | ||
30 | |||
31 | /* | ||
32 | * DEBUG remains global (no per profile flag) since it is mostly used in sysctl | ||
33 | * which is not related to profile accesses. | ||
34 | */ | ||
35 | |||
36 | #define AA_DEBUG(fmt, args...) \ | ||
37 | do { \ | ||
38 | if (aa_g_debug && printk_ratelimit()) \ | ||
39 | printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ | ||
40 | } while (0) | ||
41 | |||
42 | #define AA_ERROR(fmt, args...) \ | ||
43 | do { \ | ||
44 | if (printk_ratelimit()) \ | ||
45 | printk(KERN_ERR "AppArmor: " fmt, ##args); \ | ||
46 | } while (0) | ||
47 | |||
48 | /* Flag indicating whether initialization completed */ | ||
49 | extern int apparmor_initialized __initdata; | ||
50 | |||
51 | /* fn's in lib */ | ||
52 | char *aa_split_fqname(char *args, char **ns_name); | ||
53 | void aa_info_message(const char *str); | ||
54 | void *kvmalloc(size_t size); | ||
55 | void kvfree(void *buffer); | ||
56 | |||
57 | |||
58 | /** | ||
59 | * aa_strneq - compare null terminated @str to a non null terminated substring | ||
60 | * @str: a null terminated string | ||
61 | * @sub: a substring, not necessarily null terminated | ||
62 | * @len: length of @sub to compare | ||
63 | * | ||
64 | * The @str string must be full consumed for this to be considered a match | ||
65 | */ | ||
66 | static inline bool aa_strneq(const char *str, const char *sub, int len) | ||
67 | { | ||
68 | return !strncmp(str, sub, len) && !str[len]; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * aa_dfa_null_transition - step to next state after null character | ||
73 | * @dfa: the dfa to match against | ||
74 | * @start: the state of the dfa to start matching in | ||
75 | * | ||
76 | * aa_dfa_null_transition transitions to the next state after a null | ||
77 | * character which is not used in standard matching and is only | ||
78 | * used to separate pairs. | ||
79 | */ | ||
80 | static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, | ||
81 | unsigned int start) | ||
82 | { | ||
83 | /* the null transition only needs the string's null terminator byte */ | ||
84 | return aa_dfa_match_len(dfa, start, "", 1); | ||
85 | } | ||
86 | |||
87 | static inline bool mediated_filesystem(struct inode *inode) | ||
88 | { | ||
89 | return !(inode->i_sb->s_flags & MS_NOUSER); | ||
90 | } | ||
91 | |||
92 | #endif /* __APPARMOR_H */ | ||
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 00000000000..cb1e93a114d --- /dev/null +++ b/security/apparmor/include/apparmorfs.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor filesystem definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_APPARMORFS_H | ||
16 | #define __AA_APPARMORFS_H | ||
17 | |||
18 | extern void __init aa_destroy_aafs(void); | ||
19 | |||
20 | #endif /* __AA_APPARMORFS_H */ | ||
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h new file mode 100644 index 00000000000..1951786d32e --- /dev/null +++ b/security/apparmor/include/audit.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor auditing function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_AUDIT_H | ||
16 | #define __AA_AUDIT_H | ||
17 | |||
18 | #include <linux/audit.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/lsm_audit.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #include "file.h" | ||
25 | |||
26 | struct aa_profile; | ||
27 | |||
28 | extern const char *audit_mode_names[]; | ||
29 | #define AUDIT_MAX_INDEX 5 | ||
30 | |||
31 | #define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ | ||
32 | |||
33 | enum audit_mode { | ||
34 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | ||
35 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | ||
36 | AUDIT_QUIET, /* quiet all messages */ | ||
37 | AUDIT_NOQUIET, /* do not quiet audit messages */ | ||
38 | AUDIT_ALL /* audit all accesses */ | ||
39 | }; | ||
40 | |||
41 | enum audit_type { | ||
42 | AUDIT_APPARMOR_AUDIT, | ||
43 | AUDIT_APPARMOR_ALLOWED, | ||
44 | AUDIT_APPARMOR_DENIED, | ||
45 | AUDIT_APPARMOR_HINT, | ||
46 | AUDIT_APPARMOR_STATUS, | ||
47 | AUDIT_APPARMOR_ERROR, | ||
48 | AUDIT_APPARMOR_KILL | ||
49 | }; | ||
50 | |||
51 | extern const char *op_table[]; | ||
52 | enum aa_ops { | ||
53 | OP_NULL, | ||
54 | |||
55 | OP_SYSCTL, | ||
56 | OP_CAPABLE, | ||
57 | |||
58 | OP_UNLINK, | ||
59 | OP_MKDIR, | ||
60 | OP_RMDIR, | ||
61 | OP_MKNOD, | ||
62 | OP_TRUNC, | ||
63 | OP_LINK, | ||
64 | OP_SYMLINK, | ||
65 | OP_RENAME_SRC, | ||
66 | OP_RENAME_DEST, | ||
67 | OP_CHMOD, | ||
68 | OP_CHOWN, | ||
69 | OP_GETATTR, | ||
70 | OP_OPEN, | ||
71 | |||
72 | OP_FPERM, | ||
73 | OP_FLOCK, | ||
74 | OP_FMMAP, | ||
75 | OP_FMPROT, | ||
76 | |||
77 | OP_CREATE, | ||
78 | OP_POST_CREATE, | ||
79 | OP_BIND, | ||
80 | OP_CONNECT, | ||
81 | OP_LISTEN, | ||
82 | OP_ACCEPT, | ||
83 | OP_SENDMSG, | ||
84 | OP_RECVMSG, | ||
85 | OP_GETSOCKNAME, | ||
86 | OP_GETPEERNAME, | ||
87 | OP_GETSOCKOPT, | ||
88 | OP_SETSOCKOPT, | ||
89 | OP_SOCK_SHUTDOWN, | ||
90 | |||
91 | OP_PTRACE, | ||
92 | |||
93 | OP_EXEC, | ||
94 | OP_CHANGE_HAT, | ||
95 | OP_CHANGE_PROFILE, | ||
96 | OP_CHANGE_ONEXEC, | ||
97 | |||
98 | OP_SETPROCATTR, | ||
99 | OP_SETRLIMIT, | ||
100 | |||
101 | OP_PROF_REPL, | ||
102 | OP_PROF_LOAD, | ||
103 | OP_PROF_RM, | ||
104 | }; | ||
105 | |||
106 | |||
107 | /* define a short hand for apparmor_audit_data portion of common_audit_data */ | ||
108 | #define aad apparmor_audit_data | ||
109 | |||
110 | void aa_audit_msg(int type, struct common_audit_data *sa, | ||
111 | void (*cb) (struct audit_buffer *, void *)); | ||
112 | int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | ||
113 | struct common_audit_data *sa, | ||
114 | void (*cb) (struct audit_buffer *, void *)); | ||
115 | |||
116 | static inline int complain_error(int error) | ||
117 | { | ||
118 | if (error == -EPERM || error == -EACCES) | ||
119 | return 0; | ||
120 | return error; | ||
121 | } | ||
122 | |||
123 | #endif /* __AA_AUDIT_H */ | ||
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h new file mode 100644 index 00000000000..c24d2959ea0 --- /dev/null +++ b/security/apparmor/include/capability.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor capability mediation definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_CAPABILITY_H | ||
16 | #define __AA_CAPABILITY_H | ||
17 | |||
18 | #include <linux/sched.h> | ||
19 | |||
20 | struct aa_profile; | ||
21 | |||
22 | /* aa_caps - confinement data for capabilities | ||
23 | * @allowed: capabilities mask | ||
24 | * @audit: caps that are to be audited | ||
25 | * @quiet: caps that should not be audited | ||
26 | * @kill: caps that when requested will result in the task being killed | ||
27 | * @extended: caps that are subject finer grained mediation | ||
28 | */ | ||
29 | struct aa_caps { | ||
30 | kernel_cap_t allow; | ||
31 | kernel_cap_t audit; | ||
32 | kernel_cap_t quiet; | ||
33 | kernel_cap_t kill; | ||
34 | kernel_cap_t extended; | ||
35 | }; | ||
36 | |||
37 | int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, | ||
38 | int audit); | ||
39 | |||
40 | static inline void aa_free_cap_rules(struct aa_caps *caps) | ||
41 | { | ||
42 | /* NOP */ | ||
43 | } | ||
44 | |||
45 | #endif /* __AA_CAPBILITY_H */ | ||
diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h new file mode 100644 index 00000000000..a9cbee4d9e4 --- /dev/null +++ b/security/apparmor/include/context.h | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor contexts used to associate "labels" to objects. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_CONTEXT_H | ||
16 | #define __AA_CONTEXT_H | ||
17 | |||
18 | #include <linux/cred.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/sched.h> | ||
21 | |||
22 | #include "policy.h" | ||
23 | |||
24 | /* struct aa_file_cxt - the AppArmor context the file was opened in | ||
25 | * @perms: the permission the file was opened with | ||
26 | * | ||
27 | * The file_cxt could currently be directly stored in file->f_security | ||
28 | * as the profile reference is now stored in the f_cred. However the | ||
29 | * cxt struct will expand in the future so we keep the struct. | ||
30 | */ | ||
31 | struct aa_file_cxt { | ||
32 | u16 allow; | ||
33 | }; | ||
34 | |||
35 | /** | ||
36 | * aa_alloc_file_context - allocate file_cxt | ||
37 | * @gfp: gfp flags for allocation | ||
38 | * | ||
39 | * Returns: file_cxt or NULL on failure | ||
40 | */ | ||
41 | static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) | ||
42 | { | ||
43 | return kzalloc(sizeof(struct aa_file_cxt), gfp); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * aa_free_file_context - free a file_cxt | ||
48 | * @cxt: file_cxt to free (MAYBE_NULL) | ||
49 | */ | ||
50 | static inline void aa_free_file_context(struct aa_file_cxt *cxt) | ||
51 | { | ||
52 | if (cxt) | ||
53 | kzfree(cxt); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * struct aa_task_cxt - primary label for confined tasks | ||
58 | * @profile: the current profile (NOT NULL) | ||
59 | * @exec: profile to transition to on next exec (MAYBE NULL) | ||
60 | * @previous: profile the task may return to (MAYBE NULL) | ||
61 | * @token: magic value the task must know for returning to @previous_profile | ||
62 | * | ||
63 | * Contains the task's current profile (which could change due to | ||
64 | * change_hat). Plus the hat_magic needed during change_hat. | ||
65 | * | ||
66 | * TODO: make so a task can be confined by a stack of contexts | ||
67 | */ | ||
68 | struct aa_task_cxt { | ||
69 | struct aa_profile *profile; | ||
70 | struct aa_profile *onexec; | ||
71 | struct aa_profile *previous; | ||
72 | u64 token; | ||
73 | }; | ||
74 | |||
75 | struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); | ||
76 | void aa_free_task_context(struct aa_task_cxt *cxt); | ||
77 | void aa_dup_task_context(struct aa_task_cxt *new, | ||
78 | const struct aa_task_cxt *old); | ||
79 | int aa_replace_current_profile(struct aa_profile *profile); | ||
80 | int aa_set_current_onexec(struct aa_profile *profile); | ||
81 | int aa_set_current_hat(struct aa_profile *profile, u64 token); | ||
82 | int aa_restore_previous_profile(u64 cookie); | ||
83 | |||
84 | /** | ||
85 | * __aa_task_is_confined - determine if @task has any confinement | ||
86 | * @task: task to check confinement of (NOT NULL) | ||
87 | * | ||
88 | * If @task != current needs to be called in RCU safe critical section | ||
89 | */ | ||
90 | static inline bool __aa_task_is_confined(struct task_struct *task) | ||
91 | { | ||
92 | struct aa_task_cxt *cxt = __task_cred(task)->security; | ||
93 | |||
94 | BUG_ON(!cxt || !cxt->profile); | ||
95 | if (unconfined(aa_newest_version(cxt->profile))) | ||
96 | return 0; | ||
97 | |||
98 | return 1; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * aa_cred_profile - obtain cred's profiles | ||
103 | * @cred: cred to obtain profiles from (NOT NULL) | ||
104 | * | ||
105 | * Returns: confining profile | ||
106 | * | ||
107 | * does NOT increment reference count | ||
108 | */ | ||
109 | static inline struct aa_profile *aa_cred_profile(const struct cred *cred) | ||
110 | { | ||
111 | struct aa_task_cxt *cxt = cred->security; | ||
112 | BUG_ON(!cxt || !cxt->profile); | ||
113 | return aa_newest_version(cxt->profile); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * __aa_current_profile - find the current tasks confining profile | ||
118 | * | ||
119 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
120 | * | ||
121 | * This fn will not update the tasks cred to the most up to date version | ||
122 | * of the profile so it is safe to call when inside of locks. | ||
123 | */ | ||
124 | static inline struct aa_profile *__aa_current_profile(void) | ||
125 | { | ||
126 | return aa_cred_profile(current_cred()); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * aa_current_profile - find the current tasks confining profile and do updates | ||
131 | * | ||
132 | * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) | ||
133 | * | ||
134 | * This fn will update the tasks cred structure if the profile has been | ||
135 | * replaced. Not safe to call inside locks | ||
136 | */ | ||
137 | static inline struct aa_profile *aa_current_profile(void) | ||
138 | { | ||
139 | const struct aa_task_cxt *cxt = current_cred()->security; | ||
140 | struct aa_profile *profile; | ||
141 | BUG_ON(!cxt || !cxt->profile); | ||
142 | |||
143 | profile = aa_newest_version(cxt->profile); | ||
144 | /* | ||
145 | * Whether or not replacement succeeds, use newest profile so | ||
146 | * there is no need to update it after replacement. | ||
147 | */ | ||
148 | if (unlikely((cxt->profile != profile))) | ||
149 | aa_replace_current_profile(profile); | ||
150 | |||
151 | return profile; | ||
152 | } | ||
153 | |||
154 | #endif /* __AA_CONTEXT_H */ | ||
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h new file mode 100644 index 00000000000..de04464f0a3 --- /dev/null +++ b/security/apparmor/include/domain.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security domain transition function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/binfmts.h> | ||
16 | #include <linux/types.h> | ||
17 | |||
18 | #ifndef __AA_DOMAIN_H | ||
19 | #define __AA_DOMAIN_H | ||
20 | |||
21 | struct aa_domain { | ||
22 | int size; | ||
23 | char **table; | ||
24 | }; | ||
25 | |||
26 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | ||
27 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | ||
28 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | ||
29 | void apparmor_bprm_committed_creds(struct linux_binprm *bprm); | ||
30 | |||
31 | void aa_free_domain_entries(struct aa_domain *domain); | ||
32 | int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); | ||
33 | int aa_change_profile(const char *ns_name, const char *name, bool onexec, | ||
34 | bool permtest); | ||
35 | |||
36 | #endif /* __AA_DOMAIN_H */ | ||
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h new file mode 100644 index 00000000000..be36feabb16 --- /dev/null +++ b/security/apparmor/include/file.h | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor file mediation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_FILE_H | ||
16 | #define __AA_FILE_H | ||
17 | |||
18 | #include <linux/path.h> | ||
19 | |||
20 | #include "domain.h" | ||
21 | #include "match.h" | ||
22 | |||
23 | struct aa_profile; | ||
24 | |||
25 | /* | ||
26 | * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags | ||
27 | * for profile permissions | ||
28 | */ | ||
29 | #define AA_MAY_CREATE 0x0010 | ||
30 | #define AA_MAY_DELETE 0x0020 | ||
31 | #define AA_MAY_META_WRITE 0x0040 | ||
32 | #define AA_MAY_META_READ 0x0080 | ||
33 | |||
34 | #define AA_MAY_CHMOD 0x0100 | ||
35 | #define AA_MAY_CHOWN 0x0200 | ||
36 | #define AA_MAY_LOCK 0x0400 | ||
37 | #define AA_EXEC_MMAP 0x0800 | ||
38 | |||
39 | #define AA_MAY_LINK 0x1000 | ||
40 | #define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ | ||
41 | #define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ | ||
42 | #define AA_MAY_CHANGE_PROFILE 0x80000000 | ||
43 | #define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ | ||
44 | |||
45 | #define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ | ||
46 | AA_MAY_CREATE | AA_MAY_DELETE | \ | ||
47 | AA_MAY_META_READ | AA_MAY_META_WRITE | \ | ||
48 | AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ | ||
49 | AA_EXEC_MMAP | AA_MAY_LINK) | ||
50 | |||
51 | /* | ||
52 | * The xindex is broken into 3 parts | ||
53 | * - index - an index into either the exec name table or the variable table | ||
54 | * - exec type - which determines how the executable name and index are used | ||
55 | * - flags - which modify how the destination name is applied | ||
56 | */ | ||
57 | #define AA_X_INDEX_MASK 0x03ff | ||
58 | |||
59 | #define AA_X_TYPE_MASK 0x0c00 | ||
60 | #define AA_X_TYPE_SHIFT 10 | ||
61 | #define AA_X_NONE 0x0000 | ||
62 | #define AA_X_NAME 0x0400 /* use executable name px */ | ||
63 | #define AA_X_TABLE 0x0800 /* use a specified name ->n# */ | ||
64 | |||
65 | #define AA_X_UNSAFE 0x1000 | ||
66 | #define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ | ||
67 | #define AA_X_INHERIT 0x4000 | ||
68 | #define AA_X_UNCONFINED 0x8000 | ||
69 | |||
70 | /* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ | ||
71 | #define AA_SECURE_X_NEEDED 0x8000 | ||
72 | |||
73 | /* need to make conditional which ones are being set */ | ||
74 | struct path_cond { | ||
75 | uid_t uid; | ||
76 | umode_t mode; | ||
77 | }; | ||
78 | |||
79 | /* struct file_perms - file permission | ||
80 | * @allow: mask of permissions that are allowed | ||
81 | * @audit: mask of permissions to force an audit message for | ||
82 | * @quiet: mask of permissions to quiet audit messages for | ||
83 | * @kill: mask of permissions that when matched will kill the task | ||
84 | * @xindex: exec transition index if @allow contains MAY_EXEC | ||
85 | * | ||
86 | * The @audit and @queit mask should be mutually exclusive. | ||
87 | */ | ||
88 | struct file_perms { | ||
89 | u32 allow; | ||
90 | u32 audit; | ||
91 | u32 quiet; | ||
92 | u32 kill; | ||
93 | u16 xindex; | ||
94 | }; | ||
95 | |||
96 | extern struct file_perms nullperms; | ||
97 | |||
98 | #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) | ||
99 | |||
100 | /* FIXME: split perms from dfa and match this to description | ||
101 | * also add delegation info. | ||
102 | */ | ||
103 | static inline u16 dfa_map_xindex(u16 mask) | ||
104 | { | ||
105 | u16 old_index = (mask >> 10) & 0xf; | ||
106 | u16 index = 0; | ||
107 | |||
108 | if (mask & 0x100) | ||
109 | index |= AA_X_UNSAFE; | ||
110 | if (mask & 0x200) | ||
111 | index |= AA_X_INHERIT; | ||
112 | if (mask & 0x80) | ||
113 | index |= AA_X_UNCONFINED; | ||
114 | |||
115 | if (old_index == 1) { | ||
116 | index |= AA_X_UNCONFINED; | ||
117 | } else if (old_index == 2) { | ||
118 | index |= AA_X_NAME; | ||
119 | } else if (old_index == 3) { | ||
120 | index |= AA_X_NAME | AA_X_CHILD; | ||
121 | } else { | ||
122 | index |= AA_X_TABLE; | ||
123 | index |= old_index - 4; | ||
124 | } | ||
125 | |||
126 | return index; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * map old dfa inline permissions to new format | ||
131 | */ | ||
132 | #define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ | ||
133 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
134 | #define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) | ||
135 | #define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) | ||
136 | #define dfa_user_xindex(dfa, state) \ | ||
137 | (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) | ||
138 | |||
139 | #define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ | ||
140 | 0x7f) | \ | ||
141 | ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) | ||
142 | #define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) | ||
143 | #define dfa_other_quiet(dfa, state) \ | ||
144 | ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) | ||
145 | #define dfa_other_xindex(dfa, state) \ | ||
146 | dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) | ||
147 | |||
148 | int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | ||
149 | gfp_t gfp, int op, u32 request, const char *name, | ||
150 | const char *target, uid_t ouid, const char *info, int error); | ||
151 | |||
152 | /** | ||
153 | * struct aa_file_rules - components used for file rule permissions | ||
154 | * @dfa: dfa to match path names and conditionals against | ||
155 | * @perms: permission table indexed by the matched state accept entry of @dfa | ||
156 | * @trans: transition table for indexed by named x transitions | ||
157 | * | ||
158 | * File permission are determined by matching a path against @dfa and then | ||
159 | * then using the value of the accept entry for the matching state as | ||
160 | * an index into @perms. If a named exec transition is required it is | ||
161 | * looked up in the transition table. | ||
162 | */ | ||
163 | struct aa_file_rules { | ||
164 | unsigned int start; | ||
165 | struct aa_dfa *dfa; | ||
166 | /* struct perms perms; */ | ||
167 | struct aa_domain trans; | ||
168 | /* TODO: add delegate table */ | ||
169 | }; | ||
170 | |||
171 | unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, | ||
172 | const char *name, struct path_cond *cond, | ||
173 | struct file_perms *perms); | ||
174 | |||
175 | int aa_path_perm(int op, struct aa_profile *profile, struct path *path, | ||
176 | int flags, u32 request, struct path_cond *cond); | ||
177 | |||
178 | int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, | ||
179 | struct path *new_dir, struct dentry *new_dentry); | ||
180 | |||
181 | int aa_file_perm(int op, struct aa_profile *profile, struct file *file, | ||
182 | u32 request); | ||
183 | |||
184 | static inline void aa_free_file_rules(struct aa_file_rules *rules) | ||
185 | { | ||
186 | aa_put_dfa(rules->dfa); | ||
187 | aa_free_domain_entries(&rules->trans); | ||
188 | } | ||
189 | |||
190 | #define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) | ||
191 | |||
192 | /* from namei.c */ | ||
193 | #define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) | ||
194 | |||
195 | /** | ||
196 | * aa_map_file_perms - map file flags to AppArmor permissions | ||
197 | * @file: open file to map flags to AppArmor permissions | ||
198 | * | ||
199 | * Returns: apparmor permission set for the file | ||
200 | */ | ||
201 | static inline u32 aa_map_file_to_perms(struct file *file) | ||
202 | { | ||
203 | int flags = MAP_OPEN_FLAGS(file->f_flags); | ||
204 | u32 perms = ACC_FMODE(file->f_mode); | ||
205 | |||
206 | if ((flags & O_APPEND) && (perms & MAY_WRITE)) | ||
207 | perms = (perms & ~MAY_WRITE) | MAY_APPEND; | ||
208 | /* trunc implies write permission */ | ||
209 | if (flags & O_TRUNC) | ||
210 | perms |= MAY_WRITE; | ||
211 | if (flags & O_CREAT) | ||
212 | perms |= AA_MAY_CREATE; | ||
213 | |||
214 | return perms; | ||
215 | } | ||
216 | |||
217 | #endif /* __AA_FILE_H */ | ||
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h new file mode 100644 index 00000000000..aeda0fbc8b2 --- /dev/null +++ b/security/apparmor/include/ipc.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor ipc mediation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_IPC_H | ||
16 | #define __AA_IPC_H | ||
17 | |||
18 | #include <linux/sched.h> | ||
19 | |||
20 | struct aa_profile; | ||
21 | |||
22 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
23 | struct aa_profile *tracee, unsigned int mode); | ||
24 | |||
25 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
26 | unsigned int mode); | ||
27 | |||
28 | #endif /* __AA_IPC_H */ | ||
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h new file mode 100644 index 00000000000..734a6d35112 --- /dev/null +++ b/security/apparmor/include/match.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy dfa matching engine definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_MATCH_H | ||
16 | #define __AA_MATCH_H | ||
17 | |||
18 | #include <linux/workqueue.h> | ||
19 | |||
20 | #define DFA_NOMATCH 0 | ||
21 | #define DFA_START 1 | ||
22 | |||
23 | #define DFA_VALID_PERM_MASK 0xffffffff | ||
24 | #define DFA_VALID_PERM2_MASK 0xffffffff | ||
25 | |||
26 | /** | ||
27 | * The format used for transition tables is based on the GNU flex table | ||
28 | * file format (--tables-file option; see Table File Format in the flex | ||
29 | * info pages and the flex sources for documentation). The magic number | ||
30 | * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because | ||
31 | * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used | ||
32 | * slightly differently (see the apparmor-parser package). | ||
33 | */ | ||
34 | |||
35 | #define YYTH_MAGIC 0x1B5E783D | ||
36 | #define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ | ||
37 | |||
38 | struct table_set_header { | ||
39 | u32 th_magic; /* YYTH_MAGIC */ | ||
40 | u32 th_hsize; | ||
41 | u32 th_ssize; | ||
42 | u16 th_flags; | ||
43 | char th_version[]; | ||
44 | }; | ||
45 | |||
46 | /* The YYTD_ID are one less than flex table mappings. The flex id | ||
47 | * has 1 subtracted at table load time, this allows us to directly use the | ||
48 | * ID's as indexes. | ||
49 | */ | ||
50 | #define YYTD_ID_ACCEPT 0 | ||
51 | #define YYTD_ID_BASE 1 | ||
52 | #define YYTD_ID_CHK 2 | ||
53 | #define YYTD_ID_DEF 3 | ||
54 | #define YYTD_ID_EC 4 | ||
55 | #define YYTD_ID_META 5 | ||
56 | #define YYTD_ID_ACCEPT2 6 | ||
57 | #define YYTD_ID_NXT 7 | ||
58 | #define YYTD_ID_TSIZE 8 | ||
59 | |||
60 | #define YYTD_DATA8 1 | ||
61 | #define YYTD_DATA16 2 | ||
62 | #define YYTD_DATA32 4 | ||
63 | #define YYTD_DATA64 8 | ||
64 | |||
65 | /* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the | ||
66 | * first flags | ||
67 | */ | ||
68 | #define ACCEPT1_FLAGS(X) ((X) & 0x3f) | ||
69 | #define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) | ||
70 | #define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) | ||
71 | #define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) | ||
72 | #define DFA_FLAG_VERIFY_STATES 0x1000 | ||
73 | |||
74 | struct table_header { | ||
75 | u16 td_id; | ||
76 | u16 td_flags; | ||
77 | u32 td_hilen; | ||
78 | u32 td_lolen; | ||
79 | char td_data[]; | ||
80 | }; | ||
81 | |||
82 | #define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) | ||
83 | #define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) | ||
84 | #define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) | ||
85 | #define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) | ||
86 | #define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) | ||
87 | #define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) | ||
88 | #define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) | ||
89 | |||
90 | struct aa_dfa { | ||
91 | struct kref count; | ||
92 | u16 flags; | ||
93 | struct table_header *tables[YYTD_ID_TSIZE]; | ||
94 | }; | ||
95 | |||
96 | #define byte_to_byte(X) (X) | ||
97 | |||
98 | #define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ | ||
99 | do { \ | ||
100 | typeof(LEN) __i; \ | ||
101 | TYPE *__t = (TYPE *) TABLE; \ | ||
102 | TYPE *__b = (TYPE *) BLOB; \ | ||
103 | for (__i = 0; __i < LEN; __i++) { \ | ||
104 | __t[__i] = NTOHX(__b[__i]); \ | ||
105 | } \ | ||
106 | } while (0) | ||
107 | |||
108 | static inline size_t table_size(size_t len, size_t el_size) | ||
109 | { | ||
110 | return ALIGN(sizeof(struct table_header) + len * el_size, 8); | ||
111 | } | ||
112 | |||
113 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); | ||
114 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
115 | const char *str, int len); | ||
116 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
117 | const char *str); | ||
118 | void aa_dfa_free_kref(struct kref *kref); | ||
119 | |||
120 | /** | ||
121 | * aa_put_dfa - put a dfa refcount | ||
122 | * @dfa: dfa to put refcount (MAYBE NULL) | ||
123 | * | ||
124 | * Requires: if @dfa != NULL that a valid refcount be held | ||
125 | */ | ||
126 | static inline void aa_put_dfa(struct aa_dfa *dfa) | ||
127 | { | ||
128 | if (dfa) | ||
129 | kref_put(&dfa->count, aa_dfa_free_kref); | ||
130 | } | ||
131 | |||
132 | #endif /* __AA_MATCH_H */ | ||
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h new file mode 100644 index 00000000000..27b327a7fae --- /dev/null +++ b/security/apparmor/include/path.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor basic path manipulation function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_PATH_H | ||
16 | #define __AA_PATH_H | ||
17 | |||
18 | |||
19 | enum path_flags { | ||
20 | PATH_IS_DIR = 0x1, /* path is a directory */ | ||
21 | PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ | ||
22 | PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ | ||
23 | PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ | ||
24 | |||
25 | PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ | ||
26 | PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ | ||
27 | }; | ||
28 | |||
29 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name); | ||
30 | |||
31 | #endif /* __AA_PATH_H */ | ||
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h new file mode 100644 index 00000000000..aeda5cf5690 --- /dev/null +++ b/security/apparmor/include/policy.h | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_POLICY_H | ||
16 | #define __AA_POLICY_H | ||
17 | |||
18 | #include <linux/capability.h> | ||
19 | #include <linux/cred.h> | ||
20 | #include <linux/kref.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/socket.h> | ||
24 | |||
25 | #include "apparmor.h" | ||
26 | #include "audit.h" | ||
27 | #include "capability.h" | ||
28 | #include "domain.h" | ||
29 | #include "file.h" | ||
30 | #include "resource.h" | ||
31 | |||
32 | extern const char *profile_mode_names[]; | ||
33 | #define APPARMOR_NAMES_MAX_INDEX 3 | ||
34 | |||
35 | #define COMPLAIN_MODE(_profile) \ | ||
36 | ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ | ||
37 | ((_profile)->mode == APPARMOR_COMPLAIN)) | ||
38 | |||
39 | #define KILL_MODE(_profile) \ | ||
40 | ((aa_g_profile_mode == APPARMOR_KILL) || \ | ||
41 | ((_profile)->mode == APPARMOR_KILL)) | ||
42 | |||
43 | #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) | ||
44 | |||
45 | /* | ||
46 | * FIXME: currently need a clean way to replace and remove profiles as a | ||
47 | * set. It should be done at the namespace level. | ||
48 | * Either, with a set of profiles loaded at the namespace level or via | ||
49 | * a mark and remove marked interface. | ||
50 | */ | ||
51 | enum profile_mode { | ||
52 | APPARMOR_ENFORCE, /* enforce access rules */ | ||
53 | APPARMOR_COMPLAIN, /* allow and log access violations */ | ||
54 | APPARMOR_KILL, /* kill task on access violation */ | ||
55 | }; | ||
56 | |||
57 | enum profile_flags { | ||
58 | PFLAG_HAT = 1, /* profile is a hat */ | ||
59 | PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ | ||
60 | PFLAG_NULL = 4, /* profile is null learning profile */ | ||
61 | PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ | ||
62 | PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ | ||
63 | PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ | ||
64 | PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ | ||
65 | PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ | ||
66 | |||
67 | /* These flags must correspond with PATH_flags */ | ||
68 | PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ | ||
69 | }; | ||
70 | |||
71 | struct aa_profile; | ||
72 | |||
73 | /* struct aa_policy - common part of both namespaces and profiles | ||
74 | * @name: name of the object | ||
75 | * @hname - The hierarchical name | ||
76 | * @count: reference count of the obj | ||
77 | * @list: list policy object is on | ||
78 | * @profiles: head of the profiles list contained in the object | ||
79 | */ | ||
80 | struct aa_policy { | ||
81 | char *name; | ||
82 | char *hname; | ||
83 | struct kref count; | ||
84 | struct list_head list; | ||
85 | struct list_head profiles; | ||
86 | }; | ||
87 | |||
88 | /* struct aa_ns_acct - accounting of profiles in namespace | ||
89 | * @max_size: maximum space allowed for all profiles in namespace | ||
90 | * @max_count: maximum number of profiles that can be in this namespace | ||
91 | * @size: current size of profiles | ||
92 | * @count: current count of profiles (includes null profiles) | ||
93 | */ | ||
94 | struct aa_ns_acct { | ||
95 | int max_size; | ||
96 | int max_count; | ||
97 | int size; | ||
98 | int count; | ||
99 | }; | ||
100 | |||
101 | /* struct aa_namespace - namespace for a set of profiles | ||
102 | * @base: common policy | ||
103 | * @parent: parent of namespace | ||
104 | * @lock: lock for modifying the object | ||
105 | * @acct: accounting for the namespace | ||
106 | * @unconfined: special unconfined profile for the namespace | ||
107 | * @sub_ns: list of namespaces under the current namespace. | ||
108 | * | ||
109 | * An aa_namespace defines the set profiles that are searched to determine | ||
110 | * which profile to attach to a task. Profiles can not be shared between | ||
111 | * aa_namespaces and profile names within a namespace are guaranteed to be | ||
112 | * unique. When profiles in separate namespaces have the same name they | ||
113 | * are NOT considered to be equivalent. | ||
114 | * | ||
115 | * Namespaces are hierarchical and only namespaces and profiles below the | ||
116 | * current namespace are visible. | ||
117 | * | ||
118 | * Namespace names must be unique and can not contain the characters :/\0 | ||
119 | * | ||
120 | * FIXME TODO: add vserver support of namespaces (can it all be done in | ||
121 | * userspace?) | ||
122 | */ | ||
123 | struct aa_namespace { | ||
124 | struct aa_policy base; | ||
125 | struct aa_namespace *parent; | ||
126 | rwlock_t lock; | ||
127 | struct aa_ns_acct acct; | ||
128 | struct aa_profile *unconfined; | ||
129 | struct list_head sub_ns; | ||
130 | }; | ||
131 | |||
132 | /* struct aa_profile - basic confinement data | ||
133 | * @base - base components of the profile (name, refcount, lists, lock ...) | ||
134 | * @parent: parent of profile | ||
135 | * @ns: namespace the profile is in | ||
136 | * @replacedby: is set to the profile that replaced this profile | ||
137 | * @rename: optional profile name that this profile renamed | ||
138 | * @xmatch: optional extended matching for unconfined executables names | ||
139 | * @xmatch_len: xmatch prefix len, used to determine xmatch priority | ||
140 | * @sid: the unique security id number of this profile | ||
141 | * @audit: the auditing mode of the profile | ||
142 | * @mode: the enforcement mode of the profile | ||
143 | * @flags: flags controlling profile behavior | ||
144 | * @path_flags: flags controlling path generation behavior | ||
145 | * @size: the memory consumed by this profiles rules | ||
146 | * @file: The set of rules governing basic file access and domain transitions | ||
147 | * @caps: capabilities for the profile | ||
148 | * @rlimits: rlimits for the profile | ||
149 | * | ||
150 | * The AppArmor profile contains the basic confinement data. Each profile | ||
151 | * has a name, and exists in a namespace. The @name and @exec_match are | ||
152 | * used to determine profile attachment against unconfined tasks. All other | ||
153 | * attachments are determined by profile X transition rules. | ||
154 | * | ||
155 | * The @replacedby field is write protected by the profile lock. Reads | ||
156 | * are assumed to be atomic, and are done without locking. | ||
157 | * | ||
158 | * Profiles have a hierarchy where hats and children profiles keep | ||
159 | * a reference to their parent. | ||
160 | * | ||
161 | * Profile names can not begin with a : and can not contain the \0 | ||
162 | * character. If a profile name begins with / it will be considered when | ||
163 | * determining profile attachment on "unconfined" tasks. | ||
164 | */ | ||
165 | struct aa_profile { | ||
166 | struct aa_policy base; | ||
167 | struct aa_profile *parent; | ||
168 | |||
169 | struct aa_namespace *ns; | ||
170 | struct aa_profile *replacedby; | ||
171 | const char *rename; | ||
172 | |||
173 | struct aa_dfa *xmatch; | ||
174 | int xmatch_len; | ||
175 | u32 sid; | ||
176 | enum audit_mode audit; | ||
177 | enum profile_mode mode; | ||
178 | u32 flags; | ||
179 | u32 path_flags; | ||
180 | int size; | ||
181 | |||
182 | struct aa_file_rules file; | ||
183 | struct aa_caps caps; | ||
184 | struct aa_rlimit rlimits; | ||
185 | }; | ||
186 | |||
187 | extern struct aa_namespace *root_ns; | ||
188 | extern enum profile_mode aa_g_profile_mode; | ||
189 | |||
190 | void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); | ||
191 | |||
192 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); | ||
193 | const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); | ||
194 | int aa_alloc_root_ns(void); | ||
195 | void aa_free_root_ns(void); | ||
196 | void aa_free_namespace_kref(struct kref *kref); | ||
197 | |||
198 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
199 | const char *name); | ||
200 | |||
201 | static inline struct aa_policy *aa_get_common(struct aa_policy *c) | ||
202 | { | ||
203 | if (c) | ||
204 | kref_get(&c->count); | ||
205 | |||
206 | return c; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * aa_get_namespace - increment references count on @ns | ||
211 | * @ns: namespace to increment reference count of (MAYBE NULL) | ||
212 | * | ||
213 | * Returns: pointer to @ns, if @ns is NULL returns NULL | ||
214 | * Requires: @ns must be held with valid refcount when called | ||
215 | */ | ||
216 | static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) | ||
217 | { | ||
218 | if (ns) | ||
219 | kref_get(&(ns->base.count)); | ||
220 | |||
221 | return ns; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * aa_put_namespace - decrement refcount on @ns | ||
226 | * @ns: namespace to put reference of | ||
227 | * | ||
228 | * Decrement reference count of @ns and if no longer in use free it | ||
229 | */ | ||
230 | static inline void aa_put_namespace(struct aa_namespace *ns) | ||
231 | { | ||
232 | if (ns) | ||
233 | kref_put(&ns->base.count, aa_free_namespace_kref); | ||
234 | } | ||
235 | |||
236 | struct aa_profile *aa_alloc_profile(const char *name); | ||
237 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); | ||
238 | void aa_free_profile_kref(struct kref *kref); | ||
239 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); | ||
240 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); | ||
241 | struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); | ||
242 | |||
243 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); | ||
244 | ssize_t aa_remove_profiles(char *name, size_t size); | ||
245 | |||
246 | #define PROF_ADD 1 | ||
247 | #define PROF_REPLACE 0 | ||
248 | |||
249 | #define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) | ||
250 | |||
251 | /** | ||
252 | * aa_newest_version - find the newest version of @profile | ||
253 | * @profile: the profile to check for newer versions of (NOT NULL) | ||
254 | * | ||
255 | * Returns: newest version of @profile, if @profile is the newest version | ||
256 | * return @profile. | ||
257 | * | ||
258 | * NOTE: the profile returned is not refcounted, The refcount on @profile | ||
259 | * must be held until the caller decides what to do with the returned newest | ||
260 | * version. | ||
261 | */ | ||
262 | static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) | ||
263 | { | ||
264 | while (profile->replacedby) | ||
265 | profile = profile->replacedby; | ||
266 | |||
267 | return profile; | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * aa_get_profile - increment refcount on profile @p | ||
272 | * @p: profile (MAYBE NULL) | ||
273 | * | ||
274 | * Returns: pointer to @p if @p is NULL will return NULL | ||
275 | * Requires: @p must be held with valid refcount when called | ||
276 | */ | ||
277 | static inline struct aa_profile *aa_get_profile(struct aa_profile *p) | ||
278 | { | ||
279 | if (p) | ||
280 | kref_get(&(p->base.count)); | ||
281 | |||
282 | return p; | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * aa_put_profile - decrement refcount on profile @p | ||
287 | * @p: profile (MAYBE NULL) | ||
288 | */ | ||
289 | static inline void aa_put_profile(struct aa_profile *p) | ||
290 | { | ||
291 | if (p) | ||
292 | kref_put(&p->base.count, aa_free_profile_kref); | ||
293 | } | ||
294 | |||
295 | static inline int AUDIT_MODE(struct aa_profile *profile) | ||
296 | { | ||
297 | if (aa_g_audit != AUDIT_NORMAL) | ||
298 | return aa_g_audit; | ||
299 | |||
300 | return profile->audit; | ||
301 | } | ||
302 | |||
303 | bool aa_may_manage_policy(int op); | ||
304 | |||
305 | #endif /* __AA_POLICY_H */ | ||
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h new file mode 100644 index 00000000000..a2dcccac45a --- /dev/null +++ b/security/apparmor/include/policy_unpack.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy loading interface function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __POLICY_INTERFACE_H | ||
16 | #define __POLICY_INTERFACE_H | ||
17 | |||
18 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); | ||
19 | |||
20 | #endif /* __POLICY_INTERFACE_H */ | ||
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h new file mode 100644 index 00000000000..544aa6b766a --- /dev/null +++ b/security/apparmor/include/procattr.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_PROCATTR_H | ||
16 | #define __AA_PROCATTR_H | ||
17 | |||
18 | #define AA_DO_TEST 1 | ||
19 | #define AA_ONEXEC 1 | ||
20 | |||
21 | int aa_getprocattr(struct aa_profile *profile, char **string); | ||
22 | int aa_setprocattr_changehat(char *args, size_t size, int test); | ||
23 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); | ||
24 | int aa_setprocattr_permipc(char *fqname); | ||
25 | |||
26 | #endif /* __AA_PROCATTR_H */ | ||
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h new file mode 100644 index 00000000000..3c88be94649 --- /dev/null +++ b/security/apparmor/include/resource.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor resource limits function definitions. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #ifndef __AA_RESOURCE_H | ||
16 | #define __AA_RESOURCE_H | ||
17 | |||
18 | #include <linux/resource.h> | ||
19 | #include <linux/sched.h> | ||
20 | |||
21 | struct aa_profile; | ||
22 | |||
23 | /* struct aa_rlimit - rlimit settings for the profile | ||
24 | * @mask: which hard limits to set | ||
25 | * @limits: rlimit values that override task limits | ||
26 | * | ||
27 | * AppArmor rlimits are used to set confined task rlimits. Only the | ||
28 | * limits specified in @mask will be controlled by apparmor. | ||
29 | */ | ||
30 | struct aa_rlimit { | ||
31 | unsigned int mask; | ||
32 | struct rlimit limits[RLIM_NLIMITS]; | ||
33 | }; | ||
34 | |||
35 | int aa_map_resource(int resource); | ||
36 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
37 | struct rlimit *new_rlim); | ||
38 | |||
39 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); | ||
40 | |||
41 | static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) | ||
42 | { | ||
43 | /* NOP */ | ||
44 | } | ||
45 | |||
46 | #endif /* __AA_RESOURCE_H */ | ||
diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h new file mode 100644 index 00000000000..020db35c301 --- /dev/null +++ b/security/apparmor/include/sid.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security identifier (sid) definitions | ||
5 | * | ||
6 | * Copyright 2009-2010 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | */ | ||
13 | |||
14 | #ifndef __AA_SID_H | ||
15 | #define __AA_SID_H | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | |||
19 | struct aa_profile; | ||
20 | |||
21 | u32 aa_alloc_sid(void); | ||
22 | void aa_free_sid(u32 sid); | ||
23 | |||
24 | #endif /* __AA_SID_H */ | ||
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c new file mode 100644 index 00000000000..649fad88869 --- /dev/null +++ b/security/apparmor/ipc.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor ipc mediation | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/gfp.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | |||
18 | #include "include/audit.h" | ||
19 | #include "include/capability.h" | ||
20 | #include "include/context.h" | ||
21 | #include "include/policy.h" | ||
22 | |||
23 | /* call back to audit ptrace fields */ | ||
24 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
25 | { | ||
26 | struct common_audit_data *sa = va; | ||
27 | audit_log_format(ab, " target="); | ||
28 | audit_log_untrustedstring(ab, sa->aad.target); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * aa_audit_ptrace - do auditing for ptrace | ||
33 | * @profile: profile being enforced (NOT NULL) | ||
34 | * @target: profile being traced (NOT NULL) | ||
35 | * @error: error condition | ||
36 | * | ||
37 | * Returns: %0 or error code | ||
38 | */ | ||
39 | static int aa_audit_ptrace(struct aa_profile *profile, | ||
40 | struct aa_profile *target, int error) | ||
41 | { | ||
42 | struct common_audit_data sa; | ||
43 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
44 | sa.aad.op = OP_PTRACE; | ||
45 | sa.aad.target = target; | ||
46 | sa.aad.error = error; | ||
47 | |||
48 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, | ||
49 | audit_cb); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * aa_may_ptrace - test if tracer task can trace the tracee | ||
54 | * @tracer_task: task who will do the tracing (NOT NULL) | ||
55 | * @tracer: profile of the task doing the tracing (NOT NULL) | ||
56 | * @tracee: task to be traced | ||
57 | * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
58 | * | ||
59 | * Returns: %0 else error code if permission denied or error | ||
60 | */ | ||
61 | int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, | ||
62 | struct aa_profile *tracee, unsigned int mode) | ||
63 | { | ||
64 | /* TODO: currently only based on capability, not extended ptrace | ||
65 | * rules, | ||
66 | * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
67 | */ | ||
68 | |||
69 | if (unconfined(tracer) || tracer == tracee) | ||
70 | return 0; | ||
71 | /* log this capability request */ | ||
72 | return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * aa_ptrace - do ptrace permission check and auditing | ||
77 | * @tracer: task doing the tracing (NOT NULL) | ||
78 | * @tracee: task being traced (NOT NULL) | ||
79 | * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH | ||
80 | * | ||
81 | * Returns: %0 else error code if permission denied or error | ||
82 | */ | ||
83 | int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, | ||
84 | unsigned int mode) | ||
85 | { | ||
86 | /* | ||
87 | * tracer can ptrace tracee when | ||
88 | * - tracer is unconfined || | ||
89 | * - tracer is in complain mode | ||
90 | * - tracer has rules allowing it to trace tracee currently this is: | ||
91 | * - confined by the same profile || | ||
92 | * - tracer profile has CAP_SYS_PTRACE | ||
93 | */ | ||
94 | |||
95 | struct aa_profile *tracer_p; | ||
96 | /* cred released below */ | ||
97 | const struct cred *cred = get_task_cred(tracer); | ||
98 | int error = 0; | ||
99 | tracer_p = aa_cred_profile(cred); | ||
100 | |||
101 | if (!unconfined(tracer_p)) { | ||
102 | /* lcred released below */ | ||
103 | const struct cred *lcred = get_task_cred(tracee); | ||
104 | struct aa_profile *tracee_p = aa_cred_profile(lcred); | ||
105 | |||
106 | error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); | ||
107 | error = aa_audit_ptrace(tracer_p, tracee_p, error); | ||
108 | |||
109 | put_cred(lcred); | ||
110 | } | ||
111 | put_cred(cred); | ||
112 | |||
113 | return error; | ||
114 | } | ||
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c new file mode 100644 index 00000000000..6e85cdb4303 --- /dev/null +++ b/security/apparmor/lib.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains basic common functions used in AppArmor | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | |||
19 | #include "include/audit.h" | ||
20 | |||
21 | |||
22 | /** | ||
23 | * aa_split_fqname - split a fqname into a profile and namespace name | ||
24 | * @fqname: a full qualified name in namespace profile format (NOT NULL) | ||
25 | * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) | ||
26 | * | ||
27 | * Returns: profile name or NULL if one is not specified | ||
28 | * | ||
29 | * Split a namespace name from a profile name (see policy.c for naming | ||
30 | * description). If a portion of the name is missing it returns NULL for | ||
31 | * that portion. | ||
32 | * | ||
33 | * NOTE: may modify the @fqname string. The pointers returned point | ||
34 | * into the @fqname string. | ||
35 | */ | ||
36 | char *aa_split_fqname(char *fqname, char **ns_name) | ||
37 | { | ||
38 | char *name = strim(fqname); | ||
39 | |||
40 | *ns_name = NULL; | ||
41 | if (name[0] == ':') { | ||
42 | char *split = strchr(&name[1], ':'); | ||
43 | if (split) { | ||
44 | /* overwrite ':' with \0 */ | ||
45 | *split = 0; | ||
46 | name = skip_spaces(split + 1); | ||
47 | } else | ||
48 | /* a ns name without a following profile is allowed */ | ||
49 | name = NULL; | ||
50 | *ns_name = &name[1]; | ||
51 | } | ||
52 | if (name && *name == 0) | ||
53 | name = NULL; | ||
54 | |||
55 | return name; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_info_message - log a none profile related status message | ||
60 | * @str: message to log | ||
61 | */ | ||
62 | void aa_info_message(const char *str) | ||
63 | { | ||
64 | if (audit_enabled) { | ||
65 | struct common_audit_data sa; | ||
66 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
67 | sa.aad.info = str; | ||
68 | aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); | ||
69 | } | ||
70 | printk(KERN_INFO "AppArmor: %s\n", str); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc | ||
75 | * @size: size of allocation | ||
76 | * | ||
77 | * Return: allocated buffer or NULL if failed | ||
78 | * | ||
79 | * It is possible that policy being loaded from the user is larger than | ||
80 | * what can be allocated by kmalloc, in those cases fall back to vmalloc. | ||
81 | */ | ||
82 | void *kvmalloc(size_t size) | ||
83 | { | ||
84 | void *buffer = NULL; | ||
85 | |||
86 | if (size == 0) | ||
87 | return NULL; | ||
88 | |||
89 | /* do not attempt kmalloc if we need more than 16 pages at once */ | ||
90 | if (size <= (16*PAGE_SIZE)) | ||
91 | buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); | ||
92 | if (!buffer) { | ||
93 | /* see kvfree for why size must be at least work_struct size | ||
94 | * when allocated via vmalloc | ||
95 | */ | ||
96 | if (size < sizeof(struct work_struct)) | ||
97 | size = sizeof(struct work_struct); | ||
98 | buffer = vmalloc(size); | ||
99 | } | ||
100 | return buffer; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * do_vfree - workqueue routine for freeing vmalloced memory | ||
105 | * @work: data to be freed | ||
106 | * | ||
107 | * The work_struct is overlaid to the data being freed, as at the point | ||
108 | * the work is scheduled the data is no longer valid, be its freeing | ||
109 | * needs to be delayed until safe. | ||
110 | */ | ||
111 | static void do_vfree(struct work_struct *work) | ||
112 | { | ||
113 | vfree(work); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * kvfree - free an allocation do by kvmalloc | ||
118 | * @buffer: buffer to free (MAYBE_NULL) | ||
119 | * | ||
120 | * Free a buffer allocated by kvmalloc | ||
121 | */ | ||
122 | void kvfree(void *buffer) | ||
123 | { | ||
124 | if (is_vmalloc_addr(buffer)) { | ||
125 | /* Data is no longer valid so just use the allocated space | ||
126 | * as the work_struct | ||
127 | */ | ||
128 | struct work_struct *work = (struct work_struct *) buffer; | ||
129 | INIT_WORK(work, do_vfree); | ||
130 | schedule_work(work); | ||
131 | } else | ||
132 | kfree(buffer); | ||
133 | } | ||
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c new file mode 100644 index 00000000000..8db33a8b50c --- /dev/null +++ b/security/apparmor/lsm.c | |||
@@ -0,0 +1,938 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor LSM hooks. | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/security.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/mman.h> | ||
19 | #include <linux/mount.h> | ||
20 | #include <linux/namei.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/ctype.h> | ||
23 | #include <linux/sysctl.h> | ||
24 | #include <linux/audit.h> | ||
25 | #include <net/sock.h> | ||
26 | |||
27 | #include "include/apparmor.h" | ||
28 | #include "include/apparmorfs.h" | ||
29 | #include "include/audit.h" | ||
30 | #include "include/capability.h" | ||
31 | #include "include/context.h" | ||
32 | #include "include/file.h" | ||
33 | #include "include/ipc.h" | ||
34 | #include "include/path.h" | ||
35 | #include "include/policy.h" | ||
36 | #include "include/procattr.h" | ||
37 | |||
38 | /* Flag indicating whether initialization completed */ | ||
39 | int apparmor_initialized __initdata; | ||
40 | |||
41 | /* | ||
42 | * LSM hook functions | ||
43 | */ | ||
44 | |||
45 | /* | ||
46 | * free the associated aa_task_cxt and put its profiles | ||
47 | */ | ||
48 | static void apparmor_cred_free(struct cred *cred) | ||
49 | { | ||
50 | aa_free_task_context(cred->security); | ||
51 | cred->security = NULL; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * allocate the apparmor part of blank credentials | ||
56 | */ | ||
57 | static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) | ||
58 | { | ||
59 | /* freed by apparmor_cred_free */ | ||
60 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
61 | if (!cxt) | ||
62 | return -ENOMEM; | ||
63 | |||
64 | cred->security = cxt; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * prepare new aa_task_cxt for modification by prepare_cred block | ||
70 | */ | ||
71 | static int apparmor_cred_prepare(struct cred *new, const struct cred *old, | ||
72 | gfp_t gfp) | ||
73 | { | ||
74 | /* freed by apparmor_cred_free */ | ||
75 | struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); | ||
76 | if (!cxt) | ||
77 | return -ENOMEM; | ||
78 | |||
79 | aa_dup_task_context(cxt, old->security); | ||
80 | new->security = cxt; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * transfer the apparmor data to a blank set of creds | ||
86 | */ | ||
87 | static void apparmor_cred_transfer(struct cred *new, const struct cred *old) | ||
88 | { | ||
89 | const struct aa_task_cxt *old_cxt = old->security; | ||
90 | struct aa_task_cxt *new_cxt = new->security; | ||
91 | |||
92 | aa_dup_task_context(new_cxt, old_cxt); | ||
93 | } | ||
94 | |||
95 | static int apparmor_ptrace_access_check(struct task_struct *child, | ||
96 | unsigned int mode) | ||
97 | { | ||
98 | int error = cap_ptrace_access_check(child, mode); | ||
99 | if (error) | ||
100 | return error; | ||
101 | |||
102 | return aa_ptrace(current, child, mode); | ||
103 | } | ||
104 | |||
105 | static int apparmor_ptrace_traceme(struct task_struct *parent) | ||
106 | { | ||
107 | int error = cap_ptrace_traceme(parent); | ||
108 | if (error) | ||
109 | return error; | ||
110 | |||
111 | return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); | ||
112 | } | ||
113 | |||
114 | /* Derived from security/commoncap.c:cap_capget */ | ||
115 | static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, | ||
116 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
117 | { | ||
118 | struct aa_profile *profile; | ||
119 | const struct cred *cred; | ||
120 | |||
121 | rcu_read_lock(); | ||
122 | cred = __task_cred(target); | ||
123 | profile = aa_cred_profile(cred); | ||
124 | |||
125 | *effective = cred->cap_effective; | ||
126 | *inheritable = cred->cap_inheritable; | ||
127 | *permitted = cred->cap_permitted; | ||
128 | |||
129 | if (!unconfined(profile)) { | ||
130 | *effective = cap_intersect(*effective, profile->caps.allow); | ||
131 | *permitted = cap_intersect(*permitted, profile->caps.allow); | ||
132 | } | ||
133 | rcu_read_unlock(); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int apparmor_capable(struct task_struct *task, const struct cred *cred, | ||
139 | int cap, int audit) | ||
140 | { | ||
141 | struct aa_profile *profile; | ||
142 | /* cap_capable returns 0 on success, else -EPERM */ | ||
143 | int error = cap_capable(task, cred, cap, audit); | ||
144 | if (!error) { | ||
145 | profile = aa_cred_profile(cred); | ||
146 | if (!unconfined(profile)) | ||
147 | error = aa_capable(task, profile, cap, audit); | ||
148 | } | ||
149 | return error; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * common_perm - basic common permission check wrapper fn for paths | ||
154 | * @op: operation being checked | ||
155 | * @path: path to check permission of (NOT NULL) | ||
156 | * @mask: requested permissions mask | ||
157 | * @cond: conditional info for the permission request (NOT NULL) | ||
158 | * | ||
159 | * Returns: %0 else error code if error or permission denied | ||
160 | */ | ||
161 | static int common_perm(int op, struct path *path, u32 mask, | ||
162 | struct path_cond *cond) | ||
163 | { | ||
164 | struct aa_profile *profile; | ||
165 | int error = 0; | ||
166 | |||
167 | profile = __aa_current_profile(); | ||
168 | if (!unconfined(profile)) | ||
169 | error = aa_path_perm(op, profile, path, 0, mask, cond); | ||
170 | |||
171 | return error; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * common_perm_dir_dentry - common permission wrapper when path is dir, dentry | ||
176 | * @op: operation being checked | ||
177 | * @dir: directory of the dentry (NOT NULL) | ||
178 | * @dentry: dentry to check (NOT NULL) | ||
179 | * @mask: requested permissions mask | ||
180 | * @cond: conditional info for the permission request (NOT NULL) | ||
181 | * | ||
182 | * Returns: %0 else error code if error or permission denied | ||
183 | */ | ||
184 | static int common_perm_dir_dentry(int op, struct path *dir, | ||
185 | struct dentry *dentry, u32 mask, | ||
186 | struct path_cond *cond) | ||
187 | { | ||
188 | struct path path = { dir->mnt, dentry }; | ||
189 | |||
190 | return common_perm(op, &path, mask, cond); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * common_perm_mnt_dentry - common permission wrapper when mnt, dentry | ||
195 | * @op: operation being checked | ||
196 | * @mnt: mount point of dentry (NOT NULL) | ||
197 | * @dentry: dentry to check (NOT NULL) | ||
198 | * @mask: requested permissions mask | ||
199 | * | ||
200 | * Returns: %0 else error code if error or permission denied | ||
201 | */ | ||
202 | static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, | ||
203 | struct dentry *dentry, u32 mask) | ||
204 | { | ||
205 | struct path path = { mnt, dentry }; | ||
206 | struct path_cond cond = { dentry->d_inode->i_uid, | ||
207 | dentry->d_inode->i_mode | ||
208 | }; | ||
209 | |||
210 | return common_perm(op, &path, mask, &cond); | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * common_perm_rm - common permission wrapper for operations doing rm | ||
215 | * @op: operation being checked | ||
216 | * @dir: directory that the dentry is in (NOT NULL) | ||
217 | * @dentry: dentry being rm'd (NOT NULL) | ||
218 | * @mask: requested permission mask | ||
219 | * | ||
220 | * Returns: %0 else error code if error or permission denied | ||
221 | */ | ||
222 | static int common_perm_rm(int op, struct path *dir, | ||
223 | struct dentry *dentry, u32 mask) | ||
224 | { | ||
225 | struct inode *inode = dentry->d_inode; | ||
226 | struct path_cond cond = { }; | ||
227 | |||
228 | if (!inode || !dir->mnt || !mediated_filesystem(inode)) | ||
229 | return 0; | ||
230 | |||
231 | cond.uid = inode->i_uid; | ||
232 | cond.mode = inode->i_mode; | ||
233 | |||
234 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * common_perm_create - common permission wrapper for operations doing create | ||
239 | * @op: operation being checked | ||
240 | * @dir: directory that dentry will be created in (NOT NULL) | ||
241 | * @dentry: dentry to create (NOT NULL) | ||
242 | * @mask: request permission mask | ||
243 | * @mode: created file mode | ||
244 | * | ||
245 | * Returns: %0 else error code if error or permission denied | ||
246 | */ | ||
247 | static int common_perm_create(int op, struct path *dir, struct dentry *dentry, | ||
248 | u32 mask, umode_t mode) | ||
249 | { | ||
250 | struct path_cond cond = { current_fsuid(), mode }; | ||
251 | |||
252 | if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) | ||
253 | return 0; | ||
254 | |||
255 | return common_perm_dir_dentry(op, dir, dentry, mask, &cond); | ||
256 | } | ||
257 | |||
258 | static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) | ||
259 | { | ||
260 | return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); | ||
261 | } | ||
262 | |||
263 | static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, | ||
264 | int mode) | ||
265 | { | ||
266 | return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, | ||
267 | S_IFDIR); | ||
268 | } | ||
269 | |||
270 | static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) | ||
271 | { | ||
272 | return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); | ||
273 | } | ||
274 | |||
275 | static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, | ||
276 | int mode, unsigned int dev) | ||
277 | { | ||
278 | return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); | ||
279 | } | ||
280 | |||
281 | static int apparmor_path_truncate(struct path *path) | ||
282 | { | ||
283 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
284 | path->dentry->d_inode->i_mode | ||
285 | }; | ||
286 | |||
287 | if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) | ||
288 | return 0; | ||
289 | |||
290 | return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, | ||
291 | &cond); | ||
292 | } | ||
293 | |||
294 | static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, | ||
295 | const char *old_name) | ||
296 | { | ||
297 | return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, | ||
298 | S_IFLNK); | ||
299 | } | ||
300 | |||
301 | static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
302 | struct dentry *new_dentry) | ||
303 | { | ||
304 | struct aa_profile *profile; | ||
305 | int error = 0; | ||
306 | |||
307 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
308 | return 0; | ||
309 | |||
310 | profile = aa_current_profile(); | ||
311 | if (!unconfined(profile)) | ||
312 | error = aa_path_link(profile, old_dentry, new_dir, new_dentry); | ||
313 | return error; | ||
314 | } | ||
315 | |||
316 | static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
317 | struct path *new_dir, struct dentry *new_dentry) | ||
318 | { | ||
319 | struct aa_profile *profile; | ||
320 | int error = 0; | ||
321 | |||
322 | if (!mediated_filesystem(old_dentry->d_inode)) | ||
323 | return 0; | ||
324 | |||
325 | profile = aa_current_profile(); | ||
326 | if (!unconfined(profile)) { | ||
327 | struct path old_path = { old_dir->mnt, old_dentry }; | ||
328 | struct path new_path = { new_dir->mnt, new_dentry }; | ||
329 | struct path_cond cond = { old_dentry->d_inode->i_uid, | ||
330 | old_dentry->d_inode->i_mode | ||
331 | }; | ||
332 | |||
333 | error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0, | ||
334 | MAY_READ | AA_MAY_META_READ | MAY_WRITE | | ||
335 | AA_MAY_META_WRITE | AA_MAY_DELETE, | ||
336 | &cond); | ||
337 | if (!error) | ||
338 | error = aa_path_perm(OP_RENAME_DEST, profile, &new_path, | ||
339 | 0, MAY_WRITE | AA_MAY_META_WRITE | | ||
340 | AA_MAY_CREATE, &cond); | ||
341 | |||
342 | } | ||
343 | return error; | ||
344 | } | ||
345 | |||
346 | static int apparmor_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | ||
347 | mode_t mode) | ||
348 | { | ||
349 | if (!mediated_filesystem(dentry->d_inode)) | ||
350 | return 0; | ||
351 | |||
352 | return common_perm_mnt_dentry(OP_CHMOD, mnt, dentry, AA_MAY_CHMOD); | ||
353 | } | ||
354 | |||
355 | static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) | ||
356 | { | ||
357 | struct path_cond cond = { path->dentry->d_inode->i_uid, | ||
358 | path->dentry->d_inode->i_mode | ||
359 | }; | ||
360 | |||
361 | if (!mediated_filesystem(path->dentry->d_inode)) | ||
362 | return 0; | ||
363 | |||
364 | return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); | ||
365 | } | ||
366 | |||
367 | static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | ||
368 | { | ||
369 | if (!mediated_filesystem(dentry->d_inode)) | ||
370 | return 0; | ||
371 | |||
372 | return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry, | ||
373 | AA_MAY_META_READ); | ||
374 | } | ||
375 | |||
376 | static int apparmor_dentry_open(struct file *file, const struct cred *cred) | ||
377 | { | ||
378 | struct aa_file_cxt *fcxt = file->f_security; | ||
379 | struct aa_profile *profile; | ||
380 | int error = 0; | ||
381 | |||
382 | if (!mediated_filesystem(file->f_path.dentry->d_inode)) | ||
383 | return 0; | ||
384 | |||
385 | /* If in exec, permission is handled by bprm hooks. | ||
386 | * Cache permissions granted by the previous exec check, with | ||
387 | * implicit read and executable mmap which are required to | ||
388 | * actually execute the image. | ||
389 | */ | ||
390 | if (current->in_execve) { | ||
391 | fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | profile = aa_cred_profile(cred); | ||
396 | if (!unconfined(profile)) { | ||
397 | struct inode *inode = file->f_path.dentry->d_inode; | ||
398 | struct path_cond cond = { inode->i_uid, inode->i_mode }; | ||
399 | |||
400 | error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, | ||
401 | aa_map_file_to_perms(file), &cond); | ||
402 | /* todo cache full allowed permissions set and state */ | ||
403 | fcxt->allow = aa_map_file_to_perms(file); | ||
404 | } | ||
405 | |||
406 | return error; | ||
407 | } | ||
408 | |||
409 | static int apparmor_file_alloc_security(struct file *file) | ||
410 | { | ||
411 | /* freed by apparmor_file_free_security */ | ||
412 | file->f_security = aa_alloc_file_context(GFP_KERNEL); | ||
413 | if (!file->f_security) | ||
414 | return -ENOMEM; | ||
415 | return 0; | ||
416 | |||
417 | } | ||
418 | |||
419 | static void apparmor_file_free_security(struct file *file) | ||
420 | { | ||
421 | struct aa_file_cxt *cxt = file->f_security; | ||
422 | |||
423 | aa_free_file_context(cxt); | ||
424 | } | ||
425 | |||
426 | static int common_file_perm(int op, struct file *file, u32 mask) | ||
427 | { | ||
428 | struct aa_file_cxt *fcxt = file->f_security; | ||
429 | struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); | ||
430 | int error = 0; | ||
431 | |||
432 | BUG_ON(!fprofile); | ||
433 | |||
434 | if (!file->f_path.mnt || | ||
435 | !mediated_filesystem(file->f_path.dentry->d_inode)) | ||
436 | return 0; | ||
437 | |||
438 | profile = __aa_current_profile(); | ||
439 | |||
440 | /* revalidate access, if task is unconfined, or the cached cred | ||
441 | * doesn't match or if the request is for more permissions than | ||
442 | * was granted. | ||
443 | * | ||
444 | * Note: the test for !unconfined(fprofile) is to handle file | ||
445 | * delegation from unconfined tasks | ||
446 | */ | ||
447 | if (!unconfined(profile) && !unconfined(fprofile) && | ||
448 | ((fprofile != profile) || (mask & ~fcxt->allow))) | ||
449 | error = aa_file_perm(op, profile, file, mask); | ||
450 | |||
451 | return error; | ||
452 | } | ||
453 | |||
454 | static int apparmor_file_permission(struct file *file, int mask) | ||
455 | { | ||
456 | return common_file_perm(OP_FPERM, file, mask); | ||
457 | } | ||
458 | |||
459 | static int apparmor_file_lock(struct file *file, unsigned int cmd) | ||
460 | { | ||
461 | u32 mask = AA_MAY_LOCK; | ||
462 | |||
463 | if (cmd == F_WRLCK) | ||
464 | mask |= MAY_WRITE; | ||
465 | |||
466 | return common_file_perm(OP_FLOCK, file, mask); | ||
467 | } | ||
468 | |||
469 | static int common_mmap(int op, struct file *file, unsigned long prot, | ||
470 | unsigned long flags) | ||
471 | { | ||
472 | struct dentry *dentry; | ||
473 | int mask = 0; | ||
474 | |||
475 | if (!file || !file->f_security) | ||
476 | return 0; | ||
477 | |||
478 | if (prot & PROT_READ) | ||
479 | mask |= MAY_READ; | ||
480 | /* | ||
481 | * Private mappings don't require write perms since they don't | ||
482 | * write back to the files | ||
483 | */ | ||
484 | if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) | ||
485 | mask |= MAY_WRITE; | ||
486 | if (prot & PROT_EXEC) | ||
487 | mask |= AA_EXEC_MMAP; | ||
488 | |||
489 | dentry = file->f_path.dentry; | ||
490 | return common_file_perm(op, file, mask); | ||
491 | } | ||
492 | |||
493 | static int apparmor_file_mmap(struct file *file, unsigned long reqprot, | ||
494 | unsigned long prot, unsigned long flags, | ||
495 | unsigned long addr, unsigned long addr_only) | ||
496 | { | ||
497 | int rc = 0; | ||
498 | |||
499 | /* do DAC check */ | ||
500 | rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); | ||
501 | if (rc || addr_only) | ||
502 | return rc; | ||
503 | |||
504 | return common_mmap(OP_FMMAP, file, prot, flags); | ||
505 | } | ||
506 | |||
507 | static int apparmor_file_mprotect(struct vm_area_struct *vma, | ||
508 | unsigned long reqprot, unsigned long prot) | ||
509 | { | ||
510 | return common_mmap(OP_FMPROT, vma->vm_file, prot, | ||
511 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); | ||
512 | } | ||
513 | |||
514 | static int apparmor_getprocattr(struct task_struct *task, char *name, | ||
515 | char **value) | ||
516 | { | ||
517 | int error = -ENOENT; | ||
518 | struct aa_profile *profile; | ||
519 | /* released below */ | ||
520 | const struct cred *cred = get_task_cred(task); | ||
521 | struct aa_task_cxt *cxt = cred->security; | ||
522 | profile = aa_cred_profile(cred); | ||
523 | |||
524 | if (strcmp(name, "current") == 0) | ||
525 | error = aa_getprocattr(aa_newest_version(cxt->profile), | ||
526 | value); | ||
527 | else if (strcmp(name, "prev") == 0 && cxt->previous) | ||
528 | error = aa_getprocattr(aa_newest_version(cxt->previous), | ||
529 | value); | ||
530 | else if (strcmp(name, "exec") == 0 && cxt->onexec) | ||
531 | error = aa_getprocattr(aa_newest_version(cxt->onexec), | ||
532 | value); | ||
533 | else | ||
534 | error = -EINVAL; | ||
535 | |||
536 | put_cred(cred); | ||
537 | |||
538 | return error; | ||
539 | } | ||
540 | |||
541 | static int apparmor_setprocattr(struct task_struct *task, char *name, | ||
542 | void *value, size_t size) | ||
543 | { | ||
544 | char *command, *args = value; | ||
545 | size_t arg_size; | ||
546 | int error; | ||
547 | |||
548 | if (size == 0) | ||
549 | return -EINVAL; | ||
550 | /* args points to a PAGE_SIZE buffer, AppArmor requires that | ||
551 | * the buffer must be null terminated or have size <= PAGE_SIZE -1 | ||
552 | * so that AppArmor can null terminate them | ||
553 | */ | ||
554 | if (args[size - 1] != '\0') { | ||
555 | if (size == PAGE_SIZE) | ||
556 | return -EINVAL; | ||
557 | args[size] = '\0'; | ||
558 | } | ||
559 | |||
560 | /* task can only write its own attributes */ | ||
561 | if (current != task) | ||
562 | return -EACCES; | ||
563 | |||
564 | args = value; | ||
565 | args = strim(args); | ||
566 | command = strsep(&args, " "); | ||
567 | if (!args) | ||
568 | return -EINVAL; | ||
569 | args = skip_spaces(args); | ||
570 | if (!*args) | ||
571 | return -EINVAL; | ||
572 | |||
573 | arg_size = size - (args - (char *) value); | ||
574 | if (strcmp(name, "current") == 0) { | ||
575 | if (strcmp(command, "changehat") == 0) { | ||
576 | error = aa_setprocattr_changehat(args, arg_size, | ||
577 | !AA_DO_TEST); | ||
578 | } else if (strcmp(command, "permhat") == 0) { | ||
579 | error = aa_setprocattr_changehat(args, arg_size, | ||
580 | AA_DO_TEST); | ||
581 | } else if (strcmp(command, "changeprofile") == 0) { | ||
582 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
583 | !AA_DO_TEST); | ||
584 | } else if (strcmp(command, "permprofile") == 0) { | ||
585 | error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, | ||
586 | AA_DO_TEST); | ||
587 | } else if (strcmp(command, "permipc") == 0) { | ||
588 | error = aa_setprocattr_permipc(args); | ||
589 | } else { | ||
590 | struct common_audit_data sa; | ||
591 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
592 | sa.aad.op = OP_SETPROCATTR; | ||
593 | sa.aad.info = name; | ||
594 | sa.aad.error = -EINVAL; | ||
595 | return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, | ||
596 | &sa, NULL); | ||
597 | } | ||
598 | } else if (strcmp(name, "exec") == 0) { | ||
599 | error = aa_setprocattr_changeprofile(args, AA_ONEXEC, | ||
600 | !AA_DO_TEST); | ||
601 | } else { | ||
602 | /* only support the "current" and "exec" process attributes */ | ||
603 | return -EINVAL; | ||
604 | } | ||
605 | if (!error) | ||
606 | error = size; | ||
607 | return error; | ||
608 | } | ||
609 | |||
610 | static int apparmor_task_setrlimit(unsigned int resource, | ||
611 | struct rlimit *new_rlim) | ||
612 | { | ||
613 | struct aa_profile *profile = aa_current_profile(); | ||
614 | int error = 0; | ||
615 | |||
616 | if (!unconfined(profile)) | ||
617 | error = aa_task_setrlimit(profile, resource, new_rlim); | ||
618 | |||
619 | return error; | ||
620 | } | ||
621 | |||
622 | static struct security_operations apparmor_ops = { | ||
623 | .name = "apparmor", | ||
624 | |||
625 | .ptrace_access_check = apparmor_ptrace_access_check, | ||
626 | .ptrace_traceme = apparmor_ptrace_traceme, | ||
627 | .capget = apparmor_capget, | ||
628 | .capable = apparmor_capable, | ||
629 | |||
630 | .path_link = apparmor_path_link, | ||
631 | .path_unlink = apparmor_path_unlink, | ||
632 | .path_symlink = apparmor_path_symlink, | ||
633 | .path_mkdir = apparmor_path_mkdir, | ||
634 | .path_rmdir = apparmor_path_rmdir, | ||
635 | .path_mknod = apparmor_path_mknod, | ||
636 | .path_rename = apparmor_path_rename, | ||
637 | .path_chmod = apparmor_path_chmod, | ||
638 | .path_chown = apparmor_path_chown, | ||
639 | .path_truncate = apparmor_path_truncate, | ||
640 | .dentry_open = apparmor_dentry_open, | ||
641 | .inode_getattr = apparmor_inode_getattr, | ||
642 | |||
643 | .file_permission = apparmor_file_permission, | ||
644 | .file_alloc_security = apparmor_file_alloc_security, | ||
645 | .file_free_security = apparmor_file_free_security, | ||
646 | .file_mmap = apparmor_file_mmap, | ||
647 | .file_mprotect = apparmor_file_mprotect, | ||
648 | .file_lock = apparmor_file_lock, | ||
649 | |||
650 | .getprocattr = apparmor_getprocattr, | ||
651 | .setprocattr = apparmor_setprocattr, | ||
652 | |||
653 | .cred_alloc_blank = apparmor_cred_alloc_blank, | ||
654 | .cred_free = apparmor_cred_free, | ||
655 | .cred_prepare = apparmor_cred_prepare, | ||
656 | .cred_transfer = apparmor_cred_transfer, | ||
657 | |||
658 | .bprm_set_creds = apparmor_bprm_set_creds, | ||
659 | .bprm_committing_creds = apparmor_bprm_committing_creds, | ||
660 | .bprm_committed_creds = apparmor_bprm_committed_creds, | ||
661 | .bprm_secureexec = apparmor_bprm_secureexec, | ||
662 | |||
663 | .task_setrlimit = apparmor_task_setrlimit, | ||
664 | }; | ||
665 | |||
666 | /* | ||
667 | * AppArmor sysfs module parameters | ||
668 | */ | ||
669 | |||
670 | static int param_set_aabool(const char *val, struct kernel_param *kp); | ||
671 | static int param_get_aabool(char *buffer, struct kernel_param *kp); | ||
672 | #define param_check_aabool(name, p) __param_check(name, p, int) | ||
673 | |||
674 | static int param_set_aauint(const char *val, struct kernel_param *kp); | ||
675 | static int param_get_aauint(char *buffer, struct kernel_param *kp); | ||
676 | #define param_check_aauint(name, p) __param_check(name, p, int) | ||
677 | |||
678 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); | ||
679 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); | ||
680 | #define param_check_aalockpolicy(name, p) __param_check(name, p, int) | ||
681 | |||
682 | static int param_set_audit(const char *val, struct kernel_param *kp); | ||
683 | static int param_get_audit(char *buffer, struct kernel_param *kp); | ||
684 | #define param_check_audit(name, p) __param_check(name, p, int) | ||
685 | |||
686 | static int param_set_mode(const char *val, struct kernel_param *kp); | ||
687 | static int param_get_mode(char *buffer, struct kernel_param *kp); | ||
688 | #define param_check_mode(name, p) __param_check(name, p, int) | ||
689 | |||
690 | /* Flag values, also controllable via /sys/module/apparmor/parameters | ||
691 | * We define special types as we want to do additional mediation. | ||
692 | */ | ||
693 | |||
694 | /* AppArmor global enforcement switch - complain, enforce, kill */ | ||
695 | enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; | ||
696 | module_param_call(mode, param_set_mode, param_get_mode, | ||
697 | &aa_g_profile_mode, S_IRUSR | S_IWUSR); | ||
698 | |||
699 | /* Debug mode */ | ||
700 | int aa_g_debug; | ||
701 | module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); | ||
702 | |||
703 | /* Audit mode */ | ||
704 | enum audit_mode aa_g_audit; | ||
705 | module_param_call(audit, param_set_audit, param_get_audit, | ||
706 | &aa_g_audit, S_IRUSR | S_IWUSR); | ||
707 | |||
708 | /* Determines if audit header is included in audited messages. This | ||
709 | * provides more context if the audit daemon is not running | ||
710 | */ | ||
711 | int aa_g_audit_header = 1; | ||
712 | module_param_named(audit_header, aa_g_audit_header, aabool, | ||
713 | S_IRUSR | S_IWUSR); | ||
714 | |||
715 | /* lock out loading/removal of policy | ||
716 | * TODO: add in at boot loading of policy, which is the only way to | ||
717 | * load policy, if lock_policy is set | ||
718 | */ | ||
719 | int aa_g_lock_policy; | ||
720 | module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy, | ||
721 | S_IRUSR | S_IWUSR); | ||
722 | |||
723 | /* Syscall logging mode */ | ||
724 | int aa_g_logsyscall; | ||
725 | module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); | ||
726 | |||
727 | /* Maximum pathname length before accesses will start getting rejected */ | ||
728 | unsigned int aa_g_path_max = 2 * PATH_MAX; | ||
729 | module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); | ||
730 | |||
731 | /* Determines how paranoid loading of policy is and how much verification | ||
732 | * on the loaded policy is done. | ||
733 | */ | ||
734 | int aa_g_paranoid_load = 1; | ||
735 | module_param_named(paranoid_load, aa_g_paranoid_load, aabool, | ||
736 | S_IRUSR | S_IWUSR); | ||
737 | |||
738 | /* Boot time disable flag */ | ||
739 | static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; | ||
740 | module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); | ||
741 | |||
742 | static int __init apparmor_enabled_setup(char *str) | ||
743 | { | ||
744 | unsigned long enabled; | ||
745 | int error = strict_strtoul(str, 0, &enabled); | ||
746 | if (!error) | ||
747 | apparmor_enabled = enabled ? 1 : 0; | ||
748 | return 1; | ||
749 | } | ||
750 | |||
751 | __setup("apparmor=", apparmor_enabled_setup); | ||
752 | |||
753 | /* set global flag turning off the ability to load policy */ | ||
754 | static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) | ||
755 | { | ||
756 | if (!capable(CAP_MAC_ADMIN)) | ||
757 | return -EPERM; | ||
758 | if (aa_g_lock_policy) | ||
759 | return -EACCES; | ||
760 | return param_set_bool(val, kp); | ||
761 | } | ||
762 | |||
763 | static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) | ||
764 | { | ||
765 | if (!capable(CAP_MAC_ADMIN)) | ||
766 | return -EPERM; | ||
767 | return param_get_bool(buffer, kp); | ||
768 | } | ||
769 | |||
770 | static int param_set_aabool(const char *val, struct kernel_param *kp) | ||
771 | { | ||
772 | if (!capable(CAP_MAC_ADMIN)) | ||
773 | return -EPERM; | ||
774 | return param_set_bool(val, kp); | ||
775 | } | ||
776 | |||
777 | static int param_get_aabool(char *buffer, struct kernel_param *kp) | ||
778 | { | ||
779 | if (!capable(CAP_MAC_ADMIN)) | ||
780 | return -EPERM; | ||
781 | return param_get_bool(buffer, kp); | ||
782 | } | ||
783 | |||
784 | static int param_set_aauint(const char *val, struct kernel_param *kp) | ||
785 | { | ||
786 | if (!capable(CAP_MAC_ADMIN)) | ||
787 | return -EPERM; | ||
788 | return param_set_uint(val, kp); | ||
789 | } | ||
790 | |||
791 | static int param_get_aauint(char *buffer, struct kernel_param *kp) | ||
792 | { | ||
793 | if (!capable(CAP_MAC_ADMIN)) | ||
794 | return -EPERM; | ||
795 | return param_get_uint(buffer, kp); | ||
796 | } | ||
797 | |||
798 | static int param_get_audit(char *buffer, struct kernel_param *kp) | ||
799 | { | ||
800 | if (!capable(CAP_MAC_ADMIN)) | ||
801 | return -EPERM; | ||
802 | |||
803 | if (!apparmor_enabled) | ||
804 | return -EINVAL; | ||
805 | |||
806 | return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); | ||
807 | } | ||
808 | |||
809 | static int param_set_audit(const char *val, struct kernel_param *kp) | ||
810 | { | ||
811 | int i; | ||
812 | if (!capable(CAP_MAC_ADMIN)) | ||
813 | return -EPERM; | ||
814 | |||
815 | if (!apparmor_enabled) | ||
816 | return -EINVAL; | ||
817 | |||
818 | if (!val) | ||
819 | return -EINVAL; | ||
820 | |||
821 | for (i = 0; i < AUDIT_MAX_INDEX; i++) { | ||
822 | if (strcmp(val, audit_mode_names[i]) == 0) { | ||
823 | aa_g_audit = i; | ||
824 | return 0; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | return -EINVAL; | ||
829 | } | ||
830 | |||
831 | static int param_get_mode(char *buffer, struct kernel_param *kp) | ||
832 | { | ||
833 | if (!capable(CAP_MAC_ADMIN)) | ||
834 | return -EPERM; | ||
835 | |||
836 | if (!apparmor_enabled) | ||
837 | return -EINVAL; | ||
838 | |||
839 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | ||
840 | } | ||
841 | |||
842 | static int param_set_mode(const char *val, struct kernel_param *kp) | ||
843 | { | ||
844 | int i; | ||
845 | if (!capable(CAP_MAC_ADMIN)) | ||
846 | return -EPERM; | ||
847 | |||
848 | if (!apparmor_enabled) | ||
849 | return -EINVAL; | ||
850 | |||
851 | if (!val) | ||
852 | return -EINVAL; | ||
853 | |||
854 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | ||
855 | if (strcmp(val, profile_mode_names[i]) == 0) { | ||
856 | aa_g_profile_mode = i; | ||
857 | return 0; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | return -EINVAL; | ||
862 | } | ||
863 | |||
864 | /* | ||
865 | * AppArmor init functions | ||
866 | */ | ||
867 | |||
868 | /** | ||
869 | * set_init_cxt - set a task context and profile on the first task. | ||
870 | * | ||
871 | * TODO: allow setting an alternate profile than unconfined | ||
872 | */ | ||
873 | static int __init set_init_cxt(void) | ||
874 | { | ||
875 | struct cred *cred = (struct cred *)current->real_cred; | ||
876 | struct aa_task_cxt *cxt; | ||
877 | |||
878 | cxt = aa_alloc_task_context(GFP_KERNEL); | ||
879 | if (!cxt) | ||
880 | return -ENOMEM; | ||
881 | |||
882 | cxt->profile = aa_get_profile(root_ns->unconfined); | ||
883 | cred->security = cxt; | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int __init apparmor_init(void) | ||
889 | { | ||
890 | int error; | ||
891 | |||
892 | if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { | ||
893 | aa_info_message("AppArmor disabled by boot time parameter"); | ||
894 | apparmor_enabled = 0; | ||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | error = aa_alloc_root_ns(); | ||
899 | if (error) { | ||
900 | AA_ERROR("Unable to allocate default profile namespace\n"); | ||
901 | goto alloc_out; | ||
902 | } | ||
903 | |||
904 | error = set_init_cxt(); | ||
905 | if (error) { | ||
906 | AA_ERROR("Failed to set context on init task\n"); | ||
907 | goto register_security_out; | ||
908 | } | ||
909 | |||
910 | error = register_security(&apparmor_ops); | ||
911 | if (error) { | ||
912 | AA_ERROR("Unable to register AppArmor\n"); | ||
913 | goto register_security_out; | ||
914 | } | ||
915 | |||
916 | /* Report that AppArmor successfully initialized */ | ||
917 | apparmor_initialized = 1; | ||
918 | if (aa_g_profile_mode == APPARMOR_COMPLAIN) | ||
919 | aa_info_message("AppArmor initialized: complain mode enabled"); | ||
920 | else if (aa_g_profile_mode == APPARMOR_KILL) | ||
921 | aa_info_message("AppArmor initialized: kill mode enabled"); | ||
922 | else | ||
923 | aa_info_message("AppArmor initialized"); | ||
924 | |||
925 | return error; | ||
926 | |||
927 | register_security_out: | ||
928 | aa_free_root_ns(); | ||
929 | |||
930 | alloc_out: | ||
931 | aa_destroy_aafs(); | ||
932 | |||
933 | apparmor_enabled = 0; | ||
934 | return error; | ||
935 | |||
936 | } | ||
937 | |||
938 | security_initcall(apparmor_init); | ||
diff --git a/security/apparmor/match.c b/security/apparmor/match.c new file mode 100644 index 00000000000..5cb4dc1f699 --- /dev/null +++ b/security/apparmor/match.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor dfa based regular expression matching engine | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/errno.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/err.h> | ||
21 | #include <linux/kref.h> | ||
22 | |||
23 | #include "include/apparmor.h" | ||
24 | #include "include/match.h" | ||
25 | |||
26 | /** | ||
27 | * unpack_table - unpack a dfa table (one of accept, default, base, next check) | ||
28 | * @blob: data to unpack (NOT NULL) | ||
29 | * @bsize: size of blob | ||
30 | * | ||
31 | * Returns: pointer to table else NULL on failure | ||
32 | * | ||
33 | * NOTE: must be freed by kvfree (not kmalloc) | ||
34 | */ | ||
35 | static struct table_header *unpack_table(char *blob, size_t bsize) | ||
36 | { | ||
37 | struct table_header *table = NULL; | ||
38 | struct table_header th; | ||
39 | size_t tsize; | ||
40 | |||
41 | if (bsize < sizeof(struct table_header)) | ||
42 | goto out; | ||
43 | |||
44 | /* loaded td_id's start at 1, subtract 1 now to avoid doing | ||
45 | * it every time we use td_id as an index | ||
46 | */ | ||
47 | th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | ||
48 | th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); | ||
49 | th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | ||
50 | blob += sizeof(struct table_header); | ||
51 | |||
52 | if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || | ||
53 | th.td_flags == YYTD_DATA8)) | ||
54 | goto out; | ||
55 | |||
56 | tsize = table_size(th.td_lolen, th.td_flags); | ||
57 | if (bsize < tsize) | ||
58 | goto out; | ||
59 | |||
60 | table = kvmalloc(tsize); | ||
61 | if (table) { | ||
62 | *table = th; | ||
63 | if (th.td_flags == YYTD_DATA8) | ||
64 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
65 | u8, byte_to_byte); | ||
66 | else if (th.td_flags == YYTD_DATA16) | ||
67 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
68 | u16, be16_to_cpu); | ||
69 | else if (th.td_flags == YYTD_DATA32) | ||
70 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | ||
71 | u32, be32_to_cpu); | ||
72 | else | ||
73 | goto fail; | ||
74 | } | ||
75 | |||
76 | out: | ||
77 | /* if table was vmalloced make sure the page tables are synced | ||
78 | * before it is used, as it goes live to all cpus. | ||
79 | */ | ||
80 | if (is_vmalloc_addr(table)) | ||
81 | vm_unmap_aliases(); | ||
82 | return table; | ||
83 | fail: | ||
84 | kvfree(table); | ||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * verify_dfa - verify that transitions and states in the tables are in bounds. | ||
90 | * @dfa: dfa to test (NOT NULL) | ||
91 | * @flags: flags controlling what type of accept table are acceptable | ||
92 | * | ||
93 | * Assumes dfa has gone through the first pass verification done by unpacking | ||
94 | * NOTE: this does not valid accept table values | ||
95 | * | ||
96 | * Returns: %0 else error code on failure to verify | ||
97 | */ | ||
98 | static int verify_dfa(struct aa_dfa *dfa, int flags) | ||
99 | { | ||
100 | size_t i, state_count, trans_count; | ||
101 | int error = -EPROTO; | ||
102 | |||
103 | /* check that required tables exist */ | ||
104 | if (!(dfa->tables[YYTD_ID_DEF] && | ||
105 | dfa->tables[YYTD_ID_BASE] && | ||
106 | dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) | ||
107 | goto out; | ||
108 | |||
109 | /* accept.size == default.size == base.size */ | ||
110 | state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; | ||
111 | if (ACCEPT1_FLAGS(flags)) { | ||
112 | if (!dfa->tables[YYTD_ID_ACCEPT]) | ||
113 | goto out; | ||
114 | if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) | ||
115 | goto out; | ||
116 | } | ||
117 | if (ACCEPT2_FLAGS(flags)) { | ||
118 | if (!dfa->tables[YYTD_ID_ACCEPT2]) | ||
119 | goto out; | ||
120 | if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) | ||
121 | goto out; | ||
122 | } | ||
123 | if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) | ||
124 | goto out; | ||
125 | |||
126 | /* next.size == chk.size */ | ||
127 | trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; | ||
128 | if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) | ||
129 | goto out; | ||
130 | |||
131 | /* if equivalence classes then its table size must be 256 */ | ||
132 | if (dfa->tables[YYTD_ID_EC] && | ||
133 | dfa->tables[YYTD_ID_EC]->td_lolen != 256) | ||
134 | goto out; | ||
135 | |||
136 | if (flags & DFA_FLAG_VERIFY_STATES) { | ||
137 | for (i = 0; i < state_count; i++) { | ||
138 | if (DEFAULT_TABLE(dfa)[i] >= state_count) | ||
139 | goto out; | ||
140 | /* TODO: do check that DEF state recursion terminates */ | ||
141 | if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { | ||
142 | printk(KERN_ERR "AppArmor DFA next/check upper " | ||
143 | "bounds error\n"); | ||
144 | goto out; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | for (i = 0; i < trans_count; i++) { | ||
149 | if (NEXT_TABLE(dfa)[i] >= state_count) | ||
150 | goto out; | ||
151 | if (CHECK_TABLE(dfa)[i] >= state_count) | ||
152 | goto out; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | error = 0; | ||
157 | out: | ||
158 | return error; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * dfa_free - free a dfa allocated by aa_dfa_unpack | ||
163 | * @dfa: the dfa to free (MAYBE NULL) | ||
164 | * | ||
165 | * Requires: reference count to dfa == 0 | ||
166 | */ | ||
167 | static void dfa_free(struct aa_dfa *dfa) | ||
168 | { | ||
169 | if (dfa) { | ||
170 | int i; | ||
171 | |||
172 | for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { | ||
173 | kvfree(dfa->tables[i]); | ||
174 | dfa->tables[i] = NULL; | ||
175 | } | ||
176 | kfree(dfa); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * aa_dfa_free_kref - free aa_dfa by kref (called by aa_put_dfa) | ||
182 | * @kr: kref callback for freeing of a dfa (NOT NULL) | ||
183 | */ | ||
184 | void aa_dfa_free_kref(struct kref *kref) | ||
185 | { | ||
186 | struct aa_dfa *dfa = container_of(kref, struct aa_dfa, count); | ||
187 | dfa_free(dfa); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * aa_dfa_unpack - unpack the binary tables of a serialized dfa | ||
192 | * @blob: aligned serialized stream of data to unpack (NOT NULL) | ||
193 | * @size: size of data to unpack | ||
194 | * @flags: flags controlling what type of accept tables are acceptable | ||
195 | * | ||
196 | * Unpack a dfa that has been serialized. To find information on the dfa | ||
197 | * format look in Documentation/apparmor.txt | ||
198 | * Assumes the dfa @blob stream has been aligned on a 8 byte boundry | ||
199 | * | ||
200 | * Returns: an unpacked dfa ready for matching or ERR_PTR on failure | ||
201 | */ | ||
202 | struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) | ||
203 | { | ||
204 | int hsize; | ||
205 | int error = -ENOMEM; | ||
206 | char *data = blob; | ||
207 | struct table_header *table = NULL; | ||
208 | struct aa_dfa *dfa = kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); | ||
209 | if (!dfa) | ||
210 | goto fail; | ||
211 | |||
212 | kref_init(&dfa->count); | ||
213 | |||
214 | error = -EPROTO; | ||
215 | |||
216 | /* get dfa table set header */ | ||
217 | if (size < sizeof(struct table_set_header)) | ||
218 | goto fail; | ||
219 | |||
220 | if (ntohl(*(u32 *) data) != YYTH_MAGIC) | ||
221 | goto fail; | ||
222 | |||
223 | hsize = ntohl(*(u32 *) (data + 4)); | ||
224 | if (size < hsize) | ||
225 | goto fail; | ||
226 | |||
227 | dfa->flags = ntohs(*(u16 *) (data + 12)); | ||
228 | data += hsize; | ||
229 | size -= hsize; | ||
230 | |||
231 | while (size > 0) { | ||
232 | table = unpack_table(data, size); | ||
233 | if (!table) | ||
234 | goto fail; | ||
235 | |||
236 | switch (table->td_id) { | ||
237 | case YYTD_ID_ACCEPT: | ||
238 | if (!(table->td_flags & ACCEPT1_FLAGS(flags))) | ||
239 | goto fail; | ||
240 | break; | ||
241 | case YYTD_ID_ACCEPT2: | ||
242 | if (!(table->td_flags & ACCEPT2_FLAGS(flags))) | ||
243 | goto fail; | ||
244 | break; | ||
245 | case YYTD_ID_BASE: | ||
246 | if (table->td_flags != YYTD_DATA32) | ||
247 | goto fail; | ||
248 | break; | ||
249 | case YYTD_ID_DEF: | ||
250 | case YYTD_ID_NXT: | ||
251 | case YYTD_ID_CHK: | ||
252 | if (table->td_flags != YYTD_DATA16) | ||
253 | goto fail; | ||
254 | break; | ||
255 | case YYTD_ID_EC: | ||
256 | if (table->td_flags != YYTD_DATA8) | ||
257 | goto fail; | ||
258 | break; | ||
259 | default: | ||
260 | goto fail; | ||
261 | } | ||
262 | /* check for duplicate table entry */ | ||
263 | if (dfa->tables[table->td_id]) | ||
264 | goto fail; | ||
265 | dfa->tables[table->td_id] = table; | ||
266 | data += table_size(table->td_lolen, table->td_flags); | ||
267 | size -= table_size(table->td_lolen, table->td_flags); | ||
268 | table = NULL; | ||
269 | } | ||
270 | |||
271 | error = verify_dfa(dfa, flags); | ||
272 | if (error) | ||
273 | goto fail; | ||
274 | |||
275 | return dfa; | ||
276 | |||
277 | fail: | ||
278 | kvfree(table); | ||
279 | dfa_free(dfa); | ||
280 | return ERR_PTR(error); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * aa_dfa_match_len - traverse @dfa to find state @str stops at | ||
285 | * @dfa: the dfa to match @str against (NOT NULL) | ||
286 | * @start: the state of the dfa to start matching in | ||
287 | * @str: the string of bytes to match against the dfa (NOT NULL) | ||
288 | * @len: length of the string of bytes to match | ||
289 | * | ||
290 | * aa_dfa_match_len will match @str against the dfa and return the state it | ||
291 | * finished matching in. The final state can be used to look up the accepting | ||
292 | * label, or as the start state of a continuing match. | ||
293 | * | ||
294 | * This function will happily match again the 0 byte and only finishes | ||
295 | * when @len input is consumed. | ||
296 | * | ||
297 | * Returns: final state reached after input is consumed | ||
298 | */ | ||
299 | unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, | ||
300 | const char *str, int len) | ||
301 | { | ||
302 | u16 *def = DEFAULT_TABLE(dfa); | ||
303 | u32 *base = BASE_TABLE(dfa); | ||
304 | u16 *next = NEXT_TABLE(dfa); | ||
305 | u16 *check = CHECK_TABLE(dfa); | ||
306 | unsigned int state = start, pos; | ||
307 | |||
308 | if (state == 0) | ||
309 | return 0; | ||
310 | |||
311 | /* current state is <state>, matching character *str */ | ||
312 | if (dfa->tables[YYTD_ID_EC]) { | ||
313 | /* Equivalence class table defined */ | ||
314 | u8 *equiv = EQUIV_TABLE(dfa); | ||
315 | /* default is direct to next state */ | ||
316 | for (; len; len--) { | ||
317 | pos = base[state] + equiv[(u8) *str++]; | ||
318 | if (check[pos] == state) | ||
319 | state = next[pos]; | ||
320 | else | ||
321 | state = def[state]; | ||
322 | } | ||
323 | } else { | ||
324 | /* default is direct to next state */ | ||
325 | for (; len; len--) { | ||
326 | pos = base[state] + (u8) *str++; | ||
327 | if (check[pos] == state) | ||
328 | state = next[pos]; | ||
329 | else | ||
330 | state = def[state]; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | return state; | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * aa_dfa_next_state - traverse @dfa to find state @str stops at | ||
339 | * @dfa: the dfa to match @str against (NOT NULL) | ||
340 | * @start: the state of the dfa to start matching in | ||
341 | * @str: the null terminated string of bytes to match against the dfa (NOT NULL) | ||
342 | * | ||
343 | * aa_dfa_next_state will match @str against the dfa and return the state it | ||
344 | * finished matching in. The final state can be used to look up the accepting | ||
345 | * label, or as the start state of a continuing match. | ||
346 | * | ||
347 | * Returns: final state reached after input is consumed | ||
348 | */ | ||
349 | unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, | ||
350 | const char *str) | ||
351 | { | ||
352 | return aa_dfa_match_len(dfa, start, str, strlen(str)); | ||
353 | } | ||
diff --git a/security/apparmor/path.c b/security/apparmor/path.c new file mode 100644 index 00000000000..96bab9469d4 --- /dev/null +++ b/security/apparmor/path.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor function for pathnames | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/magic.h> | ||
16 | #include <linux/mnt_namespace.h> | ||
17 | #include <linux/mount.h> | ||
18 | #include <linux/namei.h> | ||
19 | #include <linux/nsproxy.h> | ||
20 | #include <linux/path.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/fs_struct.h> | ||
24 | |||
25 | #include "include/apparmor.h" | ||
26 | #include "include/path.h" | ||
27 | #include "include/policy.h" | ||
28 | |||
29 | |||
30 | /* modified from dcache.c */ | ||
31 | static int prepend(char **buffer, int buflen, const char *str, int namelen) | ||
32 | { | ||
33 | buflen -= namelen; | ||
34 | if (buflen < 0) | ||
35 | return -ENAMETOOLONG; | ||
36 | *buffer -= namelen; | ||
37 | memcpy(*buffer, str, namelen); | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) | ||
42 | |||
43 | /** | ||
44 | * d_namespace_path - lookup a name associated with a given path | ||
45 | * @path: path to lookup (NOT NULL) | ||
46 | * @buf: buffer to store path to (NOT NULL) | ||
47 | * @buflen: length of @buf | ||
48 | * @name: Returns - pointer for start of path name with in @buf (NOT NULL) | ||
49 | * @flags: flags controlling path lookup | ||
50 | * | ||
51 | * Handle path name lookup. | ||
52 | * | ||
53 | * Returns: %0 else error code if path lookup fails | ||
54 | * When no error the path name is returned in @name which points to | ||
55 | * to a position in @buf | ||
56 | */ | ||
57 | static int d_namespace_path(struct path *path, char *buf, int buflen, | ||
58 | char **name, int flags) | ||
59 | { | ||
60 | struct path root, tmp; | ||
61 | char *res; | ||
62 | int deleted, connected; | ||
63 | int error = 0; | ||
64 | |||
65 | /* Get the root we want to resolve too */ | ||
66 | if (flags & PATH_CHROOT_REL) { | ||
67 | /* resolve paths relative to chroot */ | ||
68 | read_lock(¤t->fs->lock); | ||
69 | root = current->fs->root; | ||
70 | /* released below */ | ||
71 | path_get(&root); | ||
72 | read_unlock(¤t->fs->lock); | ||
73 | } else { | ||
74 | /* resolve paths relative to namespace */ | ||
75 | root.mnt = current->nsproxy->mnt_ns->root; | ||
76 | root.dentry = root.mnt->mnt_root; | ||
77 | /* released below */ | ||
78 | path_get(&root); | ||
79 | } | ||
80 | |||
81 | spin_lock(&dcache_lock); | ||
82 | /* There is a race window between path lookup here and the | ||
83 | * need to strip the " (deleted) string that __d_path applies | ||
84 | * Detect the race and relookup the path | ||
85 | * | ||
86 | * The stripping of (deleted) is a hack that could be removed | ||
87 | * with an updated __d_path | ||
88 | */ | ||
89 | do { | ||
90 | tmp = root; | ||
91 | deleted = d_unlinked(path->dentry); | ||
92 | res = __d_path(path, &tmp, buf, buflen); | ||
93 | |||
94 | } while (deleted != d_unlinked(path->dentry)); | ||
95 | spin_unlock(&dcache_lock); | ||
96 | |||
97 | *name = res; | ||
98 | /* handle error conditions - and still allow a partial path to | ||
99 | * be returned. | ||
100 | */ | ||
101 | if (IS_ERR(res)) { | ||
102 | error = PTR_ERR(res); | ||
103 | *name = buf; | ||
104 | goto out; | ||
105 | } | ||
106 | if (deleted) { | ||
107 | /* On some filesystems, newly allocated dentries appear to the | ||
108 | * security_path hooks as a deleted dentry except without an | ||
109 | * inode allocated. | ||
110 | * | ||
111 | * Remove the appended deleted text and return as string for | ||
112 | * normal mediation, or auditing. The (deleted) string is | ||
113 | * guaranteed to be added in this case, so just strip it. | ||
114 | */ | ||
115 | buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ | ||
116 | |||
117 | if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { | ||
118 | error = -ENOENT; | ||
119 | goto out; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /* Determine if the path is connected to the expected root */ | ||
124 | connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; | ||
125 | |||
126 | /* If the path is not connected, | ||
127 | * check if it is a sysctl and handle specially else remove any | ||
128 | * leading / that __d_path may have returned. | ||
129 | * Unless | ||
130 | * specifically directed to connect the path, | ||
131 | * OR | ||
132 | * if in a chroot and doing chroot relative paths and the path | ||
133 | * resolves to the namespace root (would be connected outside | ||
134 | * of chroot) and specifically directed to connect paths to | ||
135 | * namespace root. | ||
136 | */ | ||
137 | if (!connected) { | ||
138 | /* is the disconnect path a sysctl? */ | ||
139 | if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | ||
140 | strncmp(*name, "/sys/", 5) == 0) { | ||
141 | /* TODO: convert over to using a per namespace | ||
142 | * control instead of hard coded /proc | ||
143 | */ | ||
144 | error = prepend(name, *name - buf, "/proc", 5); | ||
145 | } else if (!(flags & PATH_CONNECT_PATH) && | ||
146 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | ||
147 | (tmp.mnt == current->nsproxy->mnt_ns->root && | ||
148 | tmp.dentry == tmp.mnt->mnt_root))) { | ||
149 | /* disconnected path, don't return pathname starting | ||
150 | * with '/' | ||
151 | */ | ||
152 | error = -ESTALE; | ||
153 | if (*res == '/') | ||
154 | *name = res + 1; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | out: | ||
159 | path_put(&root); | ||
160 | |||
161 | return error; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended | ||
166 | * @path: path to get name for (NOT NULL) | ||
167 | * @flags: flags controlling path lookup | ||
168 | * @buffer: buffer to put name in (NOT NULL) | ||
169 | * @size: size of buffer | ||
170 | * @name: Returns - contains position of path name in @buffer (NOT NULL) | ||
171 | * | ||
172 | * Returns: %0 else error on failure | ||
173 | */ | ||
174 | static int get_name_to_buffer(struct path *path, int flags, char *buffer, | ||
175 | int size, char **name) | ||
176 | { | ||
177 | int adjust = (flags & PATH_IS_DIR) ? 1 : 0; | ||
178 | int error = d_namespace_path(path, buffer, size - adjust, name, flags); | ||
179 | |||
180 | if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') | ||
181 | /* | ||
182 | * Append "/" to the pathname. The root directory is a special | ||
183 | * case; it already ends in slash. | ||
184 | */ | ||
185 | strcpy(&buffer[size - 2], "/"); | ||
186 | |||
187 | return error; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * aa_get_name - compute the pathname of a file | ||
192 | * @path: path the file (NOT NULL) | ||
193 | * @flags: flags controlling path name generation | ||
194 | * @buffer: buffer that aa_get_name() allocated (NOT NULL) | ||
195 | * @name: Returns - the generated path name if !error (NOT NULL) | ||
196 | * | ||
197 | * @name is a pointer to the beginning of the pathname (which usually differs | ||
198 | * from the beginning of the buffer), or NULL. If there is an error @name | ||
199 | * may contain a partial or invalid name that can be used for audit purposes, | ||
200 | * but it can not be used for mediation. | ||
201 | * | ||
202 | * We need PATH_IS_DIR to indicate whether the file is a directory or not | ||
203 | * because the file may not yet exist, and so we cannot check the inode's | ||
204 | * file type. | ||
205 | * | ||
206 | * Returns: %0 else error code if could retrieve name | ||
207 | */ | ||
208 | int aa_get_name(struct path *path, int flags, char **buffer, const char **name) | ||
209 | { | ||
210 | char *buf, *str = NULL; | ||
211 | int size = 256; | ||
212 | int error; | ||
213 | |||
214 | *name = NULL; | ||
215 | *buffer = NULL; | ||
216 | for (;;) { | ||
217 | /* freed by caller */ | ||
218 | buf = kmalloc(size, GFP_KERNEL); | ||
219 | if (!buf) | ||
220 | return -ENOMEM; | ||
221 | |||
222 | error = get_name_to_buffer(path, flags, buf, size, &str); | ||
223 | if (error != -ENAMETOOLONG) | ||
224 | break; | ||
225 | |||
226 | kfree(buf); | ||
227 | size <<= 1; | ||
228 | if (size > aa_g_path_max) | ||
229 | return -ENAMETOOLONG; | ||
230 | } | ||
231 | *buffer = buf; | ||
232 | *name = str; | ||
233 | |||
234 | return error; | ||
235 | } | ||
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c new file mode 100644 index 00000000000..3cdc1ad0787 --- /dev/null +++ b/security/apparmor/policy.c | |||
@@ -0,0 +1,1184 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor policy manipulation functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | * | ||
14 | * | ||
15 | * AppArmor policy is based around profiles, which contain the rules a | ||
16 | * task is confined by. Every task in the system has a profile attached | ||
17 | * to it determined either by matching "unconfined" tasks against the | ||
18 | * visible set of profiles or by following a profiles attachment rules. | ||
19 | * | ||
20 | * Each profile exists in a profile namespace which is a container of | ||
21 | * visible profiles. Each namespace contains a special "unconfined" profile, | ||
22 | * which doesn't enforce any confinement on a task beyond DAC. | ||
23 | * | ||
24 | * Namespace and profile names can be written together in either | ||
25 | * of two syntaxes. | ||
26 | * :namespace:profile - used by kernel interfaces for easy detection | ||
27 | * namespace://profile - used by policy | ||
28 | * | ||
29 | * Profile names can not start with : or @ or ^ and may not contain \0 | ||
30 | * | ||
31 | * Reserved profile names | ||
32 | * unconfined - special automatically generated unconfined profile | ||
33 | * inherit - special name to indicate profile inheritance | ||
34 | * null-XXXX-YYYY - special automatically generated learning profiles | ||
35 | * | ||
36 | * Namespace names may not start with / or @ and may not contain \0 or : | ||
37 | * Reserved namespace names | ||
38 | * user-XXXX - user defined profiles | ||
39 | * | ||
40 | * a // in a profile or namespace name indicates a hierarchical name with the | ||
41 | * name before the // being the parent and the name after the child. | ||
42 | * | ||
43 | * Profile and namespace hierarchies serve two different but similar purposes. | ||
44 | * The namespace contains the set of visible profiles that are considered | ||
45 | * for attachment. The hierarchy of namespaces allows for virtualizing | ||
46 | * the namespace so that for example a chroot can have its own set of profiles | ||
47 | * which may define some local user namespaces. | ||
48 | * The profile hierarchy severs two distinct purposes, | ||
49 | * - it allows for sub profiles or hats, which allows an application to run | ||
50 | * subprograms under its own profile with different restriction than it | ||
51 | * self, and not have it use the system profile. | ||
52 | * eg. if a mail program starts an editor, the policy might make the | ||
53 | * restrictions tighter on the editor tighter than the mail program, | ||
54 | * and definitely different than general editor restrictions | ||
55 | * - it allows for binary hierarchy of profiles, so that execution history | ||
56 | * is preserved. This feature isn't exploited by AppArmor reference policy | ||
57 | * but is allowed. NOTE: this is currently suboptimal because profile | ||
58 | * aliasing is not currently implemented so that a profile for each | ||
59 | * level must be defined. | ||
60 | * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started | ||
61 | * from /bin/bash | ||
62 | * | ||
63 | * A profile or namespace name that can contain one or more // separators | ||
64 | * is referred to as an hname (hierarchical). | ||
65 | * eg. /bin/bash//bin/ls | ||
66 | * | ||
67 | * An fqname is a name that may contain both namespace and profile hnames. | ||
68 | * eg. :ns:/bin/bash//bin/ls | ||
69 | * | ||
70 | * NOTES: | ||
71 | * - locking of profile lists is currently fairly coarse. All profile | ||
72 | * lists within a namespace use the namespace lock. | ||
73 | * FIXME: move profile lists to using rcu_lists | ||
74 | */ | ||
75 | |||
76 | #include <linux/slab.h> | ||
77 | #include <linux/spinlock.h> | ||
78 | #include <linux/string.h> | ||
79 | |||
80 | #include "include/apparmor.h" | ||
81 | #include "include/capability.h" | ||
82 | #include "include/context.h" | ||
83 | #include "include/file.h" | ||
84 | #include "include/ipc.h" | ||
85 | #include "include/match.h" | ||
86 | #include "include/path.h" | ||
87 | #include "include/policy.h" | ||
88 | #include "include/policy_unpack.h" | ||
89 | #include "include/resource.h" | ||
90 | #include "include/sid.h" | ||
91 | |||
92 | |||
93 | /* root profile namespace */ | ||
94 | struct aa_namespace *root_ns; | ||
95 | |||
96 | const char *profile_mode_names[] = { | ||
97 | "enforce", | ||
98 | "complain", | ||
99 | "kill", | ||
100 | }; | ||
101 | |||
102 | /** | ||
103 | * hname_tail - find the last component of an hname | ||
104 | * @name: hname to find the base profile name component of (NOT NULL) | ||
105 | * | ||
106 | * Returns: the tail (base profile name) name component of an hname | ||
107 | */ | ||
108 | static const char *hname_tail(const char *hname) | ||
109 | { | ||
110 | char *split; | ||
111 | hname = strim((char *)hname); | ||
112 | for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) | ||
113 | hname = split + 2; | ||
114 | |||
115 | return hname; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * policy_init - initialize a policy structure | ||
120 | * @policy: policy to initialize (NOT NULL) | ||
121 | * @prefix: prefix name if any is required. (MAYBE NULL) | ||
122 | * @name: name of the policy, init will make a copy of it (NOT NULL) | ||
123 | * | ||
124 | * Note: this fn creates a copy of strings passed in | ||
125 | * | ||
126 | * Returns: true if policy init successful | ||
127 | */ | ||
128 | static bool policy_init(struct aa_policy *policy, const char *prefix, | ||
129 | const char *name) | ||
130 | { | ||
131 | /* freed by policy_free */ | ||
132 | if (prefix) { | ||
133 | policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, | ||
134 | GFP_KERNEL); | ||
135 | if (policy->hname) | ||
136 | sprintf(policy->hname, "%s//%s", prefix, name); | ||
137 | } else | ||
138 | policy->hname = kstrdup(name, GFP_KERNEL); | ||
139 | if (!policy->hname) | ||
140 | return 0; | ||
141 | /* base.name is a substring of fqname */ | ||
142 | policy->name = (char *)hname_tail(policy->hname); | ||
143 | INIT_LIST_HEAD(&policy->list); | ||
144 | INIT_LIST_HEAD(&policy->profiles); | ||
145 | kref_init(&policy->count); | ||
146 | |||
147 | return 1; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * policy_destroy - free the elements referenced by @policy | ||
152 | * @policy: policy that is to have its elements freed (NOT NULL) | ||
153 | */ | ||
154 | static void policy_destroy(struct aa_policy *policy) | ||
155 | { | ||
156 | /* still contains profiles -- invalid */ | ||
157 | if (!list_empty(&policy->profiles)) { | ||
158 | AA_ERROR("%s: internal error, " | ||
159 | "policy '%s' still contains profiles\n", | ||
160 | __func__, policy->name); | ||
161 | BUG(); | ||
162 | } | ||
163 | if (!list_empty(&policy->list)) { | ||
164 | AA_ERROR("%s: internal error, policy '%s' still on list\n", | ||
165 | __func__, policy->name); | ||
166 | BUG(); | ||
167 | } | ||
168 | |||
169 | /* don't free name as its a subset of hname */ | ||
170 | kzfree(policy->hname); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * __policy_find - find a policy by @name on a policy list | ||
175 | * @head: list to search (NOT NULL) | ||
176 | * @name: name to search for (NOT NULL) | ||
177 | * | ||
178 | * Requires: correct locks for the @head list be held | ||
179 | * | ||
180 | * Returns: unrefcounted policy that match @name or NULL if not found | ||
181 | */ | ||
182 | static struct aa_policy *__policy_find(struct list_head *head, const char *name) | ||
183 | { | ||
184 | struct aa_policy *policy; | ||
185 | |||
186 | list_for_each_entry(policy, head, list) { | ||
187 | if (!strcmp(policy->name, name)) | ||
188 | return policy; | ||
189 | } | ||
190 | return NULL; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * __policy_strn_find - find a policy that's name matches @len chars of @str | ||
195 | * @head: list to search (NOT NULL) | ||
196 | * @str: string to search for (NOT NULL) | ||
197 | * @len: length of match required | ||
198 | * | ||
199 | * Requires: correct locks for the @head list be held | ||
200 | * | ||
201 | * Returns: unrefcounted policy that match @str or NULL if not found | ||
202 | * | ||
203 | * if @len == strlen(@strlen) then this is equiv to __policy_find | ||
204 | * other wise it allows searching for policy by a partial match of name | ||
205 | */ | ||
206 | static struct aa_policy *__policy_strn_find(struct list_head *head, | ||
207 | const char *str, int len) | ||
208 | { | ||
209 | struct aa_policy *policy; | ||
210 | |||
211 | list_for_each_entry(policy, head, list) { | ||
212 | if (aa_strneq(policy->name, str, len)) | ||
213 | return policy; | ||
214 | } | ||
215 | |||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Routines for AppArmor namespaces | ||
221 | */ | ||
222 | |||
223 | static const char *hidden_ns_name = "---"; | ||
224 | /** | ||
225 | * aa_ns_visible - test if @view is visible from @curr | ||
226 | * @curr: namespace to treat as the parent (NOT NULL) | ||
227 | * @view: namespace to test if visible from @curr (NOT NULL) | ||
228 | * | ||
229 | * Returns: true if @view is visible from @curr else false | ||
230 | */ | ||
231 | bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) | ||
232 | { | ||
233 | if (curr == view) | ||
234 | return true; | ||
235 | |||
236 | for ( ; view; view = view->parent) { | ||
237 | if (view->parent == curr) | ||
238 | return true; | ||
239 | } | ||
240 | return false; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * aa_na_name - Find the ns name to display for @view from @curr | ||
245 | * @curr - current namespace (NOT NULL) | ||
246 | * @view - namespace attempting to view (NOT NULL) | ||
247 | * | ||
248 | * Returns: name of @view visible from @curr | ||
249 | */ | ||
250 | const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) | ||
251 | { | ||
252 | /* if view == curr then the namespace name isn't displayed */ | ||
253 | if (curr == view) | ||
254 | return ""; | ||
255 | |||
256 | if (aa_ns_visible(curr, view)) { | ||
257 | /* at this point if a ns is visible it is in a view ns | ||
258 | * thus the curr ns.hname is a prefix of its name. | ||
259 | * Only output the virtualized portion of the name | ||
260 | * Add + 2 to skip over // separating curr hname prefix | ||
261 | * from the visible tail of the views hname | ||
262 | */ | ||
263 | return view->base.hname + strlen(curr->base.hname) + 2; | ||
264 | } else | ||
265 | return hidden_ns_name; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * alloc_namespace - allocate, initialize and return a new namespace | ||
270 | * @prefix: parent namespace name (MAYBE NULL) | ||
271 | * @name: a preallocated name (NOT NULL) | ||
272 | * | ||
273 | * Returns: refcounted namespace or NULL on failure. | ||
274 | */ | ||
275 | static struct aa_namespace *alloc_namespace(const char *prefix, | ||
276 | const char *name) | ||
277 | { | ||
278 | struct aa_namespace *ns; | ||
279 | |||
280 | ns = kzalloc(sizeof(*ns), GFP_KERNEL); | ||
281 | AA_DEBUG("%s(%p)\n", __func__, ns); | ||
282 | if (!ns) | ||
283 | return NULL; | ||
284 | if (!policy_init(&ns->base, prefix, name)) | ||
285 | goto fail_ns; | ||
286 | |||
287 | INIT_LIST_HEAD(&ns->sub_ns); | ||
288 | rwlock_init(&ns->lock); | ||
289 | |||
290 | /* released by free_namespace */ | ||
291 | ns->unconfined = aa_alloc_profile("unconfined"); | ||
292 | if (!ns->unconfined) | ||
293 | goto fail_unconfined; | ||
294 | |||
295 | ns->unconfined->sid = aa_alloc_sid(); | ||
296 | ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | | ||
297 | PFLAG_IMMUTABLE; | ||
298 | |||
299 | /* | ||
300 | * released by free_namespace, however __remove_namespace breaks | ||
301 | * the cyclic references (ns->unconfined, and unconfined->ns) and | ||
302 | * replaces with refs to parent namespace unconfined | ||
303 | */ | ||
304 | ns->unconfined->ns = aa_get_namespace(ns); | ||
305 | |||
306 | return ns; | ||
307 | |||
308 | fail_unconfined: | ||
309 | kzfree(ns->base.name); | ||
310 | fail_ns: | ||
311 | kzfree(ns); | ||
312 | return NULL; | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * free_namespace - free a profile namespace | ||
317 | * @ns: the namespace to free (MAYBE NULL) | ||
318 | * | ||
319 | * Requires: All references to the namespace must have been put, if the | ||
320 | * namespace was referenced by a profile confining a task, | ||
321 | */ | ||
322 | static void free_namespace(struct aa_namespace *ns) | ||
323 | { | ||
324 | if (!ns) | ||
325 | return; | ||
326 | |||
327 | policy_destroy(&ns->base); | ||
328 | aa_put_namespace(ns->parent); | ||
329 | |||
330 | if (ns->unconfined && ns->unconfined->ns == ns) | ||
331 | ns->unconfined->ns = NULL; | ||
332 | |||
333 | aa_put_profile(ns->unconfined); | ||
334 | kzfree(ns); | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) | ||
339 | * @kr: kref callback for freeing of a namespace (NOT NULL) | ||
340 | */ | ||
341 | void aa_free_namespace_kref(struct kref *kref) | ||
342 | { | ||
343 | free_namespace(container_of(kref, struct aa_namespace, base.count)); | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * __aa_find_namespace - find a namespace on a list by @name | ||
348 | * @head: list to search for namespace on (NOT NULL) | ||
349 | * @name: name of namespace to look for (NOT NULL) | ||
350 | * | ||
351 | * Returns: unrefcounted namespace | ||
352 | * | ||
353 | * Requires: ns lock be held | ||
354 | */ | ||
355 | static struct aa_namespace *__aa_find_namespace(struct list_head *head, | ||
356 | const char *name) | ||
357 | { | ||
358 | return (struct aa_namespace *)__policy_find(head, name); | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * aa_find_namespace - look up a profile namespace on the namespace list | ||
363 | * @root: namespace to search in (NOT NULL) | ||
364 | * @name: name of namespace to find (NOT NULL) | ||
365 | * | ||
366 | * Returns: a refcounted namespace on the list, or NULL if no namespace | ||
367 | * called @name exists. | ||
368 | * | ||
369 | * refcount released by caller | ||
370 | */ | ||
371 | struct aa_namespace *aa_find_namespace(struct aa_namespace *root, | ||
372 | const char *name) | ||
373 | { | ||
374 | struct aa_namespace *ns = NULL; | ||
375 | |||
376 | read_lock(&root->lock); | ||
377 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
378 | read_unlock(&root->lock); | ||
379 | |||
380 | return ns; | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * aa_prepare_namespace - find an existing or create a new namespace of @name | ||
385 | * @name: the namespace to find or add (MAYBE NULL) | ||
386 | * | ||
387 | * Returns: refcounted namespace or NULL if failed to create one | ||
388 | */ | ||
389 | static struct aa_namespace *aa_prepare_namespace(const char *name) | ||
390 | { | ||
391 | struct aa_namespace *ns, *root; | ||
392 | |||
393 | root = aa_current_profile()->ns; | ||
394 | |||
395 | write_lock(&root->lock); | ||
396 | |||
397 | /* if name isn't specified the profile is loaded to the current ns */ | ||
398 | if (!name) { | ||
399 | /* released by caller */ | ||
400 | ns = aa_get_namespace(root); | ||
401 | goto out; | ||
402 | } | ||
403 | |||
404 | /* try and find the specified ns and if it doesn't exist create it */ | ||
405 | /* released by caller */ | ||
406 | ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); | ||
407 | if (!ns) { | ||
408 | /* namespace not found */ | ||
409 | struct aa_namespace *new_ns; | ||
410 | write_unlock(&root->lock); | ||
411 | new_ns = alloc_namespace(root->base.hname, name); | ||
412 | if (!new_ns) | ||
413 | return NULL; | ||
414 | write_lock(&root->lock); | ||
415 | /* test for race when new_ns was allocated */ | ||
416 | ns = __aa_find_namespace(&root->sub_ns, name); | ||
417 | if (!ns) { | ||
418 | /* add parent ref */ | ||
419 | new_ns->parent = aa_get_namespace(root); | ||
420 | |||
421 | list_add(&new_ns->base.list, &root->sub_ns); | ||
422 | /* add list ref */ | ||
423 | ns = aa_get_namespace(new_ns); | ||
424 | } else { | ||
425 | /* raced so free the new one */ | ||
426 | free_namespace(new_ns); | ||
427 | /* get reference on namespace */ | ||
428 | aa_get_namespace(ns); | ||
429 | } | ||
430 | } | ||
431 | out: | ||
432 | write_unlock(&root->lock); | ||
433 | |||
434 | /* return ref */ | ||
435 | return ns; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * __list_add_profile - add a profile to a list | ||
440 | * @list: list to add it to (NOT NULL) | ||
441 | * @profile: the profile to add (NOT NULL) | ||
442 | * | ||
443 | * refcount @profile, should be put by __list_remove_profile | ||
444 | * | ||
445 | * Requires: namespace lock be held, or list not be shared | ||
446 | */ | ||
447 | static void __list_add_profile(struct list_head *list, | ||
448 | struct aa_profile *profile) | ||
449 | { | ||
450 | list_add(&profile->base.list, list); | ||
451 | /* get list reference */ | ||
452 | aa_get_profile(profile); | ||
453 | } | ||
454 | |||
455 | /** | ||
456 | * __list_remove_profile - remove a profile from the list it is on | ||
457 | * @profile: the profile to remove (NOT NULL) | ||
458 | * | ||
459 | * remove a profile from the list, warning generally removal should | ||
460 | * be done with __replace_profile as most profile removals are | ||
461 | * replacements to the unconfined profile. | ||
462 | * | ||
463 | * put @profile list refcount | ||
464 | * | ||
465 | * Requires: namespace lock be held, or list not have been live | ||
466 | */ | ||
467 | static void __list_remove_profile(struct aa_profile *profile) | ||
468 | { | ||
469 | list_del_init(&profile->base.list); | ||
470 | if (!(profile->flags & PFLAG_NO_LIST_REF)) | ||
471 | /* release list reference */ | ||
472 | aa_put_profile(profile); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * __replace_profile - replace @old with @new on a list | ||
477 | * @old: profile to be replaced (NOT NULL) | ||
478 | * @new: profile to replace @old with (NOT NULL) | ||
479 | * | ||
480 | * Will duplicate and refcount elements that @new inherits from @old | ||
481 | * and will inherit @old children. | ||
482 | * | ||
483 | * refcount @new for list, put @old list refcount | ||
484 | * | ||
485 | * Requires: namespace list lock be held, or list not be shared | ||
486 | */ | ||
487 | static void __replace_profile(struct aa_profile *old, struct aa_profile *new) | ||
488 | { | ||
489 | struct aa_policy *policy; | ||
490 | struct aa_profile *child, *tmp; | ||
491 | |||
492 | if (old->parent) | ||
493 | policy = &old->parent->base; | ||
494 | else | ||
495 | policy = &old->ns->base; | ||
496 | |||
497 | /* released when @new is freed */ | ||
498 | new->parent = aa_get_profile(old->parent); | ||
499 | new->ns = aa_get_namespace(old->ns); | ||
500 | new->sid = old->sid; | ||
501 | __list_add_profile(&policy->profiles, new); | ||
502 | /* inherit children */ | ||
503 | list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { | ||
504 | aa_put_profile(child->parent); | ||
505 | child->parent = aa_get_profile(new); | ||
506 | /* list refcount transferred to @new*/ | ||
507 | list_move(&child->base.list, &new->base.profiles); | ||
508 | } | ||
509 | |||
510 | /* released by free_profile */ | ||
511 | old->replacedby = aa_get_profile(new); | ||
512 | __list_remove_profile(old); | ||
513 | } | ||
514 | |||
515 | static void __profile_list_release(struct list_head *head); | ||
516 | |||
517 | /** | ||
518 | * __remove_profile - remove old profile, and children | ||
519 | * @profile: profile to be replaced (NOT NULL) | ||
520 | * | ||
521 | * Requires: namespace list lock be held, or list not be shared | ||
522 | */ | ||
523 | static void __remove_profile(struct aa_profile *profile) | ||
524 | { | ||
525 | /* release any children lists first */ | ||
526 | __profile_list_release(&profile->base.profiles); | ||
527 | /* released by free_profile */ | ||
528 | profile->replacedby = aa_get_profile(profile->ns->unconfined); | ||
529 | __list_remove_profile(profile); | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * __profile_list_release - remove all profiles on the list and put refs | ||
534 | * @head: list of profiles (NOT NULL) | ||
535 | * | ||
536 | * Requires: namespace lock be held | ||
537 | */ | ||
538 | static void __profile_list_release(struct list_head *head) | ||
539 | { | ||
540 | struct aa_profile *profile, *tmp; | ||
541 | list_for_each_entry_safe(profile, tmp, head, base.list) | ||
542 | __remove_profile(profile); | ||
543 | } | ||
544 | |||
545 | static void __ns_list_release(struct list_head *head); | ||
546 | |||
547 | /** | ||
548 | * destroy_namespace - remove everything contained by @ns | ||
549 | * @ns: namespace to have it contents removed (NOT NULL) | ||
550 | */ | ||
551 | static void destroy_namespace(struct aa_namespace *ns) | ||
552 | { | ||
553 | if (!ns) | ||
554 | return; | ||
555 | |||
556 | write_lock(&ns->lock); | ||
557 | /* release all profiles in this namespace */ | ||
558 | __profile_list_release(&ns->base.profiles); | ||
559 | |||
560 | /* release all sub namespaces */ | ||
561 | __ns_list_release(&ns->sub_ns); | ||
562 | |||
563 | write_unlock(&ns->lock); | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * __remove_namespace - remove a namespace and all its children | ||
568 | * @ns: namespace to be removed (NOT NULL) | ||
569 | * | ||
570 | * Requires: ns->parent->lock be held and ns removed from parent. | ||
571 | */ | ||
572 | static void __remove_namespace(struct aa_namespace *ns) | ||
573 | { | ||
574 | struct aa_profile *unconfined = ns->unconfined; | ||
575 | |||
576 | /* remove ns from namespace list */ | ||
577 | list_del_init(&ns->base.list); | ||
578 | |||
579 | /* | ||
580 | * break the ns, unconfined profile cyclic reference and forward | ||
581 | * all new unconfined profiles requests to the parent namespace | ||
582 | * This will result in all confined tasks that have a profile | ||
583 | * being removed, inheriting the parent->unconfined profile. | ||
584 | */ | ||
585 | if (ns->parent) | ||
586 | ns->unconfined = aa_get_profile(ns->parent->unconfined); | ||
587 | |||
588 | destroy_namespace(ns); | ||
589 | |||
590 | /* release original ns->unconfined ref */ | ||
591 | aa_put_profile(unconfined); | ||
592 | /* release ns->base.list ref, from removal above */ | ||
593 | aa_put_namespace(ns); | ||
594 | } | ||
595 | |||
596 | /** | ||
597 | * __ns_list_release - remove all profile namespaces on the list put refs | ||
598 | * @head: list of profile namespaces (NOT NULL) | ||
599 | * | ||
600 | * Requires: namespace lock be held | ||
601 | */ | ||
602 | static void __ns_list_release(struct list_head *head) | ||
603 | { | ||
604 | struct aa_namespace *ns, *tmp; | ||
605 | list_for_each_entry_safe(ns, tmp, head, base.list) | ||
606 | __remove_namespace(ns); | ||
607 | |||
608 | } | ||
609 | |||
610 | /** | ||
611 | * aa_alloc_root_ns - allocate the root profile namespace | ||
612 | * | ||
613 | * Returns: %0 on success else error | ||
614 | * | ||
615 | */ | ||
616 | int __init aa_alloc_root_ns(void) | ||
617 | { | ||
618 | /* released by aa_free_root_ns - used as list ref*/ | ||
619 | root_ns = alloc_namespace(NULL, "root"); | ||
620 | if (!root_ns) | ||
621 | return -ENOMEM; | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | /** | ||
627 | * aa_free_root_ns - free the root profile namespace | ||
628 | */ | ||
629 | void __init aa_free_root_ns(void) | ||
630 | { | ||
631 | struct aa_namespace *ns = root_ns; | ||
632 | root_ns = NULL; | ||
633 | |||
634 | destroy_namespace(ns); | ||
635 | aa_put_namespace(ns); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * aa_alloc_profile - allocate, initialize and return a new profile | ||
640 | * @hname: name of the profile (NOT NULL) | ||
641 | * | ||
642 | * Returns: refcount profile or NULL on failure | ||
643 | */ | ||
644 | struct aa_profile *aa_alloc_profile(const char *hname) | ||
645 | { | ||
646 | struct aa_profile *profile; | ||
647 | |||
648 | /* freed by free_profile - usually through aa_put_profile */ | ||
649 | profile = kzalloc(sizeof(*profile), GFP_KERNEL); | ||
650 | if (!profile) | ||
651 | return NULL; | ||
652 | |||
653 | if (!policy_init(&profile->base, NULL, hname)) { | ||
654 | kzfree(profile); | ||
655 | return NULL; | ||
656 | } | ||
657 | |||
658 | /* refcount released by caller */ | ||
659 | return profile; | ||
660 | } | ||
661 | |||
662 | /** | ||
663 | * aa_new_null_profile - create a new null-X learning profile | ||
664 | * @parent: profile that caused this profile to be created (NOT NULL) | ||
665 | * @hat: true if the null- learning profile is a hat | ||
666 | * | ||
667 | * Create a null- complain mode profile used in learning mode. The name of | ||
668 | * the profile is unique and follows the format of parent//null-sid. | ||
669 | * | ||
670 | * null profiles are added to the profile list but the list does not | ||
671 | * hold a count on them so that they are automatically released when | ||
672 | * not in use. | ||
673 | * | ||
674 | * Returns: new refcounted profile else NULL on failure | ||
675 | */ | ||
676 | struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) | ||
677 | { | ||
678 | struct aa_profile *profile = NULL; | ||
679 | char *name; | ||
680 | u32 sid = aa_alloc_sid(); | ||
681 | |||
682 | /* freed below */ | ||
683 | name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); | ||
684 | if (!name) | ||
685 | goto fail; | ||
686 | sprintf(name, "%s//null-%x", parent->base.hname, sid); | ||
687 | |||
688 | profile = aa_alloc_profile(name); | ||
689 | kfree(name); | ||
690 | if (!profile) | ||
691 | goto fail; | ||
692 | |||
693 | profile->sid = sid; | ||
694 | profile->mode = APPARMOR_COMPLAIN; | ||
695 | profile->flags = PFLAG_NULL; | ||
696 | if (hat) | ||
697 | profile->flags |= PFLAG_HAT; | ||
698 | |||
699 | /* released on free_profile */ | ||
700 | profile->parent = aa_get_profile(parent); | ||
701 | profile->ns = aa_get_namespace(parent->ns); | ||
702 | |||
703 | write_lock(&profile->ns->lock); | ||
704 | __list_add_profile(&parent->base.profiles, profile); | ||
705 | write_unlock(&profile->ns->lock); | ||
706 | |||
707 | /* refcount released by caller */ | ||
708 | return profile; | ||
709 | |||
710 | fail: | ||
711 | aa_free_sid(sid); | ||
712 | return NULL; | ||
713 | } | ||
714 | |||
715 | /** | ||
716 | * free_profile - free a profile | ||
717 | * @profile: the profile to free (MAYBE NULL) | ||
718 | * | ||
719 | * Free a profile, its hats and null_profile. All references to the profile, | ||
720 | * its hats and null_profile must have been put. | ||
721 | * | ||
722 | * If the profile was referenced from a task context, free_profile() will | ||
723 | * be called from an rcu callback routine, so we must not sleep here. | ||
724 | */ | ||
725 | static void free_profile(struct aa_profile *profile) | ||
726 | { | ||
727 | AA_DEBUG("%s(%p)\n", __func__, profile); | ||
728 | |||
729 | if (!profile) | ||
730 | return; | ||
731 | |||
732 | if (!list_empty(&profile->base.list)) { | ||
733 | AA_ERROR("%s: internal error, " | ||
734 | "profile '%s' still on ns list\n", | ||
735 | __func__, profile->base.name); | ||
736 | BUG(); | ||
737 | } | ||
738 | |||
739 | /* free children profiles */ | ||
740 | policy_destroy(&profile->base); | ||
741 | aa_put_profile(profile->parent); | ||
742 | |||
743 | aa_put_namespace(profile->ns); | ||
744 | kzfree(profile->rename); | ||
745 | |||
746 | aa_free_file_rules(&profile->file); | ||
747 | aa_free_cap_rules(&profile->caps); | ||
748 | aa_free_rlimit_rules(&profile->rlimits); | ||
749 | |||
750 | aa_free_sid(profile->sid); | ||
751 | aa_put_dfa(profile->xmatch); | ||
752 | |||
753 | aa_put_profile(profile->replacedby); | ||
754 | |||
755 | kzfree(profile); | ||
756 | } | ||
757 | |||
758 | /** | ||
759 | * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) | ||
760 | * @kr: kref callback for freeing of a profile (NOT NULL) | ||
761 | */ | ||
762 | void aa_free_profile_kref(struct kref *kref) | ||
763 | { | ||
764 | struct aa_profile *p = container_of(kref, struct aa_profile, | ||
765 | base.count); | ||
766 | |||
767 | free_profile(p); | ||
768 | } | ||
769 | |||
770 | /* TODO: profile accounting - setup in remove */ | ||
771 | |||
772 | /** | ||
773 | * __find_child - find a profile on @head list with a name matching @name | ||
774 | * @head: list to search (NOT NULL) | ||
775 | * @name: name of profile (NOT NULL) | ||
776 | * | ||
777 | * Requires: ns lock protecting list be held | ||
778 | * | ||
779 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
780 | */ | ||
781 | static struct aa_profile *__find_child(struct list_head *head, const char *name) | ||
782 | { | ||
783 | return (struct aa_profile *)__policy_find(head, name); | ||
784 | } | ||
785 | |||
786 | /** | ||
787 | * __strn_find_child - find a profile on @head list using substring of @name | ||
788 | * @head: list to search (NOT NULL) | ||
789 | * @name: name of profile (NOT NULL) | ||
790 | * @len: length of @name substring to match | ||
791 | * | ||
792 | * Requires: ns lock protecting list be held | ||
793 | * | ||
794 | * Returns: unrefcounted profile ptr, or NULL if not found | ||
795 | */ | ||
796 | static struct aa_profile *__strn_find_child(struct list_head *head, | ||
797 | const char *name, int len) | ||
798 | { | ||
799 | return (struct aa_profile *)__policy_strn_find(head, name, len); | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * aa_find_child - find a profile by @name in @parent | ||
804 | * @parent: profile to search (NOT NULL) | ||
805 | * @name: profile name to search for (NOT NULL) | ||
806 | * | ||
807 | * Returns: a refcounted profile or NULL if not found | ||
808 | */ | ||
809 | struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | ||
810 | { | ||
811 | struct aa_profile *profile; | ||
812 | |||
813 | read_lock(&parent->ns->lock); | ||
814 | profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | ||
815 | read_unlock(&parent->ns->lock); | ||
816 | |||
817 | /* refcount released by caller */ | ||
818 | return profile; | ||
819 | } | ||
820 | |||
821 | /** | ||
822 | * __lookup_parent - lookup the parent of a profile of name @hname | ||
823 | * @ns: namespace to lookup profile in (NOT NULL) | ||
824 | * @hname: hierarchical profile name to find parent of (NOT NULL) | ||
825 | * | ||
826 | * Lookups up the parent of a fully qualified profile name, the profile | ||
827 | * that matches hname does not need to exist, in general this | ||
828 | * is used to load a new profile. | ||
829 | * | ||
830 | * Requires: ns->lock be held | ||
831 | * | ||
832 | * Returns: unrefcounted policy or NULL if not found | ||
833 | */ | ||
834 | static struct aa_policy *__lookup_parent(struct aa_namespace *ns, | ||
835 | const char *hname) | ||
836 | { | ||
837 | struct aa_policy *policy; | ||
838 | struct aa_profile *profile = NULL; | ||
839 | char *split; | ||
840 | |||
841 | policy = &ns->base; | ||
842 | |||
843 | for (split = strstr(hname, "//"); split;) { | ||
844 | profile = __strn_find_child(&policy->profiles, hname, | ||
845 | split - hname); | ||
846 | if (!profile) | ||
847 | return NULL; | ||
848 | policy = &profile->base; | ||
849 | hname = split + 2; | ||
850 | split = strstr(hname, "//"); | ||
851 | } | ||
852 | if (!profile) | ||
853 | return &ns->base; | ||
854 | return &profile->base; | ||
855 | } | ||
856 | |||
857 | /** | ||
858 | * __lookup_profile - lookup the profile matching @hname | ||
859 | * @base: base list to start looking up profile name from (NOT NULL) | ||
860 | * @hname: hierarchical profile name (NOT NULL) | ||
861 | * | ||
862 | * Requires: ns->lock be held | ||
863 | * | ||
864 | * Returns: unrefcounted profile pointer or NULL if not found | ||
865 | * | ||
866 | * Do a relative name lookup, recursing through profile tree. | ||
867 | */ | ||
868 | static struct aa_profile *__lookup_profile(struct aa_policy *base, | ||
869 | const char *hname) | ||
870 | { | ||
871 | struct aa_profile *profile = NULL; | ||
872 | char *split; | ||
873 | |||
874 | for (split = strstr(hname, "//"); split;) { | ||
875 | profile = __strn_find_child(&base->profiles, hname, | ||
876 | split - hname); | ||
877 | if (!profile) | ||
878 | return NULL; | ||
879 | |||
880 | base = &profile->base; | ||
881 | hname = split + 2; | ||
882 | split = strstr(hname, "//"); | ||
883 | } | ||
884 | |||
885 | profile = __find_child(&base->profiles, hname); | ||
886 | |||
887 | return profile; | ||
888 | } | ||
889 | |||
890 | /** | ||
891 | * aa_lookup_profile - find a profile by its full or partial name | ||
892 | * @ns: the namespace to start from (NOT NULL) | ||
893 | * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) | ||
894 | * | ||
895 | * Returns: refcounted profile or NULL if not found | ||
896 | */ | ||
897 | struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) | ||
898 | { | ||
899 | struct aa_profile *profile; | ||
900 | |||
901 | read_lock(&ns->lock); | ||
902 | profile = aa_get_profile(__lookup_profile(&ns->base, hname)); | ||
903 | read_unlock(&ns->lock); | ||
904 | |||
905 | /* refcount released by caller */ | ||
906 | return profile; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * replacement_allowed - test to see if replacement is allowed | ||
911 | * @profile: profile to test if it can be replaced (MAYBE NULL) | ||
912 | * @noreplace: true if replacement shouldn't be allowed but addition is okay | ||
913 | * @info: Returns - info about why replacement failed (NOT NULL) | ||
914 | * | ||
915 | * Returns: %0 if replacement allowed else error code | ||
916 | */ | ||
917 | static int replacement_allowed(struct aa_profile *profile, int noreplace, | ||
918 | const char **info) | ||
919 | { | ||
920 | if (profile) { | ||
921 | if (profile->flags & PFLAG_IMMUTABLE) { | ||
922 | *info = "cannot replace immutible profile"; | ||
923 | return -EPERM; | ||
924 | } else if (noreplace) { | ||
925 | *info = "profile already exists"; | ||
926 | return -EEXIST; | ||
927 | } | ||
928 | } | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | /** | ||
933 | * __add_new_profile - simple wrapper around __list_add_profile | ||
934 | * @ns: namespace that profile is being added to (NOT NULL) | ||
935 | * @policy: the policy container to add the profile to (NOT NULL) | ||
936 | * @profile: profile to add (NOT NULL) | ||
937 | * | ||
938 | * add a profile to a list and do other required basic allocations | ||
939 | */ | ||
940 | static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, | ||
941 | struct aa_profile *profile) | ||
942 | { | ||
943 | if (policy != &ns->base) | ||
944 | /* released on profile replacement or free_profile */ | ||
945 | profile->parent = aa_get_profile((struct aa_profile *) policy); | ||
946 | __list_add_profile(&policy->profiles, profile); | ||
947 | /* released on free_profile */ | ||
948 | profile->sid = aa_alloc_sid(); | ||
949 | profile->ns = aa_get_namespace(ns); | ||
950 | } | ||
951 | |||
952 | /** | ||
953 | * aa_audit_policy - Do auditing of policy changes | ||
954 | * @op: policy operation being performed | ||
955 | * @gfp: memory allocation flags | ||
956 | * @name: name of profile being manipulated (NOT NULL) | ||
957 | * @info: any extra information to be audited (MAYBE NULL) | ||
958 | * @error: error code | ||
959 | * | ||
960 | * Returns: the error to be returned after audit is done | ||
961 | */ | ||
962 | static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | ||
963 | int error) | ||
964 | { | ||
965 | struct common_audit_data sa; | ||
966 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
967 | sa.aad.op = op; | ||
968 | sa.aad.name = name; | ||
969 | sa.aad.info = info; | ||
970 | sa.aad.error = error; | ||
971 | |||
972 | return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, | ||
973 | &sa, NULL); | ||
974 | } | ||
975 | |||
976 | /** | ||
977 | * aa_may_manage_policy - can the current task manage policy | ||
978 | * @op: the policy manipulation operation being done | ||
979 | * | ||
980 | * Returns: true if the task is allowed to manipulate policy | ||
981 | */ | ||
982 | bool aa_may_manage_policy(int op) | ||
983 | { | ||
984 | /* check if loading policy is locked out */ | ||
985 | if (aa_g_lock_policy) { | ||
986 | audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); | ||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | if (!capable(CAP_MAC_ADMIN)) { | ||
991 | audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | return 1; | ||
996 | } | ||
997 | |||
998 | /** | ||
999 | * aa_replace_profiles - replace profile(s) on the profile list | ||
1000 | * @udata: serialized data stream (NOT NULL) | ||
1001 | * @size: size of the serialized data stream | ||
1002 | * @noreplace: true if only doing addition, no replacement allowed | ||
1003 | * | ||
1004 | * unpack and replace a profile on the profile list and uses of that profile | ||
1005 | * by any aa_task_cxt. If the profile does not exist on the profile list | ||
1006 | * it is added. | ||
1007 | * | ||
1008 | * Returns: size of data consumed else error code on failure. | ||
1009 | */ | ||
1010 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | ||
1011 | { | ||
1012 | struct aa_policy *policy; | ||
1013 | struct aa_profile *old_profile = NULL, *new_profile = NULL; | ||
1014 | struct aa_profile *rename_profile = NULL; | ||
1015 | struct aa_namespace *ns = NULL; | ||
1016 | const char *ns_name, *name = NULL, *info = NULL; | ||
1017 | int op = OP_PROF_REPL; | ||
1018 | ssize_t error; | ||
1019 | |||
1020 | /* released below */ | ||
1021 | new_profile = aa_unpack(udata, size, &ns_name); | ||
1022 | if (IS_ERR(new_profile)) { | ||
1023 | error = PTR_ERR(new_profile); | ||
1024 | new_profile = NULL; | ||
1025 | goto fail; | ||
1026 | } | ||
1027 | |||
1028 | /* released below */ | ||
1029 | ns = aa_prepare_namespace(ns_name); | ||
1030 | if (!ns) { | ||
1031 | info = "failed to prepare namespace"; | ||
1032 | error = -ENOMEM; | ||
1033 | name = ns_name; | ||
1034 | goto fail; | ||
1035 | } | ||
1036 | |||
1037 | name = new_profile->base.hname; | ||
1038 | |||
1039 | write_lock(&ns->lock); | ||
1040 | /* no ref on policy only use inside lock */ | ||
1041 | policy = __lookup_parent(ns, new_profile->base.hname); | ||
1042 | |||
1043 | if (!policy) { | ||
1044 | info = "parent does not exist"; | ||
1045 | error = -ENOENT; | ||
1046 | goto audit; | ||
1047 | } | ||
1048 | |||
1049 | old_profile = __find_child(&policy->profiles, new_profile->base.name); | ||
1050 | /* released below */ | ||
1051 | aa_get_profile(old_profile); | ||
1052 | |||
1053 | if (new_profile->rename) { | ||
1054 | rename_profile = __lookup_profile(&ns->base, | ||
1055 | new_profile->rename); | ||
1056 | /* released below */ | ||
1057 | aa_get_profile(rename_profile); | ||
1058 | |||
1059 | if (!rename_profile) { | ||
1060 | info = "profile to rename does not exist"; | ||
1061 | name = new_profile->rename; | ||
1062 | error = -ENOENT; | ||
1063 | goto audit; | ||
1064 | } | ||
1065 | } | ||
1066 | |||
1067 | error = replacement_allowed(old_profile, noreplace, &info); | ||
1068 | if (error) | ||
1069 | goto audit; | ||
1070 | |||
1071 | error = replacement_allowed(rename_profile, noreplace, &info); | ||
1072 | if (error) | ||
1073 | goto audit; | ||
1074 | |||
1075 | audit: | ||
1076 | if (!old_profile && !rename_profile) | ||
1077 | op = OP_PROF_LOAD; | ||
1078 | |||
1079 | error = audit_policy(op, GFP_ATOMIC, name, info, error); | ||
1080 | |||
1081 | if (!error) { | ||
1082 | if (rename_profile) | ||
1083 | __replace_profile(rename_profile, new_profile); | ||
1084 | if (old_profile) { | ||
1085 | /* when there are both rename and old profiles | ||
1086 | * inherit old profiles sid | ||
1087 | */ | ||
1088 | if (rename_profile) | ||
1089 | aa_free_sid(new_profile->sid); | ||
1090 | __replace_profile(old_profile, new_profile); | ||
1091 | } | ||
1092 | if (!(old_profile || rename_profile)) | ||
1093 | __add_new_profile(ns, policy, new_profile); | ||
1094 | } | ||
1095 | write_unlock(&ns->lock); | ||
1096 | |||
1097 | out: | ||
1098 | aa_put_namespace(ns); | ||
1099 | aa_put_profile(rename_profile); | ||
1100 | aa_put_profile(old_profile); | ||
1101 | aa_put_profile(new_profile); | ||
1102 | if (error) | ||
1103 | return error; | ||
1104 | return size; | ||
1105 | |||
1106 | fail: | ||
1107 | error = audit_policy(op, GFP_KERNEL, name, info, error); | ||
1108 | goto out; | ||
1109 | } | ||
1110 | |||
1111 | /** | ||
1112 | * aa_remove_profiles - remove profile(s) from the system | ||
1113 | * @fqname: name of the profile or namespace to remove (NOT NULL) | ||
1114 | * @size: size of the name | ||
1115 | * | ||
1116 | * Remove a profile or sub namespace from the current namespace, so that | ||
1117 | * they can not be found anymore and mark them as replaced by unconfined | ||
1118 | * | ||
1119 | * NOTE: removing confinement does not restore rlimits to preconfinemnet values | ||
1120 | * | ||
1121 | * Returns: size of data consume else error code if fails | ||
1122 | */ | ||
1123 | ssize_t aa_remove_profiles(char *fqname, size_t size) | ||
1124 | { | ||
1125 | struct aa_namespace *root, *ns = NULL; | ||
1126 | struct aa_profile *profile = NULL; | ||
1127 | const char *name = fqname, *info = NULL; | ||
1128 | ssize_t error = 0; | ||
1129 | |||
1130 | if (*fqname == 0) { | ||
1131 | info = "no profile specified"; | ||
1132 | error = -ENOENT; | ||
1133 | goto fail; | ||
1134 | } | ||
1135 | |||
1136 | root = aa_current_profile()->ns; | ||
1137 | |||
1138 | if (fqname[0] == ':') { | ||
1139 | char *ns_name; | ||
1140 | name = aa_split_fqname(fqname, &ns_name); | ||
1141 | if (ns_name) { | ||
1142 | /* released below */ | ||
1143 | ns = aa_find_namespace(root, ns_name); | ||
1144 | if (!ns) { | ||
1145 | info = "namespace does not exist"; | ||
1146 | error = -ENOENT; | ||
1147 | goto fail; | ||
1148 | } | ||
1149 | } | ||
1150 | } else | ||
1151 | /* released below */ | ||
1152 | ns = aa_get_namespace(root); | ||
1153 | |||
1154 | write_lock(&ns->lock); | ||
1155 | if (!name) { | ||
1156 | /* remove namespace - can only happen if fqname[0] == ':' */ | ||
1157 | __remove_namespace(ns); | ||
1158 | } else { | ||
1159 | /* remove profile */ | ||
1160 | profile = aa_get_profile(__lookup_profile(&ns->base, name)); | ||
1161 | if (!profile) { | ||
1162 | error = -ENOENT; | ||
1163 | info = "profile does not exist"; | ||
1164 | goto fail_ns_lock; | ||
1165 | } | ||
1166 | name = profile->base.hname; | ||
1167 | __remove_profile(profile); | ||
1168 | } | ||
1169 | write_unlock(&ns->lock); | ||
1170 | |||
1171 | /* don't fail removal if audit fails */ | ||
1172 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
1173 | aa_put_namespace(ns); | ||
1174 | aa_put_profile(profile); | ||
1175 | return size; | ||
1176 | |||
1177 | fail_ns_lock: | ||
1178 | write_unlock(&ns->lock); | ||
1179 | aa_put_namespace(ns); | ||
1180 | |||
1181 | fail: | ||
1182 | (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); | ||
1183 | return error; | ||
1184 | } | ||
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c new file mode 100644 index 00000000000..eb3700e9fd3 --- /dev/null +++ b/security/apparmor/policy_unpack.c | |||
@@ -0,0 +1,703 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor functions for unpacking policy loaded from | ||
5 | * userspace. | ||
6 | * | ||
7 | * Copyright (C) 1998-2008 Novell/SUSE | ||
8 | * Copyright 2009-2010 Canonical Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2 of the | ||
13 | * License. | ||
14 | * | ||
15 | * AppArmor uses a serialized binary format for loading policy. | ||
16 | * To find policy format documentation look in Documentation/apparmor.txt | ||
17 | * All policy is validated before it is used. | ||
18 | */ | ||
19 | |||
20 | #include <asm/unaligned.h> | ||
21 | #include <linux/ctype.h> | ||
22 | #include <linux/errno.h> | ||
23 | |||
24 | #include "include/apparmor.h" | ||
25 | #include "include/audit.h" | ||
26 | #include "include/context.h" | ||
27 | #include "include/match.h" | ||
28 | #include "include/policy.h" | ||
29 | #include "include/policy_unpack.h" | ||
30 | #include "include/sid.h" | ||
31 | |||
32 | /* | ||
33 | * The AppArmor interface treats data as a type byte followed by the | ||
34 | * actual data. The interface has the notion of a a named entry | ||
35 | * which has a name (AA_NAME typecode followed by name string) followed by | ||
36 | * the entries typecode and data. Named types allow for optional | ||
37 | * elements and extensions to be added and tested for without breaking | ||
38 | * backwards compatibility. | ||
39 | */ | ||
40 | |||
41 | enum aa_code { | ||
42 | AA_U8, | ||
43 | AA_U16, | ||
44 | AA_U32, | ||
45 | AA_U64, | ||
46 | AA_NAME, /* same as string except it is items name */ | ||
47 | AA_STRING, | ||
48 | AA_BLOB, | ||
49 | AA_STRUCT, | ||
50 | AA_STRUCTEND, | ||
51 | AA_LIST, | ||
52 | AA_LISTEND, | ||
53 | AA_ARRAY, | ||
54 | AA_ARRAYEND, | ||
55 | }; | ||
56 | |||
57 | /* | ||
58 | * aa_ext is the read of the buffer containing the serialized profile. The | ||
59 | * data is copied into a kernel buffer in apparmorfs and then handed off to | ||
60 | * the unpack routines. | ||
61 | */ | ||
62 | struct aa_ext { | ||
63 | void *start; | ||
64 | void *end; | ||
65 | void *pos; /* pointer to current position in the buffer */ | ||
66 | u32 version; | ||
67 | }; | ||
68 | |||
69 | /* audit callback for unpack fields */ | ||
70 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
71 | { | ||
72 | struct common_audit_data *sa = va; | ||
73 | if (sa->aad.iface.target) { | ||
74 | struct aa_profile *name = sa->aad.iface.target; | ||
75 | audit_log_format(ab, " name="); | ||
76 | audit_log_untrustedstring(ab, name->base.hname); | ||
77 | } | ||
78 | if (sa->aad.iface.pos) | ||
79 | audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * audit_iface - do audit message for policy unpacking/load/replace/remove | ||
84 | * @new: profile if it has been allocated (MAYBE NULL) | ||
85 | * @name: name of the profile being manipulated (MAYBE NULL) | ||
86 | * @info: any extra info about the failure (MAYBE NULL) | ||
87 | * @e: buffer position info (NOT NULL) | ||
88 | * @error: error code | ||
89 | * | ||
90 | * Returns: %0 or error | ||
91 | */ | ||
92 | static int audit_iface(struct aa_profile *new, const char *name, | ||
93 | const char *info, struct aa_ext *e, int error) | ||
94 | { | ||
95 | struct aa_profile *profile = __aa_current_profile(); | ||
96 | struct common_audit_data sa; | ||
97 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
98 | sa.aad.iface.pos = e->pos - e->start; | ||
99 | sa.aad.iface.target = new; | ||
100 | sa.aad.name = name; | ||
101 | sa.aad.info = info; | ||
102 | sa.aad.error = error; | ||
103 | |||
104 | return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, | ||
105 | audit_cb); | ||
106 | } | ||
107 | |||
108 | /* test if read will be in packed data bounds */ | ||
109 | static bool inbounds(struct aa_ext *e, size_t size) | ||
110 | { | ||
111 | return (size <= e->end - e->pos); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * aa_u16_chunck - test and do bounds checking for a u16 size based chunk | ||
116 | * @e: serialized data read head (NOT NULL) | ||
117 | * @chunk: start address for chunk of data (NOT NULL) | ||
118 | * | ||
119 | * Returns: the size of chunk found with the read head at the end of the chunk. | ||
120 | */ | ||
121 | static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) | ||
122 | { | ||
123 | size_t size = 0; | ||
124 | |||
125 | if (!inbounds(e, sizeof(u16))) | ||
126 | return 0; | ||
127 | size = le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
128 | e->pos += sizeof(u16); | ||
129 | if (!inbounds(e, size)) | ||
130 | return 0; | ||
131 | *chunk = e->pos; | ||
132 | e->pos += size; | ||
133 | return size; | ||
134 | } | ||
135 | |||
136 | /* unpack control byte */ | ||
137 | static bool unpack_X(struct aa_ext *e, enum aa_code code) | ||
138 | { | ||
139 | if (!inbounds(e, 1)) | ||
140 | return 0; | ||
141 | if (*(u8 *) e->pos != code) | ||
142 | return 0; | ||
143 | e->pos++; | ||
144 | return 1; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * unpack_nameX - check is the next element is of type X with a name of @name | ||
149 | * @e: serialized data extent information (NOT NULL) | ||
150 | * @code: type code | ||
151 | * @name: name to match to the serialized element. (MAYBE NULL) | ||
152 | * | ||
153 | * check that the next serialized data element is of type X and has a tag | ||
154 | * name @name. If @name is specified then there must be a matching | ||
155 | * name element in the stream. If @name is NULL any name element will be | ||
156 | * skipped and only the typecode will be tested. | ||
157 | * | ||
158 | * Returns 1 on success (both type code and name tests match) and the read | ||
159 | * head is advanced past the headers | ||
160 | * | ||
161 | * Returns: 0 if either match fails, the read head does not move | ||
162 | */ | ||
163 | static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) | ||
164 | { | ||
165 | /* | ||
166 | * May need to reset pos if name or type doesn't match | ||
167 | */ | ||
168 | void *pos = e->pos; | ||
169 | /* | ||
170 | * Check for presence of a tagname, and if present name size | ||
171 | * AA_NAME tag value is a u16. | ||
172 | */ | ||
173 | if (unpack_X(e, AA_NAME)) { | ||
174 | char *tag = NULL; | ||
175 | size_t size = unpack_u16_chunk(e, &tag); | ||
176 | /* if a name is specified it must match. otherwise skip tag */ | ||
177 | if (name && (!size || strcmp(name, tag))) | ||
178 | goto fail; | ||
179 | } else if (name) { | ||
180 | /* if a name is specified and there is no name tag fail */ | ||
181 | goto fail; | ||
182 | } | ||
183 | |||
184 | /* now check if type code matches */ | ||
185 | if (unpack_X(e, code)) | ||
186 | return 1; | ||
187 | |||
188 | fail: | ||
189 | e->pos = pos; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) | ||
194 | { | ||
195 | if (unpack_nameX(e, AA_U32, name)) { | ||
196 | if (!inbounds(e, sizeof(u32))) | ||
197 | return 0; | ||
198 | if (data) | ||
199 | *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
200 | e->pos += sizeof(u32); | ||
201 | return 1; | ||
202 | } | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) | ||
207 | { | ||
208 | if (unpack_nameX(e, AA_U64, name)) { | ||
209 | if (!inbounds(e, sizeof(u64))) | ||
210 | return 0; | ||
211 | if (data) | ||
212 | *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); | ||
213 | e->pos += sizeof(u64); | ||
214 | return 1; | ||
215 | } | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static size_t unpack_array(struct aa_ext *e, const char *name) | ||
220 | { | ||
221 | if (unpack_nameX(e, AA_ARRAY, name)) { | ||
222 | int size; | ||
223 | if (!inbounds(e, sizeof(u16))) | ||
224 | return 0; | ||
225 | size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); | ||
226 | e->pos += sizeof(u16); | ||
227 | return size; | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) | ||
233 | { | ||
234 | if (unpack_nameX(e, AA_BLOB, name)) { | ||
235 | u32 size; | ||
236 | if (!inbounds(e, sizeof(u32))) | ||
237 | return 0; | ||
238 | size = le32_to_cpu(get_unaligned((u32 *) e->pos)); | ||
239 | e->pos += sizeof(u32); | ||
240 | if (inbounds(e, (size_t) size)) { | ||
241 | *blob = e->pos; | ||
242 | e->pos += size; | ||
243 | return size; | ||
244 | } | ||
245 | } | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int unpack_str(struct aa_ext *e, const char **string, const char *name) | ||
250 | { | ||
251 | char *src_str; | ||
252 | size_t size = 0; | ||
253 | void *pos = e->pos; | ||
254 | *string = NULL; | ||
255 | if (unpack_nameX(e, AA_STRING, name)) { | ||
256 | size = unpack_u16_chunk(e, &src_str); | ||
257 | if (size) { | ||
258 | /* strings are null terminated, length is size - 1 */ | ||
259 | if (src_str[size - 1] != 0) | ||
260 | goto fail; | ||
261 | *string = src_str; | ||
262 | } | ||
263 | } | ||
264 | return size; | ||
265 | |||
266 | fail: | ||
267 | e->pos = pos; | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int unpack_strdup(struct aa_ext *e, char **string, const char *name) | ||
272 | { | ||
273 | const char *tmp; | ||
274 | void *pos = e->pos; | ||
275 | int res = unpack_str(e, &tmp, name); | ||
276 | *string = NULL; | ||
277 | |||
278 | if (!res) | ||
279 | return 0; | ||
280 | |||
281 | *string = kmemdup(tmp, res, GFP_KERNEL); | ||
282 | if (!*string) { | ||
283 | e->pos = pos; | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | return res; | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * verify_accept - verify the accept tables of a dfa | ||
292 | * @dfa: dfa to verify accept tables of (NOT NULL) | ||
293 | * @flags: flags governing dfa | ||
294 | * | ||
295 | * Returns: 1 if valid accept tables else 0 if error | ||
296 | */ | ||
297 | static bool verify_accept(struct aa_dfa *dfa, int flags) | ||
298 | { | ||
299 | int i; | ||
300 | |||
301 | /* verify accept permissions */ | ||
302 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
303 | int mode = ACCEPT_TABLE(dfa)[i]; | ||
304 | |||
305 | if (mode & ~DFA_VALID_PERM_MASK) | ||
306 | return 0; | ||
307 | |||
308 | if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) | ||
309 | return 0; | ||
310 | } | ||
311 | return 1; | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * unpack_dfa - unpack a file rule dfa | ||
316 | * @e: serialized data extent information (NOT NULL) | ||
317 | * | ||
318 | * returns dfa or ERR_PTR or NULL if no dfa | ||
319 | */ | ||
320 | static struct aa_dfa *unpack_dfa(struct aa_ext *e) | ||
321 | { | ||
322 | char *blob = NULL; | ||
323 | size_t size; | ||
324 | struct aa_dfa *dfa = NULL; | ||
325 | |||
326 | size = unpack_blob(e, &blob, "aadfa"); | ||
327 | if (size) { | ||
328 | /* | ||
329 | * The dfa is aligned with in the blob to 8 bytes | ||
330 | * from the beginning of the stream. | ||
331 | */ | ||
332 | size_t sz = blob - (char *)e->start; | ||
333 | size_t pad = ALIGN(sz, 8) - sz; | ||
334 | int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | | ||
335 | TO_ACCEPT2_FLAG(YYTD_DATA32); | ||
336 | |||
337 | |||
338 | if (aa_g_paranoid_load) | ||
339 | flags |= DFA_FLAG_VERIFY_STATES; | ||
340 | |||
341 | dfa = aa_dfa_unpack(blob + pad, size - pad, flags); | ||
342 | |||
343 | if (IS_ERR(dfa)) | ||
344 | return dfa; | ||
345 | |||
346 | if (!verify_accept(dfa, flags)) | ||
347 | goto fail; | ||
348 | } | ||
349 | |||
350 | return dfa; | ||
351 | |||
352 | fail: | ||
353 | aa_put_dfa(dfa); | ||
354 | return ERR_PTR(-EPROTO); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * unpack_trans_table - unpack a profile transition table | ||
359 | * @e: serialized data extent information (NOT NULL) | ||
360 | * @profile: profile to add the accept table to (NOT NULL) | ||
361 | * | ||
362 | * Returns: 1 if table succesfully unpacked | ||
363 | */ | ||
364 | static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) | ||
365 | { | ||
366 | void *pos = e->pos; | ||
367 | |||
368 | /* exec table is optional */ | ||
369 | if (unpack_nameX(e, AA_STRUCT, "xtable")) { | ||
370 | int i, size; | ||
371 | |||
372 | size = unpack_array(e, NULL); | ||
373 | /* currently 4 exec bits and entries 0-3 are reserved iupcx */ | ||
374 | if (size > 16 - 4) | ||
375 | goto fail; | ||
376 | profile->file.trans.table = kzalloc(sizeof(char *) * size, | ||
377 | GFP_KERNEL); | ||
378 | if (!profile->file.trans.table) | ||
379 | goto fail; | ||
380 | |||
381 | profile->file.trans.size = size; | ||
382 | for (i = 0; i < size; i++) { | ||
383 | char *str; | ||
384 | int c, j, size = unpack_strdup(e, &str, NULL); | ||
385 | /* unpack_strdup verifies that the last character is | ||
386 | * null termination byte. | ||
387 | */ | ||
388 | if (!size) | ||
389 | goto fail; | ||
390 | profile->file.trans.table[i] = str; | ||
391 | /* verify that name doesn't start with space */ | ||
392 | if (isspace(*str)) | ||
393 | goto fail; | ||
394 | |||
395 | /* count internal # of internal \0 */ | ||
396 | for (c = j = 0; j < size - 2; j++) { | ||
397 | if (!str[j]) | ||
398 | c++; | ||
399 | } | ||
400 | if (*str == ':') { | ||
401 | /* beginning with : requires an embedded \0, | ||
402 | * verify that exactly 1 internal \0 exists | ||
403 | * trailing \0 already verified by unpack_strdup | ||
404 | */ | ||
405 | if (c != 1) | ||
406 | goto fail; | ||
407 | /* first character after : must be valid */ | ||
408 | if (!str[1]) | ||
409 | goto fail; | ||
410 | } else if (c) | ||
411 | /* fail - all other cases with embedded \0 */ | ||
412 | goto fail; | ||
413 | } | ||
414 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
415 | goto fail; | ||
416 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
417 | goto fail; | ||
418 | } | ||
419 | return 1; | ||
420 | |||
421 | fail: | ||
422 | aa_free_domain_entries(&profile->file.trans); | ||
423 | e->pos = pos; | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) | ||
428 | { | ||
429 | void *pos = e->pos; | ||
430 | |||
431 | /* rlimits are optional */ | ||
432 | if (unpack_nameX(e, AA_STRUCT, "rlimits")) { | ||
433 | int i, size; | ||
434 | u32 tmp = 0; | ||
435 | if (!unpack_u32(e, &tmp, NULL)) | ||
436 | goto fail; | ||
437 | profile->rlimits.mask = tmp; | ||
438 | |||
439 | size = unpack_array(e, NULL); | ||
440 | if (size > RLIM_NLIMITS) | ||
441 | goto fail; | ||
442 | for (i = 0; i < size; i++) { | ||
443 | u64 tmp = 0; | ||
444 | int a = aa_map_resource(i); | ||
445 | if (!unpack_u64(e, &tmp, NULL)) | ||
446 | goto fail; | ||
447 | profile->rlimits.limits[a].rlim_max = tmp; | ||
448 | } | ||
449 | if (!unpack_nameX(e, AA_ARRAYEND, NULL)) | ||
450 | goto fail; | ||
451 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
452 | goto fail; | ||
453 | } | ||
454 | return 1; | ||
455 | |||
456 | fail: | ||
457 | e->pos = pos; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * unpack_profile - unpack a serialized profile | ||
463 | * @e: serialized data extent information (NOT NULL) | ||
464 | * | ||
465 | * NOTE: unpack profile sets audit struct if there is a failure | ||
466 | */ | ||
467 | static struct aa_profile *unpack_profile(struct aa_ext *e) | ||
468 | { | ||
469 | struct aa_profile *profile = NULL; | ||
470 | const char *name = NULL; | ||
471 | int error = -EPROTO; | ||
472 | kernel_cap_t tmpcap; | ||
473 | u32 tmp; | ||
474 | |||
475 | /* check that we have the right struct being passed */ | ||
476 | if (!unpack_nameX(e, AA_STRUCT, "profile")) | ||
477 | goto fail; | ||
478 | if (!unpack_str(e, &name, NULL)) | ||
479 | goto fail; | ||
480 | |||
481 | profile = aa_alloc_profile(name); | ||
482 | if (!profile) | ||
483 | return ERR_PTR(-ENOMEM); | ||
484 | |||
485 | /* profile renaming is optional */ | ||
486 | (void) unpack_str(e, &profile->rename, "rename"); | ||
487 | |||
488 | /* xmatch is optional and may be NULL */ | ||
489 | profile->xmatch = unpack_dfa(e); | ||
490 | if (IS_ERR(profile->xmatch)) { | ||
491 | error = PTR_ERR(profile->xmatch); | ||
492 | profile->xmatch = NULL; | ||
493 | goto fail; | ||
494 | } | ||
495 | /* xmatch_len is not optional if xmatch is set */ | ||
496 | if (profile->xmatch) { | ||
497 | if (!unpack_u32(e, &tmp, NULL)) | ||
498 | goto fail; | ||
499 | profile->xmatch_len = tmp; | ||
500 | } | ||
501 | |||
502 | /* per profile debug flags (complain, audit) */ | ||
503 | if (!unpack_nameX(e, AA_STRUCT, "flags")) | ||
504 | goto fail; | ||
505 | if (!unpack_u32(e, &tmp, NULL)) | ||
506 | goto fail; | ||
507 | if (tmp) | ||
508 | profile->flags |= PFLAG_HAT; | ||
509 | if (!unpack_u32(e, &tmp, NULL)) | ||
510 | goto fail; | ||
511 | if (tmp) | ||
512 | profile->mode = APPARMOR_COMPLAIN; | ||
513 | if (!unpack_u32(e, &tmp, NULL)) | ||
514 | goto fail; | ||
515 | if (tmp) | ||
516 | profile->audit = AUDIT_ALL; | ||
517 | |||
518 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
519 | goto fail; | ||
520 | |||
521 | /* path_flags is optional */ | ||
522 | if (unpack_u32(e, &profile->path_flags, "path_flags")) | ||
523 | profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED; | ||
524 | else | ||
525 | /* set a default value if path_flags field is not present */ | ||
526 | profile->path_flags = PFLAG_MEDIATE_DELETED; | ||
527 | |||
528 | if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) | ||
529 | goto fail; | ||
530 | if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) | ||
531 | goto fail; | ||
532 | if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) | ||
533 | goto fail; | ||
534 | if (!unpack_u32(e, &tmpcap.cap[0], NULL)) | ||
535 | goto fail; | ||
536 | |||
537 | if (unpack_nameX(e, AA_STRUCT, "caps64")) { | ||
538 | /* optional upper half of 64 bit caps */ | ||
539 | if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) | ||
540 | goto fail; | ||
541 | if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) | ||
542 | goto fail; | ||
543 | if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) | ||
544 | goto fail; | ||
545 | if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) | ||
546 | goto fail; | ||
547 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
548 | goto fail; | ||
549 | } | ||
550 | |||
551 | if (unpack_nameX(e, AA_STRUCT, "capsx")) { | ||
552 | /* optional extended caps mediation mask */ | ||
553 | if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) | ||
554 | goto fail; | ||
555 | if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) | ||
556 | goto fail; | ||
557 | } | ||
558 | |||
559 | if (!unpack_rlimits(e, profile)) | ||
560 | goto fail; | ||
561 | |||
562 | /* get file rules */ | ||
563 | profile->file.dfa = unpack_dfa(e); | ||
564 | if (IS_ERR(profile->file.dfa)) { | ||
565 | error = PTR_ERR(profile->file.dfa); | ||
566 | profile->file.dfa = NULL; | ||
567 | goto fail; | ||
568 | } | ||
569 | |||
570 | if (!unpack_u32(e, &profile->file.start, "dfa_start")) | ||
571 | /* default start state */ | ||
572 | profile->file.start = DFA_START; | ||
573 | |||
574 | if (!unpack_trans_table(e, profile)) | ||
575 | goto fail; | ||
576 | |||
577 | if (!unpack_nameX(e, AA_STRUCTEND, NULL)) | ||
578 | goto fail; | ||
579 | |||
580 | return profile; | ||
581 | |||
582 | fail: | ||
583 | if (profile) | ||
584 | name = NULL; | ||
585 | else if (!name) | ||
586 | name = "unknown"; | ||
587 | audit_iface(profile, name, "failed to unpack profile", e, error); | ||
588 | aa_put_profile(profile); | ||
589 | |||
590 | return ERR_PTR(error); | ||
591 | } | ||
592 | |||
593 | /** | ||
594 | * verify_head - unpack serialized stream header | ||
595 | * @e: serialized data read head (NOT NULL) | ||
596 | * @ns: Returns - namespace if one is specified else NULL (NOT NULL) | ||
597 | * | ||
598 | * Returns: error or 0 if header is good | ||
599 | */ | ||
600 | static int verify_header(struct aa_ext *e, const char **ns) | ||
601 | { | ||
602 | int error = -EPROTONOSUPPORT; | ||
603 | /* get the interface version */ | ||
604 | if (!unpack_u32(e, &e->version, "version")) { | ||
605 | audit_iface(NULL, NULL, "invalid profile format", e, error); | ||
606 | return error; | ||
607 | } | ||
608 | |||
609 | /* check that the interface version is currently supported */ | ||
610 | if (e->version != 5) { | ||
611 | audit_iface(NULL, NULL, "unsupported interface version", e, | ||
612 | error); | ||
613 | return error; | ||
614 | } | ||
615 | |||
616 | /* read the namespace if present */ | ||
617 | if (!unpack_str(e, ns, "namespace")) | ||
618 | *ns = NULL; | ||
619 | |||
620 | return 0; | ||
621 | } | ||
622 | |||
623 | static bool verify_xindex(int xindex, int table_size) | ||
624 | { | ||
625 | int index, xtype; | ||
626 | xtype = xindex & AA_X_TYPE_MASK; | ||
627 | index = xindex & AA_X_INDEX_MASK; | ||
628 | if (xtype == AA_X_TABLE && index > table_size) | ||
629 | return 0; | ||
630 | return 1; | ||
631 | } | ||
632 | |||
633 | /* verify dfa xindexes are in range of transition tables */ | ||
634 | static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) | ||
635 | { | ||
636 | int i; | ||
637 | for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { | ||
638 | if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) | ||
639 | return 0; | ||
640 | if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) | ||
641 | return 0; | ||
642 | } | ||
643 | return 1; | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * verify_profile - Do post unpack analysis to verify profile consistency | ||
648 | * @profile: profile to verify (NOT NULL) | ||
649 | * | ||
650 | * Returns: 0 if passes verification else error | ||
651 | */ | ||
652 | static int verify_profile(struct aa_profile *profile) | ||
653 | { | ||
654 | if (aa_g_paranoid_load) { | ||
655 | if (profile->file.dfa && | ||
656 | !verify_dfa_xindex(profile->file.dfa, | ||
657 | profile->file.trans.size)) { | ||
658 | audit_iface(profile, NULL, "Invalid named transition", | ||
659 | NULL, -EPROTO); | ||
660 | return -EPROTO; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | /** | ||
668 | * aa_unpack - unpack packed binary profile data loaded from user space | ||
669 | * @udata: user data copied to kmem (NOT NULL) | ||
670 | * @size: the size of the user data | ||
671 | * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) | ||
672 | * | ||
673 | * Unpack user data and return refcounted allocated profile or ERR_PTR | ||
674 | * | ||
675 | * Returns: profile else error pointer if fails to unpack | ||
676 | */ | ||
677 | struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) | ||
678 | { | ||
679 | struct aa_profile *profile = NULL; | ||
680 | int error; | ||
681 | struct aa_ext e = { | ||
682 | .start = udata, | ||
683 | .end = udata + size, | ||
684 | .pos = udata, | ||
685 | }; | ||
686 | |||
687 | error = verify_header(&e, ns); | ||
688 | if (error) | ||
689 | return ERR_PTR(error); | ||
690 | |||
691 | profile = unpack_profile(&e); | ||
692 | if (IS_ERR(profile)) | ||
693 | return profile; | ||
694 | |||
695 | error = verify_profile(profile); | ||
696 | if (error) { | ||
697 | aa_put_profile(profile); | ||
698 | profile = ERR_PTR(error); | ||
699 | } | ||
700 | |||
701 | /* return refcount */ | ||
702 | return profile; | ||
703 | } | ||
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c new file mode 100644 index 00000000000..04a2cf8d1b6 --- /dev/null +++ b/security/apparmor/procattr.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor /proc/<pid>/attr/ interface functions | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include "include/apparmor.h" | ||
16 | #include "include/context.h" | ||
17 | #include "include/policy.h" | ||
18 | #include "include/domain.h" | ||
19 | |||
20 | |||
21 | /** | ||
22 | * aa_getprocattr - Return the profile information for @profile | ||
23 | * @profile: the profile to print profile info about (NOT NULL) | ||
24 | * @string: Returns - string containing the profile info (NOT NULL) | ||
25 | * | ||
26 | * Returns: length of @string on success else error on failure | ||
27 | * | ||
28 | * Requires: profile != NULL | ||
29 | * | ||
30 | * Creates a string containing the namespace_name://profile_name for | ||
31 | * @profile. | ||
32 | * | ||
33 | * Returns: size of string placed in @string else error code on failure | ||
34 | */ | ||
35 | int aa_getprocattr(struct aa_profile *profile, char **string) | ||
36 | { | ||
37 | char *str; | ||
38 | int len = 0, mode_len = 0, ns_len = 0, name_len; | ||
39 | const char *mode_str = profile_mode_names[profile->mode]; | ||
40 | const char *ns_name = NULL; | ||
41 | struct aa_namespace *ns = profile->ns; | ||
42 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | ||
43 | char *s; | ||
44 | |||
45 | if (!aa_ns_visible(current_ns, ns)) | ||
46 | return -EACCES; | ||
47 | |||
48 | ns_name = aa_ns_name(current_ns, ns); | ||
49 | ns_len = strlen(ns_name); | ||
50 | |||
51 | /* if the visible ns_name is > 0 increase size for : :// seperator */ | ||
52 | if (ns_len) | ||
53 | ns_len += 4; | ||
54 | |||
55 | /* unconfined profiles don't have a mode string appended */ | ||
56 | if (!unconfined(profile)) | ||
57 | mode_len = strlen(mode_str) + 3; /* + 3 for _() */ | ||
58 | |||
59 | name_len = strlen(profile->base.hname); | ||
60 | len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ | ||
61 | s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ | ||
62 | if (!str) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | if (ns_len) { | ||
66 | /* skip over prefix current_ns->base.hname and separating // */ | ||
67 | sprintf(s, ":%s://", ns_name); | ||
68 | s += ns_len; | ||
69 | } | ||
70 | if (unconfined(profile)) | ||
71 | /* mode string not being appended */ | ||
72 | sprintf(s, "%s\n", profile->base.hname); | ||
73 | else | ||
74 | sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); | ||
75 | *string = str; | ||
76 | |||
77 | /* NOTE: len does not include \0 of string, not saved as part of file */ | ||
78 | return len; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * split_token_from_name - separate a string of form <token>^<name> | ||
83 | * @op: operation being checked | ||
84 | * @args: string to parse (NOT NULL) | ||
85 | * @token: stores returned parsed token value (NOT NULL) | ||
86 | * | ||
87 | * Returns: start position of name after token else NULL on failure | ||
88 | */ | ||
89 | static char *split_token_from_name(int op, char *args, u64 * token) | ||
90 | { | ||
91 | char *name; | ||
92 | |||
93 | *token = simple_strtoull(args, &name, 16); | ||
94 | if ((name == args) || *name != '^') { | ||
95 | AA_ERROR("%s: Invalid input '%s'", op_table[op], args); | ||
96 | return ERR_PTR(-EINVAL); | ||
97 | } | ||
98 | |||
99 | name++; /* skip ^ */ | ||
100 | if (!*name) | ||
101 | name = NULL; | ||
102 | return name; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * aa_setprocattr_chagnehat - handle procattr interface to change_hat | ||
107 | * @args: args received from writing to /proc/<pid>/attr/current (NOT NULL) | ||
108 | * @size: size of the args | ||
109 | * @test: true if this is a test of change_hat permissions | ||
110 | * | ||
111 | * Returns: %0 or error code if change_hat fails | ||
112 | */ | ||
113 | int aa_setprocattr_changehat(char *args, size_t size, int test) | ||
114 | { | ||
115 | char *hat; | ||
116 | u64 token; | ||
117 | const char *hats[16]; /* current hard limit on # of names */ | ||
118 | int count = 0; | ||
119 | |||
120 | hat = split_token_from_name(OP_CHANGE_HAT, args, &token); | ||
121 | if (IS_ERR(hat)) | ||
122 | return PTR_ERR(hat); | ||
123 | |||
124 | if (!hat && !token) { | ||
125 | AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | if (hat) { | ||
130 | /* set up hat name vector, args guaranteed null terminated | ||
131 | * at args[size] by setprocattr. | ||
132 | * | ||
133 | * If there are multiple hat names in the buffer each is | ||
134 | * separated by a \0. Ie. userspace writes them pre tokenized | ||
135 | */ | ||
136 | char *end = args + size; | ||
137 | for (count = 0; (hat < end) && count < 16; ++count) { | ||
138 | char *next = hat + strlen(hat) + 1; | ||
139 | hats[count] = hat; | ||
140 | hat = next; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", | ||
145 | __func__, token, hat ? hat : NULL); | ||
146 | |||
147 | return aa_change_hat(hats, count, token, test); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * aa_setprocattr_changeprofile - handle procattr interface to changeprofile | ||
152 | * @fqname: args received from writting to /proc/<pid>/attr/current (NOT NULL) | ||
153 | * @onexec: true if change_profile should be delayed until exec | ||
154 | * @test: true if this is a test of change_profile permissions | ||
155 | * | ||
156 | * Returns: %0 or error code if change_profile fails | ||
157 | */ | ||
158 | int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) | ||
159 | { | ||
160 | char *name, *ns_name; | ||
161 | |||
162 | name = aa_split_fqname(fqname, &ns_name); | ||
163 | return aa_change_profile(ns_name, name, onexec, test); | ||
164 | } | ||
165 | |||
166 | int aa_setprocattr_permipc(char *fqname) | ||
167 | { | ||
168 | /* TODO: add ipc permission querying */ | ||
169 | return -ENOTSUPP; | ||
170 | } | ||
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c new file mode 100644 index 00000000000..4a368f1fd36 --- /dev/null +++ b/security/apparmor/resource.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor resource mediation and attachment | ||
5 | * | ||
6 | * Copyright (C) 1998-2008 Novell/SUSE | ||
7 | * Copyright 2009-2010 Canonical Ltd. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License as | ||
11 | * published by the Free Software Foundation, version 2 of the | ||
12 | * License. | ||
13 | */ | ||
14 | |||
15 | #include <linux/audit.h> | ||
16 | |||
17 | #include "include/audit.h" | ||
18 | #include "include/resource.h" | ||
19 | #include "include/policy.h" | ||
20 | |||
21 | /* | ||
22 | * Table of rlimit names: we generate it from resource.h. | ||
23 | */ | ||
24 | #include "rlim_names.h" | ||
25 | |||
26 | /* audit callback for resource specific fields */ | ||
27 | static void audit_cb(struct audit_buffer *ab, void *va) | ||
28 | { | ||
29 | struct common_audit_data *sa = va; | ||
30 | |||
31 | audit_log_format(ab, " rlimit=%s value=%lu", | ||
32 | rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * audit_resource - audit setting resource limit | ||
37 | * @profile: profile being enforced (NOT NULL) | ||
38 | * @resoure: rlimit being auditing | ||
39 | * @value: value being set | ||
40 | * @error: error value | ||
41 | * | ||
42 | * Returns: 0 or sa->error else other error code on failure | ||
43 | */ | ||
44 | static int audit_resource(struct aa_profile *profile, unsigned int resource, | ||
45 | unsigned long value, int error) | ||
46 | { | ||
47 | struct common_audit_data sa; | ||
48 | |||
49 | COMMON_AUDIT_DATA_INIT(&sa, NONE); | ||
50 | sa.aad.op = OP_SETRLIMIT, | ||
51 | sa.aad.rlim.rlim = resource; | ||
52 | sa.aad.rlim.max = value; | ||
53 | sa.aad.error = error; | ||
54 | return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, | ||
55 | audit_cb); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * aa_map_resouce - map compiled policy resource to internal # | ||
60 | * @resource: flattened policy resource number | ||
61 | * | ||
62 | * Returns: resource # for the current architecture. | ||
63 | * | ||
64 | * rlimit resource can vary based on architecture, map the compiled policy | ||
65 | * resource # to the internal representation for the architecture. | ||
66 | */ | ||
67 | int aa_map_resource(int resource) | ||
68 | { | ||
69 | return rlim_map[resource]; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * aa_task_setrlimit - test permission to set an rlimit | ||
74 | * @profile - profile confining the task (NOT NULL) | ||
75 | * @resource - the resource being set | ||
76 | * @new_rlim - the new resource limit (NOT NULL) | ||
77 | * | ||
78 | * Control raising the processes hard limit. | ||
79 | * | ||
80 | * Returns: 0 or error code if setting resource failed | ||
81 | */ | ||
82 | int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, | ||
83 | struct rlimit *new_rlim) | ||
84 | { | ||
85 | int error = 0; | ||
86 | |||
87 | if (profile->rlimits.mask & (1 << resource) && | ||
88 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) | ||
89 | |||
90 | error = audit_resource(profile, resource, new_rlim->rlim_max, | ||
91 | -EACCES); | ||
92 | |||
93 | return error; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * __aa_transition_rlimits - apply new profile rlimits | ||
98 | * @old: old profile on task (NOT NULL) | ||
99 | * @new: new profile with rlimits to apply (NOT NULL) | ||
100 | */ | ||
101 | void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) | ||
102 | { | ||
103 | unsigned int mask = 0; | ||
104 | struct rlimit *rlim, *initrlim; | ||
105 | int i; | ||
106 | |||
107 | /* for any rlimits the profile controlled reset the soft limit | ||
108 | * to the less of the tasks hard limit and the init tasks soft limit | ||
109 | */ | ||
110 | if (old->rlimits.mask) { | ||
111 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
112 | if (old->rlimits.mask & mask) { | ||
113 | rlim = current->signal->rlim + i; | ||
114 | initrlim = init_task.signal->rlim + i; | ||
115 | rlim->rlim_cur = min(rlim->rlim_max, | ||
116 | initrlim->rlim_cur); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /* set any new hard limits as dictated by the new profile */ | ||
122 | if (!new->rlimits.mask) | ||
123 | return; | ||
124 | for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { | ||
125 | if (!(new->rlimits.mask & mask)) | ||
126 | continue; | ||
127 | |||
128 | rlim = current->signal->rlim + i; | ||
129 | rlim->rlim_max = min(rlim->rlim_max, | ||
130 | new->rlimits.limits[i].rlim_max); | ||
131 | /* soft limit should not exceed hard limit */ | ||
132 | rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); | ||
133 | } | ||
134 | } | ||
diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c new file mode 100644 index 00000000000..f0b34f76ebe --- /dev/null +++ b/security/apparmor/sid.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * AppArmor security module | ||
3 | * | ||
4 | * This file contains AppArmor security identifier (sid) manipulation fns | ||
5 | * | ||
6 | * Copyright 2009-2010 Canonical Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2 of the | ||
11 | * License. | ||
12 | * | ||
13 | * | ||
14 | * AppArmor allocates a unique sid for every profile loaded. If a profile | ||
15 | * is replaced it receives the sid of the profile it is replacing. | ||
16 | * | ||
17 | * The sid value of 0 is invalid. | ||
18 | */ | ||
19 | |||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/err.h> | ||
23 | |||
24 | #include "include/sid.h" | ||
25 | |||
26 | /* global counter from which sids are allocated */ | ||
27 | static u32 global_sid; | ||
28 | static DEFINE_SPINLOCK(sid_lock); | ||
29 | |||
30 | /* TODO FIXME: add sid to profile mapping, and sid recycling */ | ||
31 | |||
32 | /** | ||
33 | * aa_alloc_sid - allocate a new sid for a profile | ||
34 | */ | ||
35 | u32 aa_alloc_sid(void) | ||
36 | { | ||
37 | u32 sid; | ||
38 | |||
39 | /* | ||
40 | * TODO FIXME: sid recycling - part of profile mapping table | ||
41 | */ | ||
42 | spin_lock(&sid_lock); | ||
43 | sid = (++global_sid); | ||
44 | spin_unlock(&sid_lock); | ||
45 | return sid; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * aa_free_sid - free a sid | ||
50 | * @sid: sid to free | ||
51 | */ | ||
52 | void aa_free_sid(u32 sid) | ||
53 | { | ||
54 | ; /* NOP ATM */ | ||
55 | } | ||
diff --git a/security/capability.c b/security/capability.c index 7e468263f2d..95a6599a37b 100644 --- a/security/capability.c +++ b/security/capability.c | |||
@@ -27,7 +27,7 @@ static int cap_quota_on(struct dentry *dentry) | |||
27 | return 0; | 27 | return 0; |
28 | } | 28 | } |
29 | 29 | ||
30 | static int cap_bprm_check_security (struct linux_binprm *bprm) | 30 | static int cap_bprm_check_security(struct linux_binprm *bprm) |
31 | { | 31 | { |
32 | return 0; | 32 | return 0; |
33 | } | 33 | } |
@@ -268,8 +268,7 @@ static int cap_path_rename(struct path *old_path, struct dentry *old_dentry, | |||
268 | return 0; | 268 | return 0; |
269 | } | 269 | } |
270 | 270 | ||
271 | static int cap_path_truncate(struct path *path, loff_t length, | 271 | static int cap_path_truncate(struct path *path) |
272 | unsigned int time_attrs) | ||
273 | { | 272 | { |
274 | return 0; | 273 | return 0; |
275 | } | 274 | } |
diff --git a/security/inode.c b/security/inode.c index 1c812e87450..8c777f022ad 100644 --- a/security/inode.c +++ b/security/inode.c | |||
@@ -86,7 +86,7 @@ static int mknod(struct inode *dir, struct dentry *dentry, | |||
86 | int mode, dev_t dev) | 86 | int mode, dev_t dev) |
87 | { | 87 | { |
88 | struct inode *inode; | 88 | struct inode *inode; |
89 | int error = -EPERM; | 89 | int error = -ENOMEM; |
90 | 90 | ||
91 | if (dentry->d_inode) | 91 | if (dentry->d_inode) |
92 | return -EEXIST; | 92 | return -EEXIST; |
@@ -166,6 +166,8 @@ static int create_by_name(const char *name, mode_t mode, | |||
166 | error = mkdir(parent->d_inode, *dentry, mode); | 166 | error = mkdir(parent->d_inode, *dentry, mode); |
167 | else | 167 | else |
168 | error = create(parent->d_inode, *dentry, mode); | 168 | error = create(parent->d_inode, *dentry, mode); |
169 | if (error) | ||
170 | dput(*dentry); | ||
169 | } else | 171 | } else |
170 | error = PTR_ERR(*dentry); | 172 | error = PTR_ERR(*dentry); |
171 | mutex_unlock(&parent->d_inode->i_mutex); | 173 | mutex_unlock(&parent->d_inode->i_mutex); |
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8fe736aabe7..ef21b96a0b4 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c | |||
@@ -45,7 +45,8 @@ static ssize_t ima_show_htable_violations(struct file *filp, | |||
45 | } | 45 | } |
46 | 46 | ||
47 | static const struct file_operations ima_htable_violations_ops = { | 47 | static const struct file_operations ima_htable_violations_ops = { |
48 | .read = ima_show_htable_violations | 48 | .read = ima_show_htable_violations, |
49 | .llseek = generic_file_llseek, | ||
49 | }; | 50 | }; |
50 | 51 | ||
51 | static ssize_t ima_show_measurements_count(struct file *filp, | 52 | static ssize_t ima_show_measurements_count(struct file *filp, |
@@ -57,7 +58,8 @@ static ssize_t ima_show_measurements_count(struct file *filp, | |||
57 | } | 58 | } |
58 | 59 | ||
59 | static const struct file_operations ima_measurements_count_ops = { | 60 | static const struct file_operations ima_measurements_count_ops = { |
60 | .read = ima_show_measurements_count | 61 | .read = ima_show_measurements_count, |
62 | .llseek = generic_file_llseek, | ||
61 | }; | 63 | }; |
62 | 64 | ||
63 | /* returns pointer to hlist_node */ | 65 | /* returns pointer to hlist_node */ |
@@ -319,7 +321,8 @@ static int ima_release_policy(struct inode *inode, struct file *file) | |||
319 | static const struct file_operations ima_measure_policy_ops = { | 321 | static const struct file_operations ima_measure_policy_ops = { |
320 | .open = ima_open_policy, | 322 | .open = ima_open_policy, |
321 | .write = ima_write_policy, | 323 | .write = ima_write_policy, |
322 | .release = ima_release_policy | 324 | .release = ima_release_policy, |
325 | .llseek = generic_file_llseek, | ||
323 | }; | 326 | }; |
324 | 327 | ||
325 | int __init ima_fs_init(void) | 328 | int __init ima_fs_init(void) |
diff --git a/security/keys/internal.h b/security/keys/internal.h index 38783dcf6c6..addb67b169f 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h | |||
@@ -114,6 +114,10 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, | |||
114 | const void *description, | 114 | const void *description, |
115 | key_match_func_t match); | 115 | key_match_func_t match); |
116 | 116 | ||
117 | extern key_ref_t search_my_process_keyrings(struct key_type *type, | ||
118 | const void *description, | ||
119 | key_match_func_t match, | ||
120 | const struct cred *cred); | ||
117 | extern key_ref_t search_process_keyrings(struct key_type *type, | 121 | extern key_ref_t search_process_keyrings(struct key_type *type, |
118 | const void *description, | 122 | const void *description, |
119 | key_match_func_t match, | 123 | key_match_func_t match, |
@@ -134,6 +138,7 @@ extern struct key *request_key_and_link(struct key_type *type, | |||
134 | struct key *dest_keyring, | 138 | struct key *dest_keyring, |
135 | unsigned long flags); | 139 | unsigned long flags); |
136 | 140 | ||
141 | extern int lookup_user_key_possessed(const struct key *key, const void *target); | ||
137 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, | 142 | extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, |
138 | key_perm_t perm); | 143 | key_perm_t perm); |
139 | #define KEY_LOOKUP_CREATE 0x01 | 144 | #define KEY_LOOKUP_CREATE 0x01 |
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6261745e445..b2b0998d6ab 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c | |||
@@ -505,13 +505,11 @@ okay: | |||
505 | 505 | ||
506 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, | 506 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, |
507 | "%s;%d;%d;%08x;%s", | 507 | "%s;%d;%d;%08x;%s", |
508 | key_ref_to_ptr(key_ref)->type->name, | 508 | key->type->name, |
509 | key_ref_to_ptr(key_ref)->uid, | 509 | key->uid, |
510 | key_ref_to_ptr(key_ref)->gid, | 510 | key->gid, |
511 | key_ref_to_ptr(key_ref)->perm, | 511 | key->perm, |
512 | key_ref_to_ptr(key_ref)->description ? | 512 | key->description ?: ""); |
513 | key_ref_to_ptr(key_ref)->description : "" | ||
514 | ); | ||
515 | 513 | ||
516 | /* include a NUL char at the end of the data */ | 514 | /* include a NUL char at the end of the data */ |
517 | if (ret > PAGE_SIZE - 1) | 515 | if (ret > PAGE_SIZE - 1) |
@@ -1091,7 +1089,7 @@ error: | |||
1091 | long keyctl_set_timeout(key_serial_t id, unsigned timeout) | 1089 | long keyctl_set_timeout(key_serial_t id, unsigned timeout) |
1092 | { | 1090 | { |
1093 | struct timespec now; | 1091 | struct timespec now; |
1094 | struct key *key; | 1092 | struct key *key, *instkey; |
1095 | key_ref_t key_ref; | 1093 | key_ref_t key_ref; |
1096 | time_t expiry; | 1094 | time_t expiry; |
1097 | long ret; | 1095 | long ret; |
@@ -1099,10 +1097,25 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) | |||
1099 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, | 1097 | key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, |
1100 | KEY_SETATTR); | 1098 | KEY_SETATTR); |
1101 | if (IS_ERR(key_ref)) { | 1099 | if (IS_ERR(key_ref)) { |
1100 | /* setting the timeout on a key under construction is permitted | ||
1101 | * if we have the authorisation token handy */ | ||
1102 | if (PTR_ERR(key_ref) == -EACCES) { | ||
1103 | instkey = key_get_instantiation_authkey(id); | ||
1104 | if (!IS_ERR(instkey)) { | ||
1105 | key_put(instkey); | ||
1106 | key_ref = lookup_user_key(id, | ||
1107 | KEY_LOOKUP_PARTIAL, | ||
1108 | 0); | ||
1109 | if (!IS_ERR(key_ref)) | ||
1110 | goto okay; | ||
1111 | } | ||
1112 | } | ||
1113 | |||
1102 | ret = PTR_ERR(key_ref); | 1114 | ret = PTR_ERR(key_ref); |
1103 | goto error; | 1115 | goto error; |
1104 | } | 1116 | } |
1105 | 1117 | ||
1118 | okay: | ||
1106 | key = key_ref_to_ptr(key_ref); | 1119 | key = key_ref_to_ptr(key_ref); |
1107 | 1120 | ||
1108 | /* make the changes with the locks held to prevent races */ | 1121 | /* make the changes with the locks held to prevent races */ |
diff --git a/security/keys/proc.c b/security/keys/proc.c index 068b66ea2f1..70373966816 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c | |||
@@ -184,20 +184,36 @@ static void proc_keys_stop(struct seq_file *p, void *v) | |||
184 | 184 | ||
185 | static int proc_keys_show(struct seq_file *m, void *v) | 185 | static int proc_keys_show(struct seq_file *m, void *v) |
186 | { | 186 | { |
187 | const struct cred *cred = current_cred(); | ||
187 | struct rb_node *_p = v; | 188 | struct rb_node *_p = v; |
188 | struct key *key = rb_entry(_p, struct key, serial_node); | 189 | struct key *key = rb_entry(_p, struct key, serial_node); |
189 | struct timespec now; | 190 | struct timespec now; |
190 | unsigned long timo; | 191 | unsigned long timo; |
192 | key_ref_t key_ref, skey_ref; | ||
191 | char xbuf[12]; | 193 | char xbuf[12]; |
192 | int rc; | 194 | int rc; |
193 | 195 | ||
196 | key_ref = make_key_ref(key, 0); | ||
197 | |||
198 | /* determine if the key is possessed by this process (a test we can | ||
199 | * skip if the key does not indicate the possessor can view it | ||
200 | */ | ||
201 | if (key->perm & KEY_POS_VIEW) { | ||
202 | skey_ref = search_my_process_keyrings(key->type, key, | ||
203 | lookup_user_key_possessed, | ||
204 | cred); | ||
205 | if (!IS_ERR(skey_ref)) { | ||
206 | key_ref_put(skey_ref); | ||
207 | key_ref = make_key_ref(key, 1); | ||
208 | } | ||
209 | } | ||
210 | |||
194 | /* check whether the current task is allowed to view the key (assuming | 211 | /* check whether the current task is allowed to view the key (assuming |
195 | * non-possession) | 212 | * non-possession) |
196 | * - the caller holds a spinlock, and thus the RCU read lock, making our | 213 | * - the caller holds a spinlock, and thus the RCU read lock, making our |
197 | * access to __current_cred() safe | 214 | * access to __current_cred() safe |
198 | */ | 215 | */ |
199 | rc = key_task_permission(make_key_ref(key, 0), current_cred(), | 216 | rc = key_task_permission(key_ref, cred, KEY_VIEW); |
200 | KEY_VIEW); | ||
201 | if (rc < 0) | 217 | if (rc < 0) |
202 | return 0; | 218 | return 0; |
203 | 219 | ||
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 6b8e4ff4cc6..f8e7251ae2c 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c | |||
@@ -309,22 +309,19 @@ void key_fsgid_changed(struct task_struct *tsk) | |||
309 | 309 | ||
310 | /*****************************************************************************/ | 310 | /*****************************************************************************/ |
311 | /* | 311 | /* |
312 | * search the process keyrings for the first matching key | 312 | * search only my process keyrings for the first matching key |
313 | * - we use the supplied match function to see if the description (or other | 313 | * - we use the supplied match function to see if the description (or other |
314 | * feature of interest) matches | 314 | * feature of interest) matches |
315 | * - we return -EAGAIN if we didn't find any matching key | 315 | * - we return -EAGAIN if we didn't find any matching key |
316 | * - we return -ENOKEY if we found only negative matching keys | 316 | * - we return -ENOKEY if we found only negative matching keys |
317 | */ | 317 | */ |
318 | key_ref_t search_process_keyrings(struct key_type *type, | 318 | key_ref_t search_my_process_keyrings(struct key_type *type, |
319 | const void *description, | 319 | const void *description, |
320 | key_match_func_t match, | 320 | key_match_func_t match, |
321 | const struct cred *cred) | 321 | const struct cred *cred) |
322 | { | 322 | { |
323 | struct request_key_auth *rka; | ||
324 | key_ref_t key_ref, ret, err; | 323 | key_ref_t key_ref, ret, err; |
325 | 324 | ||
326 | might_sleep(); | ||
327 | |||
328 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were | 325 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were |
329 | * searchable, but we failed to find a key or we found a negative key; | 326 | * searchable, but we failed to find a key or we found a negative key; |
330 | * otherwise we want to return a sample error (probably -EACCES) if | 327 | * otherwise we want to return a sample error (probably -EACCES) if |
@@ -424,6 +421,36 @@ key_ref_t search_process_keyrings(struct key_type *type, | |||
424 | } | 421 | } |
425 | } | 422 | } |
426 | 423 | ||
424 | /* no key - decide on the error we're going to go for */ | ||
425 | key_ref = ret ? ret : err; | ||
426 | |||
427 | found: | ||
428 | return key_ref; | ||
429 | } | ||
430 | |||
431 | /*****************************************************************************/ | ||
432 | /* | ||
433 | * search the process keyrings for the first matching key | ||
434 | * - we use the supplied match function to see if the description (or other | ||
435 | * feature of interest) matches | ||
436 | * - we return -EAGAIN if we didn't find any matching key | ||
437 | * - we return -ENOKEY if we found only negative matching keys | ||
438 | */ | ||
439 | key_ref_t search_process_keyrings(struct key_type *type, | ||
440 | const void *description, | ||
441 | key_match_func_t match, | ||
442 | const struct cred *cred) | ||
443 | { | ||
444 | struct request_key_auth *rka; | ||
445 | key_ref_t key_ref, ret = ERR_PTR(-EACCES), err; | ||
446 | |||
447 | might_sleep(); | ||
448 | |||
449 | key_ref = search_my_process_keyrings(type, description, match, cred); | ||
450 | if (!IS_ERR(key_ref)) | ||
451 | goto found; | ||
452 | err = key_ref; | ||
453 | |||
427 | /* if this process has an instantiation authorisation key, then we also | 454 | /* if this process has an instantiation authorisation key, then we also |
428 | * search the keyrings of the process mentioned there | 455 | * search the keyrings of the process mentioned there |
429 | * - we don't permit access to request_key auth keys via this method | 456 | * - we don't permit access to request_key auth keys via this method |
@@ -446,24 +473,19 @@ key_ref_t search_process_keyrings(struct key_type *type, | |||
446 | if (!IS_ERR(key_ref)) | 473 | if (!IS_ERR(key_ref)) |
447 | goto found; | 474 | goto found; |
448 | 475 | ||
449 | switch (PTR_ERR(key_ref)) { | 476 | ret = key_ref; |
450 | case -EAGAIN: /* no key */ | ||
451 | if (ret) | ||
452 | break; | ||
453 | case -ENOKEY: /* negative key */ | ||
454 | ret = key_ref; | ||
455 | break; | ||
456 | default: | ||
457 | err = key_ref; | ||
458 | break; | ||
459 | } | ||
460 | } else { | 477 | } else { |
461 | up_read(&cred->request_key_auth->sem); | 478 | up_read(&cred->request_key_auth->sem); |
462 | } | 479 | } |
463 | } | 480 | } |
464 | 481 | ||
465 | /* no key - decide on the error we're going to go for */ | 482 | /* no key - decide on the error we're going to go for */ |
466 | key_ref = ret ? ret : err; | 483 | if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY)) |
484 | key_ref = ERR_PTR(-ENOKEY); | ||
485 | else if (err == ERR_PTR(-EACCES)) | ||
486 | key_ref = ret; | ||
487 | else | ||
488 | key_ref = err; | ||
467 | 489 | ||
468 | found: | 490 | found: |
469 | return key_ref; | 491 | return key_ref; |
@@ -474,7 +496,7 @@ found: | |||
474 | /* | 496 | /* |
475 | * see if the key we're looking at is the target key | 497 | * see if the key we're looking at is the target key |
476 | */ | 498 | */ |
477 | static int lookup_user_key_possessed(const struct key *key, const void *target) | 499 | int lookup_user_key_possessed(const struct key *key, const void *target) |
478 | { | 500 | { |
479 | return key == target; | 501 | return key == target; |
480 | 502 | ||
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f5ec9ac5d57..0088dd8bf68 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c | |||
@@ -144,6 +144,7 @@ static int call_sbin_request_key(struct key_construction *cons, | |||
144 | prkey = 0; | 144 | prkey = 0; |
145 | if (cred->tgcred->process_keyring) | 145 | if (cred->tgcred->process_keyring) |
146 | prkey = cred->tgcred->process_keyring->serial; | 146 | prkey = cred->tgcred->process_keyring->serial; |
147 | sprintf(keyring_str[1], "%d", prkey); | ||
147 | 148 | ||
148 | rcu_read_lock(); | 149 | rcu_read_lock(); |
149 | session = rcu_dereference(cred->tgcred->session_keyring); | 150 | session = rcu_dereference(cred->tgcred->session_keyring); |
@@ -536,6 +537,8 @@ int wait_for_key_construction(struct key *key, bool intr) | |||
536 | intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | 537 | intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); |
537 | if (ret < 0) | 538 | if (ret < 0) |
538 | return ret; | 539 | return ret; |
540 | if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) | ||
541 | return -ENOKEY; | ||
539 | return key_validate(key); | 542 | return key_validate(key); |
540 | } | 543 | } |
541 | EXPORT_SYMBOL(wait_for_key_construction); | 544 | EXPORT_SYMBOL(wait_for_key_construction); |
diff --git a/security/security.c b/security/security.c index aa510609a95..c53949f17d9 100644 --- a/security/security.c +++ b/security/security.c | |||
@@ -417,12 +417,11 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | |||
417 | new_dentry); | 417 | new_dentry); |
418 | } | 418 | } |
419 | 419 | ||
420 | int security_path_truncate(struct path *path, loff_t length, | 420 | int security_path_truncate(struct path *path) |
421 | unsigned int time_attrs) | ||
422 | { | 421 | { |
423 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | 422 | if (unlikely(IS_PRIVATE(path->dentry->d_inode))) |
424 | return 0; | 423 | return 0; |
425 | return security_ops->path_truncate(path, length, time_attrs); | 424 | return security_ops->path_truncate(path); |
426 | } | 425 | } |
427 | 426 | ||
428 | int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | 427 | int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, |
@@ -620,7 +619,13 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid) | |||
620 | 619 | ||
621 | int security_file_permission(struct file *file, int mask) | 620 | int security_file_permission(struct file *file, int mask) |
622 | { | 621 | { |
623 | return security_ops->file_permission(file, mask); | 622 | int ret; |
623 | |||
624 | ret = security_ops->file_permission(file, mask); | ||
625 | if (ret) | ||
626 | return ret; | ||
627 | |||
628 | return fsnotify_perm(file, mask); | ||
624 | } | 629 | } |
625 | 630 | ||
626 | int security_file_alloc(struct file *file) | 631 | int security_file_alloc(struct file *file) |
@@ -684,7 +689,13 @@ int security_file_receive(struct file *file) | |||
684 | 689 | ||
685 | int security_dentry_open(struct file *file, const struct cred *cred) | 690 | int security_dentry_open(struct file *file, const struct cred *cred) |
686 | { | 691 | { |
687 | return security_ops->dentry_open(file, cred); | 692 | int ret; |
693 | |||
694 | ret = security_ops->dentry_open(file, cred); | ||
695 | if (ret) | ||
696 | return ret; | ||
697 | |||
698 | return fsnotify_perm(file, MAY_OPEN); | ||
688 | } | 699 | } |
689 | 700 | ||
690 | int security_task_create(unsigned long clone_flags) | 701 | int security_task_create(unsigned long clone_flags) |
diff --git a/security/selinux/Makefile b/security/selinux/Makefile index f013982df41..58d80f3bd6f 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile | |||
@@ -25,6 +25,6 @@ $(obj)/avc.o: $(obj)/flask.h | |||
25 | quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h | 25 | quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h |
26 | cmd_flask = scripts/selinux/genheaders/genheaders $(obj)/flask.h $(obj)/av_permissions.h | 26 | cmd_flask = scripts/selinux/genheaders/genheaders $(obj)/flask.h $(obj)/av_permissions.h |
27 | 27 | ||
28 | targets += flask.h | 28 | targets += flask.h av_permissions.h |
29 | $(obj)/flask.h: $(src)/include/classmap.h FORCE | 29 | $(obj)/flask.h: $(src)/include/classmap.h FORCE |
30 | $(call if_changed,flask) | 30 | $(call if_changed,flask) |
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 7f1a304712a..9da6420e205 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
@@ -288,7 +288,6 @@ static struct avc_node *avc_alloc_node(void) | |||
288 | if (!node) | 288 | if (!node) |
289 | goto out; | 289 | goto out; |
290 | 290 | ||
291 | INIT_RCU_HEAD(&node->rhead); | ||
292 | INIT_HLIST_NODE(&node->list); | 291 | INIT_HLIST_NODE(&node->list); |
293 | avc_cache_stats_incr(allocations); | 292 | avc_cache_stats_incr(allocations); |
294 | 293 | ||
@@ -489,9 +488,29 @@ void avc_audit(u32 ssid, u32 tsid, | |||
489 | struct common_audit_data stack_data; | 488 | struct common_audit_data stack_data; |
490 | u32 denied, audited; | 489 | u32 denied, audited; |
491 | denied = requested & ~avd->allowed; | 490 | denied = requested & ~avd->allowed; |
492 | if (denied) | 491 | if (denied) { |
493 | audited = denied & avd->auditdeny; | 492 | audited = denied & avd->auditdeny; |
494 | else if (result) | 493 | /* |
494 | * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in | ||
495 | * this field means that ANY denials should NOT be audited if | ||
496 | * the policy contains an explicit dontaudit rule for that | ||
497 | * permission. Take notice that this is unrelated to the | ||
498 | * actual permissions that were denied. As an example lets | ||
499 | * assume: | ||
500 | * | ||
501 | * denied == READ | ||
502 | * avd.auditdeny & ACCESS == 0 (not set means explicit rule) | ||
503 | * selinux_audit_data.auditdeny & ACCESS == 1 | ||
504 | * | ||
505 | * We will NOT audit the denial even though the denied | ||
506 | * permission was READ and the auditdeny checks were for | ||
507 | * ACCESS | ||
508 | */ | ||
509 | if (a && | ||
510 | a->selinux_audit_data.auditdeny && | ||
511 | !(a->selinux_audit_data.auditdeny & avd->auditdeny)) | ||
512 | audited = 0; | ||
513 | } else if (result) | ||
495 | audited = denied = requested; | 514 | audited = denied = requested; |
496 | else | 515 | else |
497 | audited = requested & avd->auditallow; | 516 | audited = requested & avd->auditallow; |
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2a8a0a915ff..42043f96e54 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -87,9 +87,6 @@ | |||
87 | #include "netlabel.h" | 87 | #include "netlabel.h" |
88 | #include "audit.h" | 88 | #include "audit.h" |
89 | 89 | ||
90 | #define XATTR_SELINUX_SUFFIX "selinux" | ||
91 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX | ||
92 | |||
93 | #define NUM_SEL_MNT_OPTS 5 | 90 | #define NUM_SEL_MNT_OPTS 5 |
94 | 91 | ||
95 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); | 92 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); |
@@ -188,7 +185,7 @@ static inline u32 task_sid(const struct task_struct *task) | |||
188 | */ | 185 | */ |
189 | static inline u32 current_sid(void) | 186 | static inline u32 current_sid(void) |
190 | { | 187 | { |
191 | const struct task_security_struct *tsec = current_cred()->security; | 188 | const struct task_security_struct *tsec = current_security(); |
192 | 189 | ||
193 | return tsec->sid; | 190 | return tsec->sid; |
194 | } | 191 | } |
@@ -279,32 +276,6 @@ static void superblock_free_security(struct super_block *sb) | |||
279 | kfree(sbsec); | 276 | kfree(sbsec); |
280 | } | 277 | } |
281 | 278 | ||
282 | static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) | ||
283 | { | ||
284 | struct sk_security_struct *sksec; | ||
285 | |||
286 | sksec = kzalloc(sizeof(*sksec), priority); | ||
287 | if (!sksec) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | sksec->peer_sid = SECINITSID_UNLABELED; | ||
291 | sksec->sid = SECINITSID_UNLABELED; | ||
292 | sk->sk_security = sksec; | ||
293 | |||
294 | selinux_netlbl_sk_security_reset(sksec); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static void sk_free_security(struct sock *sk) | ||
300 | { | ||
301 | struct sk_security_struct *sksec = sk->sk_security; | ||
302 | |||
303 | sk->sk_security = NULL; | ||
304 | selinux_netlbl_sk_security_free(sksec); | ||
305 | kfree(sksec); | ||
306 | } | ||
307 | |||
308 | /* The security server must be initialized before | 279 | /* The security server must be initialized before |
309 | any labeling or access decisions can be provided. */ | 280 | any labeling or access decisions can be provided. */ |
310 | extern int ss_initialized; | 281 | extern int ss_initialized; |
@@ -1584,8 +1555,7 @@ static int may_create(struct inode *dir, | |||
1584 | struct dentry *dentry, | 1555 | struct dentry *dentry, |
1585 | u16 tclass) | 1556 | u16 tclass) |
1586 | { | 1557 | { |
1587 | const struct cred *cred = current_cred(); | 1558 | const struct task_security_struct *tsec = current_security(); |
1588 | const struct task_security_struct *tsec = cred->security; | ||
1589 | struct inode_security_struct *dsec; | 1559 | struct inode_security_struct *dsec; |
1590 | struct superblock_security_struct *sbsec; | 1560 | struct superblock_security_struct *sbsec; |
1591 | u32 sid, newsid; | 1561 | u32 sid, newsid; |
@@ -1806,27 +1776,9 @@ static inline u32 open_file_to_av(struct file *file) | |||
1806 | { | 1776 | { |
1807 | u32 av = file_to_av(file); | 1777 | u32 av = file_to_av(file); |
1808 | 1778 | ||
1809 | if (selinux_policycap_openperm) { | 1779 | if (selinux_policycap_openperm) |
1810 | mode_t mode = file->f_path.dentry->d_inode->i_mode; | 1780 | av |= FILE__OPEN; |
1811 | /* | 1781 | |
1812 | * lnk files and socks do not really have an 'open' | ||
1813 | */ | ||
1814 | if (S_ISREG(mode)) | ||
1815 | av |= FILE__OPEN; | ||
1816 | else if (S_ISCHR(mode)) | ||
1817 | av |= CHR_FILE__OPEN; | ||
1818 | else if (S_ISBLK(mode)) | ||
1819 | av |= BLK_FILE__OPEN; | ||
1820 | else if (S_ISFIFO(mode)) | ||
1821 | av |= FIFO_FILE__OPEN; | ||
1822 | else if (S_ISDIR(mode)) | ||
1823 | av |= DIR__OPEN; | ||
1824 | else if (S_ISSOCK(mode)) | ||
1825 | av |= SOCK_FILE__OPEN; | ||
1826 | else | ||
1827 | printk(KERN_ERR "SELinux: WARNING: inside %s with " | ||
1828 | "unknown mode:%o\n", __func__, mode); | ||
1829 | } | ||
1830 | return av; | 1782 | return av; |
1831 | } | 1783 | } |
1832 | 1784 | ||
@@ -2183,8 +2135,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) | |||
2183 | 2135 | ||
2184 | static int selinux_bprm_secureexec(struct linux_binprm *bprm) | 2136 | static int selinux_bprm_secureexec(struct linux_binprm *bprm) |
2185 | { | 2137 | { |
2186 | const struct cred *cred = current_cred(); | 2138 | const struct task_security_struct *tsec = current_security(); |
2187 | const struct task_security_struct *tsec = cred->security; | ||
2188 | u32 sid, osid; | 2139 | u32 sid, osid; |
2189 | int atsecure = 0; | 2140 | int atsecure = 0; |
2190 | 2141 | ||
@@ -2562,8 +2513,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, | |||
2562 | char **name, void **value, | 2513 | char **name, void **value, |
2563 | size_t *len) | 2514 | size_t *len) |
2564 | { | 2515 | { |
2565 | const struct cred *cred = current_cred(); | 2516 | const struct task_security_struct *tsec = current_security(); |
2566 | const struct task_security_struct *tsec = cred->security; | ||
2567 | struct inode_security_struct *dsec; | 2517 | struct inode_security_struct *dsec; |
2568 | struct superblock_security_struct *sbsec; | 2518 | struct superblock_security_struct *sbsec; |
2569 | u32 sid, newsid, clen; | 2519 | u32 sid, newsid, clen; |
@@ -2679,14 +2629,26 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na | |||
2679 | static int selinux_inode_permission(struct inode *inode, int mask) | 2629 | static int selinux_inode_permission(struct inode *inode, int mask) |
2680 | { | 2630 | { |
2681 | const struct cred *cred = current_cred(); | 2631 | const struct cred *cred = current_cred(); |
2632 | struct common_audit_data ad; | ||
2633 | u32 perms; | ||
2634 | bool from_access; | ||
2682 | 2635 | ||
2683 | if (!mask) { | 2636 | from_access = mask & MAY_ACCESS; |
2684 | /* No permission to check. Existence test. */ | 2637 | mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); |
2638 | |||
2639 | /* No permission to check. Existence test. */ | ||
2640 | if (!mask) | ||
2685 | return 0; | 2641 | return 0; |
2686 | } | ||
2687 | 2642 | ||
2688 | return inode_has_perm(cred, inode, | 2643 | COMMON_AUDIT_DATA_INIT(&ad, FS); |
2689 | file_mask_to_av(inode->i_mode, mask), NULL); | 2644 | ad.u.fs.inode = inode; |
2645 | |||
2646 | if (from_access) | ||
2647 | ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; | ||
2648 | |||
2649 | perms = file_mask_to_av(inode->i_mode, mask); | ||
2650 | |||
2651 | return inode_has_perm(cred, inode, perms, &ad); | ||
2690 | } | 2652 | } |
2691 | 2653 | ||
2692 | static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) | 2654 | static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) |
@@ -3675,71 +3637,54 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
3675 | } | 3637 | } |
3676 | 3638 | ||
3677 | /* socket security operations */ | 3639 | /* socket security operations */ |
3678 | static int socket_has_perm(struct task_struct *task, struct socket *sock, | 3640 | |
3679 | u32 perms) | 3641 | static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) |
3680 | { | 3642 | { |
3681 | struct inode_security_struct *isec; | 3643 | return tsec->sockcreate_sid ? : tsec->sid; |
3682 | struct common_audit_data ad; | 3644 | } |
3683 | u32 sid; | ||
3684 | int err = 0; | ||
3685 | 3645 | ||
3686 | isec = SOCK_INODE(sock)->i_security; | 3646 | static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) |
3647 | { | ||
3648 | struct sk_security_struct *sksec = sk->sk_security; | ||
3649 | struct common_audit_data ad; | ||
3650 | u32 tsid = task_sid(task); | ||
3687 | 3651 | ||
3688 | if (isec->sid == SECINITSID_KERNEL) | 3652 | if (sksec->sid == SECINITSID_KERNEL) |
3689 | goto out; | 3653 | return 0; |
3690 | sid = task_sid(task); | ||
3691 | 3654 | ||
3692 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3655 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3693 | ad.u.net.sk = sock->sk; | 3656 | ad.u.net.sk = sk; |
3694 | err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); | ||
3695 | 3657 | ||
3696 | out: | 3658 | return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); |
3697 | return err; | ||
3698 | } | 3659 | } |
3699 | 3660 | ||
3700 | static int selinux_socket_create(int family, int type, | 3661 | static int selinux_socket_create(int family, int type, |
3701 | int protocol, int kern) | 3662 | int protocol, int kern) |
3702 | { | 3663 | { |
3703 | const struct cred *cred = current_cred(); | 3664 | const struct task_security_struct *tsec = current_security(); |
3704 | const struct task_security_struct *tsec = cred->security; | 3665 | u32 newsid; |
3705 | u32 sid, newsid; | ||
3706 | u16 secclass; | 3666 | u16 secclass; |
3707 | int err = 0; | ||
3708 | 3667 | ||
3709 | if (kern) | 3668 | if (kern) |
3710 | goto out; | 3669 | return 0; |
3711 | |||
3712 | sid = tsec->sid; | ||
3713 | newsid = tsec->sockcreate_sid ?: sid; | ||
3714 | 3670 | ||
3671 | newsid = socket_sockcreate_sid(tsec); | ||
3715 | secclass = socket_type_to_security_class(family, type, protocol); | 3672 | secclass = socket_type_to_security_class(family, type, protocol); |
3716 | err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL); | 3673 | return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); |
3717 | |||
3718 | out: | ||
3719 | return err; | ||
3720 | } | 3674 | } |
3721 | 3675 | ||
3722 | static int selinux_socket_post_create(struct socket *sock, int family, | 3676 | static int selinux_socket_post_create(struct socket *sock, int family, |
3723 | int type, int protocol, int kern) | 3677 | int type, int protocol, int kern) |
3724 | { | 3678 | { |
3725 | const struct cred *cred = current_cred(); | 3679 | const struct task_security_struct *tsec = current_security(); |
3726 | const struct task_security_struct *tsec = cred->security; | 3680 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
3727 | struct inode_security_struct *isec; | ||
3728 | struct sk_security_struct *sksec; | 3681 | struct sk_security_struct *sksec; |
3729 | u32 sid, newsid; | ||
3730 | int err = 0; | 3682 | int err = 0; |
3731 | 3683 | ||
3732 | sid = tsec->sid; | ||
3733 | newsid = tsec->sockcreate_sid; | ||
3734 | |||
3735 | isec = SOCK_INODE(sock)->i_security; | ||
3736 | |||
3737 | if (kern) | 3684 | if (kern) |
3738 | isec->sid = SECINITSID_KERNEL; | 3685 | isec->sid = SECINITSID_KERNEL; |
3739 | else if (newsid) | ||
3740 | isec->sid = newsid; | ||
3741 | else | 3686 | else |
3742 | isec->sid = sid; | 3687 | isec->sid = socket_sockcreate_sid(tsec); |
3743 | 3688 | ||
3744 | isec->sclass = socket_type_to_security_class(family, type, protocol); | 3689 | isec->sclass = socket_type_to_security_class(family, type, protocol); |
3745 | isec->initialized = 1; | 3690 | isec->initialized = 1; |
@@ -3760,10 +3705,11 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
3760 | 3705 | ||
3761 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 3706 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
3762 | { | 3707 | { |
3708 | struct sock *sk = sock->sk; | ||
3763 | u16 family; | 3709 | u16 family; |
3764 | int err; | 3710 | int err; |
3765 | 3711 | ||
3766 | err = socket_has_perm(current, sock, SOCKET__BIND); | 3712 | err = sock_has_perm(current, sk, SOCKET__BIND); |
3767 | if (err) | 3713 | if (err) |
3768 | goto out; | 3714 | goto out; |
3769 | 3715 | ||
@@ -3772,19 +3718,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3772 | * Multiple address binding for SCTP is not supported yet: we just | 3718 | * Multiple address binding for SCTP is not supported yet: we just |
3773 | * check the first address now. | 3719 | * check the first address now. |
3774 | */ | 3720 | */ |
3775 | family = sock->sk->sk_family; | 3721 | family = sk->sk_family; |
3776 | if (family == PF_INET || family == PF_INET6) { | 3722 | if (family == PF_INET || family == PF_INET6) { |
3777 | char *addrp; | 3723 | char *addrp; |
3778 | struct inode_security_struct *isec; | 3724 | struct sk_security_struct *sksec = sk->sk_security; |
3779 | struct common_audit_data ad; | 3725 | struct common_audit_data ad; |
3780 | struct sockaddr_in *addr4 = NULL; | 3726 | struct sockaddr_in *addr4 = NULL; |
3781 | struct sockaddr_in6 *addr6 = NULL; | 3727 | struct sockaddr_in6 *addr6 = NULL; |
3782 | unsigned short snum; | 3728 | unsigned short snum; |
3783 | struct sock *sk = sock->sk; | ||
3784 | u32 sid, node_perm; | 3729 | u32 sid, node_perm; |
3785 | 3730 | ||
3786 | isec = SOCK_INODE(sock)->i_security; | ||
3787 | |||
3788 | if (family == PF_INET) { | 3731 | if (family == PF_INET) { |
3789 | addr4 = (struct sockaddr_in *)address; | 3732 | addr4 = (struct sockaddr_in *)address; |
3790 | snum = ntohs(addr4->sin_port); | 3733 | snum = ntohs(addr4->sin_port); |
@@ -3808,15 +3751,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3808 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3751 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3809 | ad.u.net.sport = htons(snum); | 3752 | ad.u.net.sport = htons(snum); |
3810 | ad.u.net.family = family; | 3753 | ad.u.net.family = family; |
3811 | err = avc_has_perm(isec->sid, sid, | 3754 | err = avc_has_perm(sksec->sid, sid, |
3812 | isec->sclass, | 3755 | sksec->sclass, |
3813 | SOCKET__NAME_BIND, &ad); | 3756 | SOCKET__NAME_BIND, &ad); |
3814 | if (err) | 3757 | if (err) |
3815 | goto out; | 3758 | goto out; |
3816 | } | 3759 | } |
3817 | } | 3760 | } |
3818 | 3761 | ||
3819 | switch (isec->sclass) { | 3762 | switch (sksec->sclass) { |
3820 | case SECCLASS_TCP_SOCKET: | 3763 | case SECCLASS_TCP_SOCKET: |
3821 | node_perm = TCP_SOCKET__NODE_BIND; | 3764 | node_perm = TCP_SOCKET__NODE_BIND; |
3822 | break; | 3765 | break; |
@@ -3847,8 +3790,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3847 | else | 3790 | else |
3848 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); | 3791 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); |
3849 | 3792 | ||
3850 | err = avc_has_perm(isec->sid, sid, | 3793 | err = avc_has_perm(sksec->sid, sid, |
3851 | isec->sclass, node_perm, &ad); | 3794 | sksec->sclass, node_perm, &ad); |
3852 | if (err) | 3795 | if (err) |
3853 | goto out; | 3796 | goto out; |
3854 | } | 3797 | } |
@@ -3859,19 +3802,18 @@ out: | |||
3859 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | 3802 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) |
3860 | { | 3803 | { |
3861 | struct sock *sk = sock->sk; | 3804 | struct sock *sk = sock->sk; |
3862 | struct inode_security_struct *isec; | 3805 | struct sk_security_struct *sksec = sk->sk_security; |
3863 | int err; | 3806 | int err; |
3864 | 3807 | ||
3865 | err = socket_has_perm(current, sock, SOCKET__CONNECT); | 3808 | err = sock_has_perm(current, sk, SOCKET__CONNECT); |
3866 | if (err) | 3809 | if (err) |
3867 | return err; | 3810 | return err; |
3868 | 3811 | ||
3869 | /* | 3812 | /* |
3870 | * If a TCP or DCCP socket, check name_connect permission for the port. | 3813 | * If a TCP or DCCP socket, check name_connect permission for the port. |
3871 | */ | 3814 | */ |
3872 | isec = SOCK_INODE(sock)->i_security; | 3815 | if (sksec->sclass == SECCLASS_TCP_SOCKET || |
3873 | if (isec->sclass == SECCLASS_TCP_SOCKET || | 3816 | sksec->sclass == SECCLASS_DCCP_SOCKET) { |
3874 | isec->sclass == SECCLASS_DCCP_SOCKET) { | ||
3875 | struct common_audit_data ad; | 3817 | struct common_audit_data ad; |
3876 | struct sockaddr_in *addr4 = NULL; | 3818 | struct sockaddr_in *addr4 = NULL; |
3877 | struct sockaddr_in6 *addr6 = NULL; | 3819 | struct sockaddr_in6 *addr6 = NULL; |
@@ -3894,13 +3836,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
3894 | if (err) | 3836 | if (err) |
3895 | goto out; | 3837 | goto out; |
3896 | 3838 | ||
3897 | perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? | 3839 | perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? |
3898 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; | 3840 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; |
3899 | 3841 | ||
3900 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3842 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3901 | ad.u.net.dport = htons(snum); | 3843 | ad.u.net.dport = htons(snum); |
3902 | ad.u.net.family = sk->sk_family; | 3844 | ad.u.net.family = sk->sk_family; |
3903 | err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); | 3845 | err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); |
3904 | if (err) | 3846 | if (err) |
3905 | goto out; | 3847 | goto out; |
3906 | } | 3848 | } |
@@ -3913,7 +3855,7 @@ out: | |||
3913 | 3855 | ||
3914 | static int selinux_socket_listen(struct socket *sock, int backlog) | 3856 | static int selinux_socket_listen(struct socket *sock, int backlog) |
3915 | { | 3857 | { |
3916 | return socket_has_perm(current, sock, SOCKET__LISTEN); | 3858 | return sock_has_perm(current, sock->sk, SOCKET__LISTEN); |
3917 | } | 3859 | } |
3918 | 3860 | ||
3919 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | 3861 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) |
@@ -3922,7 +3864,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
3922 | struct inode_security_struct *isec; | 3864 | struct inode_security_struct *isec; |
3923 | struct inode_security_struct *newisec; | 3865 | struct inode_security_struct *newisec; |
3924 | 3866 | ||
3925 | err = socket_has_perm(current, sock, SOCKET__ACCEPT); | 3867 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); |
3926 | if (err) | 3868 | if (err) |
3927 | return err; | 3869 | return err; |
3928 | 3870 | ||
@@ -3939,30 +3881,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
3939 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, | 3881 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, |
3940 | int size) | 3882 | int size) |
3941 | { | 3883 | { |
3942 | return socket_has_perm(current, sock, SOCKET__WRITE); | 3884 | return sock_has_perm(current, sock->sk, SOCKET__WRITE); |
3943 | } | 3885 | } |
3944 | 3886 | ||
3945 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, | 3887 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, |
3946 | int size, int flags) | 3888 | int size, int flags) |
3947 | { | 3889 | { |
3948 | return socket_has_perm(current, sock, SOCKET__READ); | 3890 | return sock_has_perm(current, sock->sk, SOCKET__READ); |
3949 | } | 3891 | } |
3950 | 3892 | ||
3951 | static int selinux_socket_getsockname(struct socket *sock) | 3893 | static int selinux_socket_getsockname(struct socket *sock) |
3952 | { | 3894 | { |
3953 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3895 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
3954 | } | 3896 | } |
3955 | 3897 | ||
3956 | static int selinux_socket_getpeername(struct socket *sock) | 3898 | static int selinux_socket_getpeername(struct socket *sock) |
3957 | { | 3899 | { |
3958 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3900 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
3959 | } | 3901 | } |
3960 | 3902 | ||
3961 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) | 3903 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) |
3962 | { | 3904 | { |
3963 | int err; | 3905 | int err; |
3964 | 3906 | ||
3965 | err = socket_has_perm(current, sock, SOCKET__SETOPT); | 3907 | err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); |
3966 | if (err) | 3908 | if (err) |
3967 | return err; | 3909 | return err; |
3968 | 3910 | ||
@@ -3972,68 +3914,58 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname | |||
3972 | static int selinux_socket_getsockopt(struct socket *sock, int level, | 3914 | static int selinux_socket_getsockopt(struct socket *sock, int level, |
3973 | int optname) | 3915 | int optname) |
3974 | { | 3916 | { |
3975 | return socket_has_perm(current, sock, SOCKET__GETOPT); | 3917 | return sock_has_perm(current, sock->sk, SOCKET__GETOPT); |
3976 | } | 3918 | } |
3977 | 3919 | ||
3978 | static int selinux_socket_shutdown(struct socket *sock, int how) | 3920 | static int selinux_socket_shutdown(struct socket *sock, int how) |
3979 | { | 3921 | { |
3980 | return socket_has_perm(current, sock, SOCKET__SHUTDOWN); | 3922 | return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); |
3981 | } | 3923 | } |
3982 | 3924 | ||
3983 | static int selinux_socket_unix_stream_connect(struct socket *sock, | 3925 | static int selinux_socket_unix_stream_connect(struct socket *sock, |
3984 | struct socket *other, | 3926 | struct socket *other, |
3985 | struct sock *newsk) | 3927 | struct sock *newsk) |
3986 | { | 3928 | { |
3987 | struct sk_security_struct *sksec; | 3929 | struct sk_security_struct *sksec_sock = sock->sk->sk_security; |
3988 | struct inode_security_struct *isec; | 3930 | struct sk_security_struct *sksec_other = other->sk->sk_security; |
3989 | struct inode_security_struct *other_isec; | 3931 | struct sk_security_struct *sksec_new = newsk->sk_security; |
3990 | struct common_audit_data ad; | 3932 | struct common_audit_data ad; |
3991 | int err; | 3933 | int err; |
3992 | 3934 | ||
3993 | isec = SOCK_INODE(sock)->i_security; | ||
3994 | other_isec = SOCK_INODE(other)->i_security; | ||
3995 | |||
3996 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3935 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3997 | ad.u.net.sk = other->sk; | 3936 | ad.u.net.sk = other->sk; |
3998 | 3937 | ||
3999 | err = avc_has_perm(isec->sid, other_isec->sid, | 3938 | err = avc_has_perm(sksec_sock->sid, sksec_other->sid, |
4000 | isec->sclass, | 3939 | sksec_other->sclass, |
4001 | UNIX_STREAM_SOCKET__CONNECTTO, &ad); | 3940 | UNIX_STREAM_SOCKET__CONNECTTO, &ad); |
4002 | if (err) | 3941 | if (err) |
4003 | return err; | 3942 | return err; |
4004 | 3943 | ||
4005 | /* connecting socket */ | ||
4006 | sksec = sock->sk->sk_security; | ||
4007 | sksec->peer_sid = other_isec->sid; | ||
4008 | |||
4009 | /* server child socket */ | 3944 | /* server child socket */ |
4010 | sksec = newsk->sk_security; | 3945 | sksec_new->peer_sid = sksec_sock->sid; |
4011 | sksec->peer_sid = isec->sid; | 3946 | err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, |
4012 | err = security_sid_mls_copy(other_isec->sid, sksec->peer_sid, &sksec->sid); | 3947 | &sksec_new->sid); |
3948 | if (err) | ||
3949 | return err; | ||
4013 | 3950 | ||
4014 | return err; | 3951 | /* connecting socket */ |
3952 | sksec_sock->peer_sid = sksec_new->sid; | ||
3953 | |||
3954 | return 0; | ||
4015 | } | 3955 | } |
4016 | 3956 | ||
4017 | static int selinux_socket_unix_may_send(struct socket *sock, | 3957 | static int selinux_socket_unix_may_send(struct socket *sock, |
4018 | struct socket *other) | 3958 | struct socket *other) |
4019 | { | 3959 | { |
4020 | struct inode_security_struct *isec; | 3960 | struct sk_security_struct *ssec = sock->sk->sk_security; |
4021 | struct inode_security_struct *other_isec; | 3961 | struct sk_security_struct *osec = other->sk->sk_security; |
4022 | struct common_audit_data ad; | 3962 | struct common_audit_data ad; |
4023 | int err; | ||
4024 | |||
4025 | isec = SOCK_INODE(sock)->i_security; | ||
4026 | other_isec = SOCK_INODE(other)->i_security; | ||
4027 | 3963 | ||
4028 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3964 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
4029 | ad.u.net.sk = other->sk; | 3965 | ad.u.net.sk = other->sk; |
4030 | 3966 | ||
4031 | err = avc_has_perm(isec->sid, other_isec->sid, | 3967 | return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, |
4032 | isec->sclass, SOCKET__SENDTO, &ad); | 3968 | &ad); |
4033 | if (err) | ||
4034 | return err; | ||
4035 | |||
4036 | return 0; | ||
4037 | } | 3969 | } |
4038 | 3970 | ||
4039 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, | 3971 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, |
@@ -4172,26 +4104,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
4172 | int err = 0; | 4104 | int err = 0; |
4173 | char *scontext; | 4105 | char *scontext; |
4174 | u32 scontext_len; | 4106 | u32 scontext_len; |
4175 | struct sk_security_struct *sksec; | 4107 | struct sk_security_struct *sksec = sock->sk->sk_security; |
4176 | struct inode_security_struct *isec; | ||
4177 | u32 peer_sid = SECSID_NULL; | 4108 | u32 peer_sid = SECSID_NULL; |
4178 | 4109 | ||
4179 | isec = SOCK_INODE(sock)->i_security; | 4110 | if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || |
4180 | 4111 | sksec->sclass == SECCLASS_TCP_SOCKET) | |
4181 | if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || | ||
4182 | isec->sclass == SECCLASS_TCP_SOCKET) { | ||
4183 | sksec = sock->sk->sk_security; | ||
4184 | peer_sid = sksec->peer_sid; | 4112 | peer_sid = sksec->peer_sid; |
4185 | } | 4113 | if (peer_sid == SECSID_NULL) |
4186 | if (peer_sid == SECSID_NULL) { | 4114 | return -ENOPROTOOPT; |
4187 | err = -ENOPROTOOPT; | ||
4188 | goto out; | ||
4189 | } | ||
4190 | 4115 | ||
4191 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); | 4116 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); |
4192 | |||
4193 | if (err) | 4117 | if (err) |
4194 | goto out; | 4118 | return err; |
4195 | 4119 | ||
4196 | if (scontext_len > len) { | 4120 | if (scontext_len > len) { |
4197 | err = -ERANGE; | 4121 | err = -ERANGE; |
@@ -4204,9 +4128,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
4204 | out_len: | 4128 | out_len: |
4205 | if (put_user(scontext_len, optlen)) | 4129 | if (put_user(scontext_len, optlen)) |
4206 | err = -EFAULT; | 4130 | err = -EFAULT; |
4207 | |||
4208 | kfree(scontext); | 4131 | kfree(scontext); |
4209 | out: | ||
4210 | return err; | 4132 | return err; |
4211 | } | 4133 | } |
4212 | 4134 | ||
@@ -4238,12 +4160,27 @@ out: | |||
4238 | 4160 | ||
4239 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) | 4161 | static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) |
4240 | { | 4162 | { |
4241 | return sk_alloc_security(sk, family, priority); | 4163 | struct sk_security_struct *sksec; |
4164 | |||
4165 | sksec = kzalloc(sizeof(*sksec), priority); | ||
4166 | if (!sksec) | ||
4167 | return -ENOMEM; | ||
4168 | |||
4169 | sksec->peer_sid = SECINITSID_UNLABELED; | ||
4170 | sksec->sid = SECINITSID_UNLABELED; | ||
4171 | selinux_netlbl_sk_security_reset(sksec); | ||
4172 | sk->sk_security = sksec; | ||
4173 | |||
4174 | return 0; | ||
4242 | } | 4175 | } |
4243 | 4176 | ||
4244 | static void selinux_sk_free_security(struct sock *sk) | 4177 | static void selinux_sk_free_security(struct sock *sk) |
4245 | { | 4178 | { |
4246 | sk_free_security(sk); | 4179 | struct sk_security_struct *sksec = sk->sk_security; |
4180 | |||
4181 | sk->sk_security = NULL; | ||
4182 | selinux_netlbl_sk_security_free(sksec); | ||
4183 | kfree(sksec); | ||
4247 | } | 4184 | } |
4248 | 4185 | ||
4249 | static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) | 4186 | static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) |
@@ -4403,8 +4340,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4403 | int err = 0; | 4340 | int err = 0; |
4404 | u32 perm; | 4341 | u32 perm; |
4405 | struct nlmsghdr *nlh; | 4342 | struct nlmsghdr *nlh; |
4406 | struct socket *sock = sk->sk_socket; | 4343 | struct sk_security_struct *sksec = sk->sk_security; |
4407 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | ||
4408 | 4344 | ||
4409 | if (skb->len < NLMSG_SPACE(0)) { | 4345 | if (skb->len < NLMSG_SPACE(0)) { |
4410 | err = -EINVAL; | 4346 | err = -EINVAL; |
@@ -4412,13 +4348,13 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4412 | } | 4348 | } |
4413 | nlh = nlmsg_hdr(skb); | 4349 | nlh = nlmsg_hdr(skb); |
4414 | 4350 | ||
4415 | err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); | 4351 | err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); |
4416 | if (err) { | 4352 | if (err) { |
4417 | if (err == -EINVAL) { | 4353 | if (err == -EINVAL) { |
4418 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, | 4354 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, |
4419 | "SELinux: unrecognized netlink message" | 4355 | "SELinux: unrecognized netlink message" |
4420 | " type=%hu for sclass=%hu\n", | 4356 | " type=%hu for sclass=%hu\n", |
4421 | nlh->nlmsg_type, isec->sclass); | 4357 | nlh->nlmsg_type, sksec->sclass); |
4422 | if (!selinux_enforcing || security_get_allow_unknown()) | 4358 | if (!selinux_enforcing || security_get_allow_unknown()) |
4423 | err = 0; | 4359 | err = 0; |
4424 | } | 4360 | } |
@@ -4429,7 +4365,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4429 | goto out; | 4365 | goto out; |
4430 | } | 4366 | } |
4431 | 4367 | ||
4432 | err = socket_has_perm(current, sock, perm); | 4368 | err = sock_has_perm(current, sk, perm); |
4433 | out: | 4369 | out: |
4434 | return err; | 4370 | return err; |
4435 | } | 4371 | } |
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 8b32e959bb2..b4c9eb4bd6f 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h | |||
@@ -2,7 +2,8 @@ | |||
2 | "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" | 2 | "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" |
3 | 3 | ||
4 | #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ | 4 | #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ |
5 | "rename", "execute", "swapon", "quotaon", "mounton" | 5 | "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ |
6 | "open", "execmod" | ||
6 | 7 | ||
7 | #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ | 8 | #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ |
8 | "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ | 9 | "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ |
@@ -43,22 +44,21 @@ struct security_class_mapping secclass_map[] = { | |||
43 | "quotaget", NULL } }, | 44 | "quotaget", NULL } }, |
44 | { "file", | 45 | { "file", |
45 | { COMMON_FILE_PERMS, | 46 | { COMMON_FILE_PERMS, |
46 | "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, | 47 | "execute_no_trans", "entrypoint", NULL } }, |
47 | { "dir", | 48 | { "dir", |
48 | { COMMON_FILE_PERMS, "add_name", "remove_name", | 49 | { COMMON_FILE_PERMS, "add_name", "remove_name", |
49 | "reparent", "search", "rmdir", "open", NULL } }, | 50 | "reparent", "search", "rmdir", NULL } }, |
50 | { "fd", { "use", NULL } }, | 51 | { "fd", { "use", NULL } }, |
51 | { "lnk_file", | 52 | { "lnk_file", |
52 | { COMMON_FILE_PERMS, NULL } }, | 53 | { COMMON_FILE_PERMS, NULL } }, |
53 | { "chr_file", | 54 | { "chr_file", |
54 | { COMMON_FILE_PERMS, | 55 | { COMMON_FILE_PERMS, NULL } }, |
55 | "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, | ||
56 | { "blk_file", | 56 | { "blk_file", |
57 | { COMMON_FILE_PERMS, "open", NULL } }, | 57 | { COMMON_FILE_PERMS, NULL } }, |
58 | { "sock_file", | 58 | { "sock_file", |
59 | { COMMON_FILE_PERMS, "open", NULL } }, | 59 | { COMMON_FILE_PERMS, NULL } }, |
60 | { "fifo_file", | 60 | { "fifo_file", |
61 | { COMMON_FILE_PERMS, "open", NULL } }, | 61 | { COMMON_FILE_PERMS, NULL } }, |
62 | { "socket", | 62 | { "socket", |
63 | { COMMON_SOCK_PERMS, NULL } }, | 63 | { COMMON_SOCK_PERMS, NULL } }, |
64 | { "tcp_socket", | 64 | { "tcp_socket", |
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index dc92792271f..65ebfe954f8 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c | |||
@@ -183,8 +183,6 @@ static void sel_netnode_insert(struct sel_netnode *node) | |||
183 | BUG(); | 183 | BUG(); |
184 | } | 184 | } |
185 | 185 | ||
186 | INIT_RCU_HEAD(&node->rcu); | ||
187 | |||
188 | /* we need to impose a limit on the growth of the hash table so check | 186 | /* we need to impose a limit on the growth of the hash table so check |
189 | * this bucket to make sure it is within the specified bounds */ | 187 | * this bucket to make sure it is within the specified bounds */ |
190 | list_add_rcu(&node->list, &sel_netnode_hash[idx].list); | 188 | list_add_rcu(&node->list, &sel_netnode_hash[idx].list); |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0293843f7ed..79a1bb63566 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
@@ -184,6 +184,7 @@ out: | |||
184 | static const struct file_operations sel_enforce_ops = { | 184 | static const struct file_operations sel_enforce_ops = { |
185 | .read = sel_read_enforce, | 185 | .read = sel_read_enforce, |
186 | .write = sel_write_enforce, | 186 | .write = sel_write_enforce, |
187 | .llseek = generic_file_llseek, | ||
187 | }; | 188 | }; |
188 | 189 | ||
189 | static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, | 190 | static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, |
@@ -201,6 +202,7 @@ static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, | |||
201 | 202 | ||
202 | static const struct file_operations sel_handle_unknown_ops = { | 203 | static const struct file_operations sel_handle_unknown_ops = { |
203 | .read = sel_read_handle_unknown, | 204 | .read = sel_read_handle_unknown, |
205 | .llseek = generic_file_llseek, | ||
204 | }; | 206 | }; |
205 | 207 | ||
206 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | 208 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
@@ -251,6 +253,7 @@ out: | |||
251 | 253 | ||
252 | static const struct file_operations sel_disable_ops = { | 254 | static const struct file_operations sel_disable_ops = { |
253 | .write = sel_write_disable, | 255 | .write = sel_write_disable, |
256 | .llseek = generic_file_llseek, | ||
254 | }; | 257 | }; |
255 | 258 | ||
256 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | 259 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, |
@@ -265,6 +268,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | |||
265 | 268 | ||
266 | static const struct file_operations sel_policyvers_ops = { | 269 | static const struct file_operations sel_policyvers_ops = { |
267 | .read = sel_read_policyvers, | 270 | .read = sel_read_policyvers, |
271 | .llseek = generic_file_llseek, | ||
268 | }; | 272 | }; |
269 | 273 | ||
270 | /* declaration for sel_write_load */ | 274 | /* declaration for sel_write_load */ |
@@ -289,6 +293,7 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, | |||
289 | 293 | ||
290 | static const struct file_operations sel_mls_ops = { | 294 | static const struct file_operations sel_mls_ops = { |
291 | .read = sel_read_mls, | 295 | .read = sel_read_mls, |
296 | .llseek = generic_file_llseek, | ||
292 | }; | 297 | }; |
293 | 298 | ||
294 | static ssize_t sel_write_load(struct file *file, const char __user *buf, | 299 | static ssize_t sel_write_load(struct file *file, const char __user *buf, |
@@ -356,6 +361,7 @@ out: | |||
356 | 361 | ||
357 | static const struct file_operations sel_load_ops = { | 362 | static const struct file_operations sel_load_ops = { |
358 | .write = sel_write_load, | 363 | .write = sel_write_load, |
364 | .llseek = generic_file_llseek, | ||
359 | }; | 365 | }; |
360 | 366 | ||
361 | static ssize_t sel_write_context(struct file *file, char *buf, size_t size) | 367 | static ssize_t sel_write_context(struct file *file, char *buf, size_t size) |
@@ -437,6 +443,7 @@ out: | |||
437 | static const struct file_operations sel_checkreqprot_ops = { | 443 | static const struct file_operations sel_checkreqprot_ops = { |
438 | .read = sel_read_checkreqprot, | 444 | .read = sel_read_checkreqprot, |
439 | .write = sel_write_checkreqprot, | 445 | .write = sel_write_checkreqprot, |
446 | .llseek = generic_file_llseek, | ||
440 | }; | 447 | }; |
441 | 448 | ||
442 | /* | 449 | /* |
@@ -482,6 +489,7 @@ static const struct file_operations transaction_ops = { | |||
482 | .write = selinux_transaction_write, | 489 | .write = selinux_transaction_write, |
483 | .read = simple_transaction_read, | 490 | .read = simple_transaction_read, |
484 | .release = simple_transaction_release, | 491 | .release = simple_transaction_release, |
492 | .llseek = generic_file_llseek, | ||
485 | }; | 493 | }; |
486 | 494 | ||
487 | /* | 495 | /* |
@@ -883,6 +891,7 @@ out: | |||
883 | static const struct file_operations sel_bool_ops = { | 891 | static const struct file_operations sel_bool_ops = { |
884 | .read = sel_read_bool, | 892 | .read = sel_read_bool, |
885 | .write = sel_write_bool, | 893 | .write = sel_write_bool, |
894 | .llseek = generic_file_llseek, | ||
886 | }; | 895 | }; |
887 | 896 | ||
888 | static ssize_t sel_commit_bools_write(struct file *filep, | 897 | static ssize_t sel_commit_bools_write(struct file *filep, |
@@ -935,6 +944,7 @@ out: | |||
935 | 944 | ||
936 | static const struct file_operations sel_commit_bools_ops = { | 945 | static const struct file_operations sel_commit_bools_ops = { |
937 | .write = sel_commit_bools_write, | 946 | .write = sel_commit_bools_write, |
947 | .llseek = generic_file_llseek, | ||
938 | }; | 948 | }; |
939 | 949 | ||
940 | static void sel_remove_entries(struct dentry *de) | 950 | static void sel_remove_entries(struct dentry *de) |
@@ -1127,10 +1137,12 @@ out: | |||
1127 | static const struct file_operations sel_avc_cache_threshold_ops = { | 1137 | static const struct file_operations sel_avc_cache_threshold_ops = { |
1128 | .read = sel_read_avc_cache_threshold, | 1138 | .read = sel_read_avc_cache_threshold, |
1129 | .write = sel_write_avc_cache_threshold, | 1139 | .write = sel_write_avc_cache_threshold, |
1140 | .llseek = generic_file_llseek, | ||
1130 | }; | 1141 | }; |
1131 | 1142 | ||
1132 | static const struct file_operations sel_avc_hash_stats_ops = { | 1143 | static const struct file_operations sel_avc_hash_stats_ops = { |
1133 | .read = sel_read_avc_hash_stats, | 1144 | .read = sel_read_avc_hash_stats, |
1145 | .llseek = generic_file_llseek, | ||
1134 | }; | 1146 | }; |
1135 | 1147 | ||
1136 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | 1148 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS |
@@ -1255,6 +1267,7 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf, | |||
1255 | 1267 | ||
1256 | static const struct file_operations sel_initcon_ops = { | 1268 | static const struct file_operations sel_initcon_ops = { |
1257 | .read = sel_read_initcon, | 1269 | .read = sel_read_initcon, |
1270 | .llseek = generic_file_llseek, | ||
1258 | }; | 1271 | }; |
1259 | 1272 | ||
1260 | static int sel_make_initcon_files(struct dentry *dir) | 1273 | static int sel_make_initcon_files(struct dentry *dir) |
@@ -1330,6 +1343,7 @@ out: | |||
1330 | 1343 | ||
1331 | static const struct file_operations sel_class_ops = { | 1344 | static const struct file_operations sel_class_ops = { |
1332 | .read = sel_read_class, | 1345 | .read = sel_read_class, |
1346 | .llseek = generic_file_llseek, | ||
1333 | }; | 1347 | }; |
1334 | 1348 | ||
1335 | static ssize_t sel_read_perm(struct file *file, char __user *buf, | 1349 | static ssize_t sel_read_perm(struct file *file, char __user *buf, |
@@ -1354,6 +1368,7 @@ out: | |||
1354 | 1368 | ||
1355 | static const struct file_operations sel_perm_ops = { | 1369 | static const struct file_operations sel_perm_ops = { |
1356 | .read = sel_read_perm, | 1370 | .read = sel_read_perm, |
1371 | .llseek = generic_file_llseek, | ||
1357 | }; | 1372 | }; |
1358 | 1373 | ||
1359 | static ssize_t sel_read_policycap(struct file *file, char __user *buf, | 1374 | static ssize_t sel_read_policycap(struct file *file, char __user *buf, |
@@ -1372,6 +1387,7 @@ static ssize_t sel_read_policycap(struct file *file, char __user *buf, | |||
1372 | 1387 | ||
1373 | static const struct file_operations sel_policycap_ops = { | 1388 | static const struct file_operations sel_policycap_ops = { |
1374 | .read = sel_read_policycap, | 1389 | .read = sel_read_policycap, |
1390 | .llseek = generic_file_llseek, | ||
1375 | }; | 1391 | }; |
1376 | 1392 | ||
1377 | static int sel_make_perm_files(char *objclass, int classvalue, | 1393 | static int sel_make_perm_files(char *objclass, int classvalue, |
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 1215b8e47db..929480c6c43 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c | |||
@@ -342,20 +342,20 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
342 | 342 | ||
343 | if (vers < POLICYDB_VERSION_AVTAB) { | 343 | if (vers < POLICYDB_VERSION_AVTAB) { |
344 | rc = next_entry(buf32, fp, sizeof(u32)); | 344 | rc = next_entry(buf32, fp, sizeof(u32)); |
345 | if (rc < 0) { | 345 | if (rc) { |
346 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 346 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
347 | return -1; | 347 | return rc; |
348 | } | 348 | } |
349 | items2 = le32_to_cpu(buf32[0]); | 349 | items2 = le32_to_cpu(buf32[0]); |
350 | if (items2 > ARRAY_SIZE(buf32)) { | 350 | if (items2 > ARRAY_SIZE(buf32)) { |
351 | printk(KERN_ERR "SELinux: avtab: entry overflow\n"); | 351 | printk(KERN_ERR "SELinux: avtab: entry overflow\n"); |
352 | return -1; | 352 | return -EINVAL; |
353 | 353 | ||
354 | } | 354 | } |
355 | rc = next_entry(buf32, fp, sizeof(u32)*items2); | 355 | rc = next_entry(buf32, fp, sizeof(u32)*items2); |
356 | if (rc < 0) { | 356 | if (rc) { |
357 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 357 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
358 | return -1; | 358 | return rc; |
359 | } | 359 | } |
360 | items = 0; | 360 | items = 0; |
361 | 361 | ||
@@ -363,19 +363,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
363 | key.source_type = (u16)val; | 363 | key.source_type = (u16)val; |
364 | if (key.source_type != val) { | 364 | if (key.source_type != val) { |
365 | printk(KERN_ERR "SELinux: avtab: truncated source type\n"); | 365 | printk(KERN_ERR "SELinux: avtab: truncated source type\n"); |
366 | return -1; | 366 | return -EINVAL; |
367 | } | 367 | } |
368 | val = le32_to_cpu(buf32[items++]); | 368 | val = le32_to_cpu(buf32[items++]); |
369 | key.target_type = (u16)val; | 369 | key.target_type = (u16)val; |
370 | if (key.target_type != val) { | 370 | if (key.target_type != val) { |
371 | printk(KERN_ERR "SELinux: avtab: truncated target type\n"); | 371 | printk(KERN_ERR "SELinux: avtab: truncated target type\n"); |
372 | return -1; | 372 | return -EINVAL; |
373 | } | 373 | } |
374 | val = le32_to_cpu(buf32[items++]); | 374 | val = le32_to_cpu(buf32[items++]); |
375 | key.target_class = (u16)val; | 375 | key.target_class = (u16)val; |
376 | if (key.target_class != val) { | 376 | if (key.target_class != val) { |
377 | printk(KERN_ERR "SELinux: avtab: truncated target class\n"); | 377 | printk(KERN_ERR "SELinux: avtab: truncated target class\n"); |
378 | return -1; | 378 | return -EINVAL; |
379 | } | 379 | } |
380 | 380 | ||
381 | val = le32_to_cpu(buf32[items++]); | 381 | val = le32_to_cpu(buf32[items++]); |
@@ -383,12 +383,12 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
383 | 383 | ||
384 | if (!(val & (AVTAB_AV | AVTAB_TYPE))) { | 384 | if (!(val & (AVTAB_AV | AVTAB_TYPE))) { |
385 | printk(KERN_ERR "SELinux: avtab: null entry\n"); | 385 | printk(KERN_ERR "SELinux: avtab: null entry\n"); |
386 | return -1; | 386 | return -EINVAL; |
387 | } | 387 | } |
388 | if ((val & AVTAB_AV) && | 388 | if ((val & AVTAB_AV) && |
389 | (val & AVTAB_TYPE)) { | 389 | (val & AVTAB_TYPE)) { |
390 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); | 390 | printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); |
391 | return -1; | 391 | return -EINVAL; |
392 | } | 392 | } |
393 | 393 | ||
394 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { | 394 | for (i = 0; i < ARRAY_SIZE(spec_order); i++) { |
@@ -403,15 +403,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
403 | 403 | ||
404 | if (items != items2) { | 404 | if (items != items2) { |
405 | printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); | 405 | printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); |
406 | return -1; | 406 | return -EINVAL; |
407 | } | 407 | } |
408 | return 0; | 408 | return 0; |
409 | } | 409 | } |
410 | 410 | ||
411 | rc = next_entry(buf16, fp, sizeof(u16)*4); | 411 | rc = next_entry(buf16, fp, sizeof(u16)*4); |
412 | if (rc < 0) { | 412 | if (rc) { |
413 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 413 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
414 | return -1; | 414 | return rc; |
415 | } | 415 | } |
416 | 416 | ||
417 | items = 0; | 417 | items = 0; |
@@ -424,7 +424,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
424 | !policydb_type_isvalid(pol, key.target_type) || | 424 | !policydb_type_isvalid(pol, key.target_type) || |
425 | !policydb_class_isvalid(pol, key.target_class)) { | 425 | !policydb_class_isvalid(pol, key.target_class)) { |
426 | printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); | 426 | printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); |
427 | return -1; | 427 | return -EINVAL; |
428 | } | 428 | } |
429 | 429 | ||
430 | set = 0; | 430 | set = 0; |
@@ -434,19 +434,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, | |||
434 | } | 434 | } |
435 | if (!set || set > 1) { | 435 | if (!set || set > 1) { |
436 | printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); | 436 | printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); |
437 | return -1; | 437 | return -EINVAL; |
438 | } | 438 | } |
439 | 439 | ||
440 | rc = next_entry(buf32, fp, sizeof(u32)); | 440 | rc = next_entry(buf32, fp, sizeof(u32)); |
441 | if (rc < 0) { | 441 | if (rc) { |
442 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); | 442 | printk(KERN_ERR "SELinux: avtab: truncated entry\n"); |
443 | return -1; | 443 | return rc; |
444 | } | 444 | } |
445 | datum.data = le32_to_cpu(*buf32); | 445 | datum.data = le32_to_cpu(*buf32); |
446 | if ((key.specified & AVTAB_TYPE) && | 446 | if ((key.specified & AVTAB_TYPE) && |
447 | !policydb_type_isvalid(pol, datum.data)) { | 447 | !policydb_type_isvalid(pol, datum.data)) { |
448 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); | 448 | printk(KERN_ERR "SELinux: avtab: invalid type\n"); |
449 | return -1; | 449 | return -EINVAL; |
450 | } | 450 | } |
451 | return insertf(a, &key, &datum, p); | 451 | return insertf(a, &key, &datum, p); |
452 | } | 452 | } |
@@ -487,8 +487,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) | |||
487 | printk(KERN_ERR "SELinux: avtab: out of memory\n"); | 487 | printk(KERN_ERR "SELinux: avtab: out of memory\n"); |
488 | else if (rc == -EEXIST) | 488 | else if (rc == -EEXIST) |
489 | printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); | 489 | printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); |
490 | else | 490 | |
491 | rc = -EINVAL; | ||
492 | goto bad; | 491 | goto bad; |
493 | } | 492 | } |
494 | } | 493 | } |
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 4a4e35cac22..c91e150c308 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c | |||
@@ -117,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) | |||
117 | 117 | ||
118 | int cond_policydb_init(struct policydb *p) | 118 | int cond_policydb_init(struct policydb *p) |
119 | { | 119 | { |
120 | int rc; | ||
121 | |||
120 | p->bool_val_to_struct = NULL; | 122 | p->bool_val_to_struct = NULL; |
121 | p->cond_list = NULL; | 123 | p->cond_list = NULL; |
122 | if (avtab_init(&p->te_cond_avtab)) | 124 | |
123 | return -1; | 125 | rc = avtab_init(&p->te_cond_avtab); |
126 | if (rc) | ||
127 | return rc; | ||
124 | 128 | ||
125 | return 0; | 129 | return 0; |
126 | } | 130 | } |
@@ -219,34 +223,37 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | |||
219 | 223 | ||
220 | booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); | 224 | booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); |
221 | if (!booldatum) | 225 | if (!booldatum) |
222 | return -1; | 226 | return -ENOMEM; |
223 | 227 | ||
224 | rc = next_entry(buf, fp, sizeof buf); | 228 | rc = next_entry(buf, fp, sizeof buf); |
225 | if (rc < 0) | 229 | if (rc) |
226 | goto err; | 230 | goto err; |
227 | 231 | ||
228 | booldatum->value = le32_to_cpu(buf[0]); | 232 | booldatum->value = le32_to_cpu(buf[0]); |
229 | booldatum->state = le32_to_cpu(buf[1]); | 233 | booldatum->state = le32_to_cpu(buf[1]); |
230 | 234 | ||
235 | rc = -EINVAL; | ||
231 | if (!bool_isvalid(booldatum)) | 236 | if (!bool_isvalid(booldatum)) |
232 | goto err; | 237 | goto err; |
233 | 238 | ||
234 | len = le32_to_cpu(buf[2]); | 239 | len = le32_to_cpu(buf[2]); |
235 | 240 | ||
241 | rc = -ENOMEM; | ||
236 | key = kmalloc(len + 1, GFP_KERNEL); | 242 | key = kmalloc(len + 1, GFP_KERNEL); |
237 | if (!key) | 243 | if (!key) |
238 | goto err; | 244 | goto err; |
239 | rc = next_entry(key, fp, len); | 245 | rc = next_entry(key, fp, len); |
240 | if (rc < 0) | 246 | if (rc) |
241 | goto err; | 247 | goto err; |
242 | key[len] = '\0'; | 248 | key[len] = '\0'; |
243 | if (hashtab_insert(h, key, booldatum)) | 249 | rc = hashtab_insert(h, key, booldatum); |
250 | if (rc) | ||
244 | goto err; | 251 | goto err; |
245 | 252 | ||
246 | return 0; | 253 | return 0; |
247 | err: | 254 | err: |
248 | cond_destroy_bool(key, booldatum, NULL); | 255 | cond_destroy_bool(key, booldatum, NULL); |
249 | return -1; | 256 | return rc; |
250 | } | 257 | } |
251 | 258 | ||
252 | struct cond_insertf_data { | 259 | struct cond_insertf_data { |
@@ -263,7 +270,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
263 | struct cond_av_list *other = data->other, *list, *cur; | 270 | struct cond_av_list *other = data->other, *list, *cur; |
264 | struct avtab_node *node_ptr; | 271 | struct avtab_node *node_ptr; |
265 | u8 found; | 272 | u8 found; |
266 | 273 | int rc = -EINVAL; | |
267 | 274 | ||
268 | /* | 275 | /* |
269 | * For type rules we have to make certain there aren't any | 276 | * For type rules we have to make certain there aren't any |
@@ -313,12 +320,15 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
313 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); | 320 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); |
314 | if (!node_ptr) { | 321 | if (!node_ptr) { |
315 | printk(KERN_ERR "SELinux: could not insert rule.\n"); | 322 | printk(KERN_ERR "SELinux: could not insert rule.\n"); |
323 | rc = -ENOMEM; | ||
316 | goto err; | 324 | goto err; |
317 | } | 325 | } |
318 | 326 | ||
319 | list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); | 327 | list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); |
320 | if (!list) | 328 | if (!list) { |
329 | rc = -ENOMEM; | ||
321 | goto err; | 330 | goto err; |
331 | } | ||
322 | 332 | ||
323 | list->node = node_ptr; | 333 | list->node = node_ptr; |
324 | if (!data->head) | 334 | if (!data->head) |
@@ -331,7 +341,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum | |||
331 | err: | 341 | err: |
332 | cond_av_list_destroy(data->head); | 342 | cond_av_list_destroy(data->head); |
333 | data->head = NULL; | 343 | data->head = NULL; |
334 | return -1; | 344 | return rc; |
335 | } | 345 | } |
336 | 346 | ||
337 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) | 347 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) |
@@ -345,8 +355,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * | |||
345 | 355 | ||
346 | len = 0; | 356 | len = 0; |
347 | rc = next_entry(buf, fp, sizeof(u32)); | 357 | rc = next_entry(buf, fp, sizeof(u32)); |
348 | if (rc < 0) | 358 | if (rc) |
349 | return -1; | 359 | return rc; |
350 | 360 | ||
351 | len = le32_to_cpu(buf[0]); | 361 | len = le32_to_cpu(buf[0]); |
352 | if (len == 0) | 362 | if (len == 0) |
@@ -361,7 +371,6 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * | |||
361 | &data); | 371 | &data); |
362 | if (rc) | 372 | if (rc) |
363 | return rc; | 373 | return rc; |
364 | |||
365 | } | 374 | } |
366 | 375 | ||
367 | *ret_list = data.head; | 376 | *ret_list = data.head; |
@@ -390,24 +399,25 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
390 | struct cond_expr *expr = NULL, *last = NULL; | 399 | struct cond_expr *expr = NULL, *last = NULL; |
391 | 400 | ||
392 | rc = next_entry(buf, fp, sizeof(u32)); | 401 | rc = next_entry(buf, fp, sizeof(u32)); |
393 | if (rc < 0) | 402 | if (rc) |
394 | return -1; | 403 | return rc; |
395 | 404 | ||
396 | node->cur_state = le32_to_cpu(buf[0]); | 405 | node->cur_state = le32_to_cpu(buf[0]); |
397 | 406 | ||
398 | len = 0; | 407 | len = 0; |
399 | rc = next_entry(buf, fp, sizeof(u32)); | 408 | rc = next_entry(buf, fp, sizeof(u32)); |
400 | if (rc < 0) | 409 | if (rc) |
401 | return -1; | 410 | return rc; |
402 | 411 | ||
403 | /* expr */ | 412 | /* expr */ |
404 | len = le32_to_cpu(buf[0]); | 413 | len = le32_to_cpu(buf[0]); |
405 | 414 | ||
406 | for (i = 0; i < len; i++) { | 415 | for (i = 0; i < len; i++) { |
407 | rc = next_entry(buf, fp, sizeof(u32) * 2); | 416 | rc = next_entry(buf, fp, sizeof(u32) * 2); |
408 | if (rc < 0) | 417 | if (rc) |
409 | goto err; | 418 | goto err; |
410 | 419 | ||
420 | rc = -ENOMEM; | ||
411 | expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); | 421 | expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); |
412 | if (!expr) | 422 | if (!expr) |
413 | goto err; | 423 | goto err; |
@@ -416,6 +426,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
416 | expr->bool = le32_to_cpu(buf[1]); | 426 | expr->bool = le32_to_cpu(buf[1]); |
417 | 427 | ||
418 | if (!expr_isvalid(p, expr)) { | 428 | if (!expr_isvalid(p, expr)) { |
429 | rc = -EINVAL; | ||
419 | kfree(expr); | 430 | kfree(expr); |
420 | goto err; | 431 | goto err; |
421 | } | 432 | } |
@@ -427,14 +438,16 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | |||
427 | last = expr; | 438 | last = expr; |
428 | } | 439 | } |
429 | 440 | ||
430 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | 441 | rc = cond_read_av_list(p, fp, &node->true_list, NULL); |
442 | if (rc) | ||
431 | goto err; | 443 | goto err; |
432 | if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | 444 | rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); |
445 | if (rc) | ||
433 | goto err; | 446 | goto err; |
434 | return 0; | 447 | return 0; |
435 | err: | 448 | err: |
436 | cond_node_destroy(node); | 449 | cond_node_destroy(node); |
437 | return -1; | 450 | return rc; |
438 | } | 451 | } |
439 | 452 | ||
440 | int cond_read_list(struct policydb *p, void *fp) | 453 | int cond_read_list(struct policydb *p, void *fp) |
@@ -445,8 +458,8 @@ int cond_read_list(struct policydb *p, void *fp) | |||
445 | int rc; | 458 | int rc; |
446 | 459 | ||
447 | rc = next_entry(buf, fp, sizeof buf); | 460 | rc = next_entry(buf, fp, sizeof buf); |
448 | if (rc < 0) | 461 | if (rc) |
449 | return -1; | 462 | return rc; |
450 | 463 | ||
451 | len = le32_to_cpu(buf[0]); | 464 | len = le32_to_cpu(buf[0]); |
452 | 465 | ||
@@ -455,11 +468,13 @@ int cond_read_list(struct policydb *p, void *fp) | |||
455 | goto err; | 468 | goto err; |
456 | 469 | ||
457 | for (i = 0; i < len; i++) { | 470 | for (i = 0; i < len; i++) { |
471 | rc = -ENOMEM; | ||
458 | node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); | 472 | node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); |
459 | if (!node) | 473 | if (!node) |
460 | goto err; | 474 | goto err; |
461 | 475 | ||
462 | if (cond_read_node(p, node, fp) != 0) | 476 | rc = cond_read_node(p, node, fp); |
477 | if (rc) | ||
463 | goto err; | 478 | goto err; |
464 | 479 | ||
465 | if (i == 0) | 480 | if (i == 0) |
@@ -472,7 +487,7 @@ int cond_read_list(struct policydb *p, void *fp) | |||
472 | err: | 487 | err: |
473 | cond_list_destroy(p->cond_list); | 488 | cond_list_destroy(p->cond_list); |
474 | p->cond_list = NULL; | 489 | p->cond_list = NULL; |
475 | return -1; | 490 | return rc; |
476 | } | 491 | } |
477 | 492 | ||
478 | /* Determine whether additional permissions are granted by the conditional | 493 | /* Determine whether additional permissions are granted by the conditional |
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c57802a164d..3a29704be8c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/string.h> | 31 | #include <linux/string.h> |
32 | #include <linux/errno.h> | 32 | #include <linux/errno.h> |
33 | #include <linux/audit.h> | 33 | #include <linux/audit.h> |
34 | #include <linux/flex_array.h> | ||
34 | #include "security.h" | 35 | #include "security.h" |
35 | 36 | ||
36 | #include "policydb.h" | 37 | #include "policydb.h" |
@@ -655,6 +656,9 @@ static int range_tr_destroy(void *key, void *datum, void *p) | |||
655 | 656 | ||
656 | static void ocontext_destroy(struct ocontext *c, int i) | 657 | static void ocontext_destroy(struct ocontext *c, int i) |
657 | { | 658 | { |
659 | if (!c) | ||
660 | return; | ||
661 | |||
658 | context_destroy(&c->context[0]); | 662 | context_destroy(&c->context[0]); |
659 | context_destroy(&c->context[1]); | 663 | context_destroy(&c->context[1]); |
660 | if (i == OCON_ISID || i == OCON_FS || | 664 | if (i == OCON_ISID || i == OCON_FS || |
@@ -736,11 +740,17 @@ void policydb_destroy(struct policydb *p) | |||
736 | hashtab_map(p->range_tr, range_tr_destroy, NULL); | 740 | hashtab_map(p->range_tr, range_tr_destroy, NULL); |
737 | hashtab_destroy(p->range_tr); | 741 | hashtab_destroy(p->range_tr); |
738 | 742 | ||
739 | if (p->type_attr_map) { | 743 | if (p->type_attr_map_array) { |
740 | for (i = 0; i < p->p_types.nprim; i++) | 744 | for (i = 0; i < p->p_types.nprim; i++) { |
741 | ebitmap_destroy(&p->type_attr_map[i]); | 745 | struct ebitmap *e; |
746 | |||
747 | e = flex_array_get(p->type_attr_map_array, i); | ||
748 | if (!e) | ||
749 | continue; | ||
750 | ebitmap_destroy(e); | ||
751 | } | ||
752 | flex_array_free(p->type_attr_map_array); | ||
742 | } | 753 | } |
743 | kfree(p->type_attr_map); | ||
744 | ebitmap_destroy(&p->policycaps); | 754 | ebitmap_destroy(&p->policycaps); |
745 | ebitmap_destroy(&p->permissive_map); | 755 | ebitmap_destroy(&p->permissive_map); |
746 | 756 | ||
@@ -1701,6 +1711,333 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) | |||
1701 | return 1U << (perdatum->value-1); | 1711 | return 1U << (perdatum->value-1); |
1702 | } | 1712 | } |
1703 | 1713 | ||
1714 | static int range_read(struct policydb *p, void *fp) | ||
1715 | { | ||
1716 | struct range_trans *rt = NULL; | ||
1717 | struct mls_range *r = NULL; | ||
1718 | int i, rc; | ||
1719 | __le32 buf[2]; | ||
1720 | u32 nel; | ||
1721 | |||
1722 | if (p->policyvers < POLICYDB_VERSION_MLS) | ||
1723 | return 0; | ||
1724 | |||
1725 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1726 | if (rc) | ||
1727 | goto out; | ||
1728 | |||
1729 | nel = le32_to_cpu(buf[0]); | ||
1730 | for (i = 0; i < nel; i++) { | ||
1731 | rc = -ENOMEM; | ||
1732 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); | ||
1733 | if (!rt) | ||
1734 | goto out; | ||
1735 | |||
1736 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
1737 | if (rc) | ||
1738 | goto out; | ||
1739 | |||
1740 | rt->source_type = le32_to_cpu(buf[0]); | ||
1741 | rt->target_type = le32_to_cpu(buf[1]); | ||
1742 | if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { | ||
1743 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1744 | if (rc) | ||
1745 | goto out; | ||
1746 | rt->target_class = le32_to_cpu(buf[0]); | ||
1747 | } else | ||
1748 | rt->target_class = p->process_class; | ||
1749 | |||
1750 | rc = -EINVAL; | ||
1751 | if (!policydb_type_isvalid(p, rt->source_type) || | ||
1752 | !policydb_type_isvalid(p, rt->target_type) || | ||
1753 | !policydb_class_isvalid(p, rt->target_class)) | ||
1754 | goto out; | ||
1755 | |||
1756 | rc = -ENOMEM; | ||
1757 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
1758 | if (!r) | ||
1759 | goto out; | ||
1760 | |||
1761 | rc = mls_read_range_helper(r, fp); | ||
1762 | if (rc) | ||
1763 | goto out; | ||
1764 | |||
1765 | rc = -EINVAL; | ||
1766 | if (!mls_range_isvalid(p, r)) { | ||
1767 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); | ||
1768 | goto out; | ||
1769 | } | ||
1770 | |||
1771 | rc = hashtab_insert(p->range_tr, rt, r); | ||
1772 | if (rc) | ||
1773 | goto out; | ||
1774 | |||
1775 | rt = NULL; | ||
1776 | r = NULL; | ||
1777 | } | ||
1778 | rangetr_hash_eval(p->range_tr); | ||
1779 | rc = 0; | ||
1780 | out: | ||
1781 | kfree(rt); | ||
1782 | kfree(r); | ||
1783 | return rc; | ||
1784 | } | ||
1785 | |||
1786 | static int genfs_read(struct policydb *p, void *fp) | ||
1787 | { | ||
1788 | int i, j, rc; | ||
1789 | u32 nel, nel2, len, len2; | ||
1790 | __le32 buf[1]; | ||
1791 | struct ocontext *l, *c; | ||
1792 | struct ocontext *newc = NULL; | ||
1793 | struct genfs *genfs_p, *genfs; | ||
1794 | struct genfs *newgenfs = NULL; | ||
1795 | |||
1796 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1797 | if (rc) | ||
1798 | goto out; | ||
1799 | nel = le32_to_cpu(buf[0]); | ||
1800 | |||
1801 | for (i = 0; i < nel; i++) { | ||
1802 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1803 | if (rc) | ||
1804 | goto out; | ||
1805 | len = le32_to_cpu(buf[0]); | ||
1806 | |||
1807 | rc = -ENOMEM; | ||
1808 | newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
1809 | if (!newgenfs) | ||
1810 | goto out; | ||
1811 | |||
1812 | rc = -ENOMEM; | ||
1813 | newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); | ||
1814 | if (!newgenfs->fstype) | ||
1815 | goto out; | ||
1816 | |||
1817 | rc = next_entry(newgenfs->fstype, fp, len); | ||
1818 | if (rc) | ||
1819 | goto out; | ||
1820 | |||
1821 | newgenfs->fstype[len] = 0; | ||
1822 | |||
1823 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
1824 | genfs_p = genfs, genfs = genfs->next) { | ||
1825 | rc = -EINVAL; | ||
1826 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
1827 | printk(KERN_ERR "SELinux: dup genfs fstype %s\n", | ||
1828 | newgenfs->fstype); | ||
1829 | goto out; | ||
1830 | } | ||
1831 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
1832 | break; | ||
1833 | } | ||
1834 | newgenfs->next = genfs; | ||
1835 | if (genfs_p) | ||
1836 | genfs_p->next = newgenfs; | ||
1837 | else | ||
1838 | p->genfs = newgenfs; | ||
1839 | genfs = newgenfs; | ||
1840 | newgenfs = NULL; | ||
1841 | |||
1842 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1843 | if (rc) | ||
1844 | goto out; | ||
1845 | |||
1846 | nel2 = le32_to_cpu(buf[0]); | ||
1847 | for (j = 0; j < nel2; j++) { | ||
1848 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1849 | if (rc) | ||
1850 | goto out; | ||
1851 | len = le32_to_cpu(buf[0]); | ||
1852 | |||
1853 | rc = -ENOMEM; | ||
1854 | newc = kzalloc(sizeof(*newc), GFP_KERNEL); | ||
1855 | if (!newc) | ||
1856 | goto out; | ||
1857 | |||
1858 | rc = -ENOMEM; | ||
1859 | newc->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
1860 | if (!newc->u.name) | ||
1861 | goto out; | ||
1862 | |||
1863 | rc = next_entry(newc->u.name, fp, len); | ||
1864 | if (rc) | ||
1865 | goto out; | ||
1866 | newc->u.name[len] = 0; | ||
1867 | |||
1868 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1869 | if (rc) | ||
1870 | goto out; | ||
1871 | |||
1872 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
1873 | rc = context_read_and_validate(&newc->context[0], p, fp); | ||
1874 | if (rc) | ||
1875 | goto out; | ||
1876 | |||
1877 | for (l = NULL, c = genfs->head; c; | ||
1878 | l = c, c = c->next) { | ||
1879 | rc = -EINVAL; | ||
1880 | if (!strcmp(newc->u.name, c->u.name) && | ||
1881 | (!c->v.sclass || !newc->v.sclass || | ||
1882 | newc->v.sclass == c->v.sclass)) { | ||
1883 | printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", | ||
1884 | genfs->fstype, c->u.name); | ||
1885 | goto out; | ||
1886 | } | ||
1887 | len = strlen(newc->u.name); | ||
1888 | len2 = strlen(c->u.name); | ||
1889 | if (len > len2) | ||
1890 | break; | ||
1891 | } | ||
1892 | |||
1893 | newc->next = c; | ||
1894 | if (l) | ||
1895 | l->next = newc; | ||
1896 | else | ||
1897 | genfs->head = newc; | ||
1898 | newc = NULL; | ||
1899 | } | ||
1900 | } | ||
1901 | rc = 0; | ||
1902 | out: | ||
1903 | if (newgenfs) | ||
1904 | kfree(newgenfs->fstype); | ||
1905 | kfree(newgenfs); | ||
1906 | ocontext_destroy(newc, OCON_FSUSE); | ||
1907 | |||
1908 | return rc; | ||
1909 | } | ||
1910 | |||
1911 | static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, | ||
1912 | void *fp) | ||
1913 | { | ||
1914 | int i, j, rc; | ||
1915 | u32 nel, len; | ||
1916 | __le32 buf[3]; | ||
1917 | struct ocontext *l, *c; | ||
1918 | u32 nodebuf[8]; | ||
1919 | |||
1920 | for (i = 0; i < info->ocon_num; i++) { | ||
1921 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1922 | if (rc) | ||
1923 | goto out; | ||
1924 | nel = le32_to_cpu(buf[0]); | ||
1925 | |||
1926 | l = NULL; | ||
1927 | for (j = 0; j < nel; j++) { | ||
1928 | rc = -ENOMEM; | ||
1929 | c = kzalloc(sizeof(*c), GFP_KERNEL); | ||
1930 | if (!c) | ||
1931 | goto out; | ||
1932 | if (l) | ||
1933 | l->next = c; | ||
1934 | else | ||
1935 | p->ocontexts[i] = c; | ||
1936 | l = c; | ||
1937 | |||
1938 | switch (i) { | ||
1939 | case OCON_ISID: | ||
1940 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1941 | if (rc) | ||
1942 | goto out; | ||
1943 | |||
1944 | c->sid[0] = le32_to_cpu(buf[0]); | ||
1945 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1946 | if (rc) | ||
1947 | goto out; | ||
1948 | break; | ||
1949 | case OCON_FS: | ||
1950 | case OCON_NETIF: | ||
1951 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1952 | if (rc) | ||
1953 | goto out; | ||
1954 | len = le32_to_cpu(buf[0]); | ||
1955 | |||
1956 | rc = -ENOMEM; | ||
1957 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
1958 | if (!c->u.name) | ||
1959 | goto out; | ||
1960 | |||
1961 | rc = next_entry(c->u.name, fp, len); | ||
1962 | if (rc) | ||
1963 | goto out; | ||
1964 | |||
1965 | c->u.name[len] = 0; | ||
1966 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1967 | if (rc) | ||
1968 | goto out; | ||
1969 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
1970 | if (rc) | ||
1971 | goto out; | ||
1972 | break; | ||
1973 | case OCON_PORT: | ||
1974 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1975 | if (rc) | ||
1976 | goto out; | ||
1977 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
1978 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
1979 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
1980 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1981 | if (rc) | ||
1982 | goto out; | ||
1983 | break; | ||
1984 | case OCON_NODE: | ||
1985 | rc = next_entry(nodebuf, fp, sizeof(u32) * 2); | ||
1986 | if (rc) | ||
1987 | goto out; | ||
1988 | c->u.node.addr = nodebuf[0]; /* network order */ | ||
1989 | c->u.node.mask = nodebuf[1]; /* network order */ | ||
1990 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1991 | if (rc) | ||
1992 | goto out; | ||
1993 | break; | ||
1994 | case OCON_FSUSE: | ||
1995 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1996 | if (rc) | ||
1997 | goto out; | ||
1998 | |||
1999 | rc = -EINVAL; | ||
2000 | c->v.behavior = le32_to_cpu(buf[0]); | ||
2001 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
2002 | goto out; | ||
2003 | |||
2004 | rc = -ENOMEM; | ||
2005 | len = le32_to_cpu(buf[1]); | ||
2006 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
2007 | if (!c->u.name) | ||
2008 | goto out; | ||
2009 | |||
2010 | rc = next_entry(c->u.name, fp, len); | ||
2011 | if (rc) | ||
2012 | goto out; | ||
2013 | c->u.name[len] = 0; | ||
2014 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
2015 | if (rc) | ||
2016 | goto out; | ||
2017 | break; | ||
2018 | case OCON_NODE6: { | ||
2019 | int k; | ||
2020 | |||
2021 | rc = next_entry(nodebuf, fp, sizeof(u32) * 8); | ||
2022 | if (rc) | ||
2023 | goto out; | ||
2024 | for (k = 0; k < 4; k++) | ||
2025 | c->u.node6.addr[k] = nodebuf[k]; | ||
2026 | for (k = 0; k < 4; k++) | ||
2027 | c->u.node6.mask[k] = nodebuf[k+4]; | ||
2028 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
2029 | if (rc) | ||
2030 | goto out; | ||
2031 | break; | ||
2032 | } | ||
2033 | } | ||
2034 | } | ||
2035 | } | ||
2036 | rc = 0; | ||
2037 | out: | ||
2038 | return rc; | ||
2039 | } | ||
2040 | |||
1704 | /* | 2041 | /* |
1705 | * Read the configuration data from a policy database binary | 2042 | * Read the configuration data from a policy database binary |
1706 | * representation file into a policy database structure. | 2043 | * representation file into a policy database structure. |
@@ -1709,16 +2046,12 @@ int policydb_read(struct policydb *p, void *fp) | |||
1709 | { | 2046 | { |
1710 | struct role_allow *ra, *lra; | 2047 | struct role_allow *ra, *lra; |
1711 | struct role_trans *tr, *ltr; | 2048 | struct role_trans *tr, *ltr; |
1712 | struct ocontext *l, *c, *newc; | ||
1713 | struct genfs *genfs_p, *genfs, *newgenfs; | ||
1714 | int i, j, rc; | 2049 | int i, j, rc; |
1715 | __le32 buf[4]; | 2050 | __le32 buf[4]; |
1716 | u32 nodebuf[8]; | 2051 | u32 len, nprim, nel; |
1717 | u32 len, len2, nprim, nel, nel2; | 2052 | |
1718 | char *policydb_str; | 2053 | char *policydb_str; |
1719 | struct policydb_compat_info *info; | 2054 | struct policydb_compat_info *info; |
1720 | struct range_trans *rt; | ||
1721 | struct mls_range *r; | ||
1722 | 2055 | ||
1723 | rc = policydb_init(p); | 2056 | rc = policydb_init(p); |
1724 | if (rc) | 2057 | if (rc) |
@@ -1919,294 +2252,45 @@ int policydb_read(struct policydb *p, void *fp) | |||
1919 | if (!p->process_trans_perms) | 2252 | if (!p->process_trans_perms) |
1920 | goto bad; | 2253 | goto bad; |
1921 | 2254 | ||
1922 | for (i = 0; i < info->ocon_num; i++) { | 2255 | rc = ocontext_read(p, info, fp); |
1923 | rc = next_entry(buf, fp, sizeof(u32)); | 2256 | if (rc) |
1924 | if (rc < 0) | ||
1925 | goto bad; | ||
1926 | nel = le32_to_cpu(buf[0]); | ||
1927 | l = NULL; | ||
1928 | for (j = 0; j < nel; j++) { | ||
1929 | c = kzalloc(sizeof(*c), GFP_KERNEL); | ||
1930 | if (!c) { | ||
1931 | rc = -ENOMEM; | ||
1932 | goto bad; | ||
1933 | } | ||
1934 | if (l) | ||
1935 | l->next = c; | ||
1936 | else | ||
1937 | p->ocontexts[i] = c; | ||
1938 | l = c; | ||
1939 | rc = -EINVAL; | ||
1940 | switch (i) { | ||
1941 | case OCON_ISID: | ||
1942 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1943 | if (rc < 0) | ||
1944 | goto bad; | ||
1945 | c->sid[0] = le32_to_cpu(buf[0]); | ||
1946 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1947 | if (rc) | ||
1948 | goto bad; | ||
1949 | break; | ||
1950 | case OCON_FS: | ||
1951 | case OCON_NETIF: | ||
1952 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1953 | if (rc < 0) | ||
1954 | goto bad; | ||
1955 | len = le32_to_cpu(buf[0]); | ||
1956 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
1957 | if (!c->u.name) { | ||
1958 | rc = -ENOMEM; | ||
1959 | goto bad; | ||
1960 | } | ||
1961 | rc = next_entry(c->u.name, fp, len); | ||
1962 | if (rc < 0) | ||
1963 | goto bad; | ||
1964 | c->u.name[len] = 0; | ||
1965 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1966 | if (rc) | ||
1967 | goto bad; | ||
1968 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
1969 | if (rc) | ||
1970 | goto bad; | ||
1971 | break; | ||
1972 | case OCON_PORT: | ||
1973 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1974 | if (rc < 0) | ||
1975 | goto bad; | ||
1976 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
1977 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
1978 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
1979 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1980 | if (rc) | ||
1981 | goto bad; | ||
1982 | break; | ||
1983 | case OCON_NODE: | ||
1984 | rc = next_entry(nodebuf, fp, sizeof(u32) * 2); | ||
1985 | if (rc < 0) | ||
1986 | goto bad; | ||
1987 | c->u.node.addr = nodebuf[0]; /* network order */ | ||
1988 | c->u.node.mask = nodebuf[1]; /* network order */ | ||
1989 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1990 | if (rc) | ||
1991 | goto bad; | ||
1992 | break; | ||
1993 | case OCON_FSUSE: | ||
1994 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1995 | if (rc < 0) | ||
1996 | goto bad; | ||
1997 | c->v.behavior = le32_to_cpu(buf[0]); | ||
1998 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
1999 | goto bad; | ||
2000 | len = le32_to_cpu(buf[1]); | ||
2001 | c->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
2002 | if (!c->u.name) { | ||
2003 | rc = -ENOMEM; | ||
2004 | goto bad; | ||
2005 | } | ||
2006 | rc = next_entry(c->u.name, fp, len); | ||
2007 | if (rc < 0) | ||
2008 | goto bad; | ||
2009 | c->u.name[len] = 0; | ||
2010 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
2011 | if (rc) | ||
2012 | goto bad; | ||
2013 | break; | ||
2014 | case OCON_NODE6: { | ||
2015 | int k; | ||
2016 | |||
2017 | rc = next_entry(nodebuf, fp, sizeof(u32) * 8); | ||
2018 | if (rc < 0) | ||
2019 | goto bad; | ||
2020 | for (k = 0; k < 4; k++) | ||
2021 | c->u.node6.addr[k] = nodebuf[k]; | ||
2022 | for (k = 0; k < 4; k++) | ||
2023 | c->u.node6.mask[k] = nodebuf[k+4]; | ||
2024 | if (context_read_and_validate(&c->context[0], p, fp)) | ||
2025 | goto bad; | ||
2026 | break; | ||
2027 | } | ||
2028 | } | ||
2029 | } | ||
2030 | } | ||
2031 | |||
2032 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2033 | if (rc < 0) | ||
2034 | goto bad; | 2257 | goto bad; |
2035 | nel = le32_to_cpu(buf[0]); | ||
2036 | genfs_p = NULL; | ||
2037 | rc = -EINVAL; | ||
2038 | for (i = 0; i < nel; i++) { | ||
2039 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2040 | if (rc < 0) | ||
2041 | goto bad; | ||
2042 | len = le32_to_cpu(buf[0]); | ||
2043 | newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
2044 | if (!newgenfs) { | ||
2045 | rc = -ENOMEM; | ||
2046 | goto bad; | ||
2047 | } | ||
2048 | 2258 | ||
2049 | newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); | 2259 | rc = genfs_read(p, fp); |
2050 | if (!newgenfs->fstype) { | 2260 | if (rc) |
2051 | rc = -ENOMEM; | 2261 | goto bad; |
2052 | kfree(newgenfs); | ||
2053 | goto bad; | ||
2054 | } | ||
2055 | rc = next_entry(newgenfs->fstype, fp, len); | ||
2056 | if (rc < 0) { | ||
2057 | kfree(newgenfs->fstype); | ||
2058 | kfree(newgenfs); | ||
2059 | goto bad; | ||
2060 | } | ||
2061 | newgenfs->fstype[len] = 0; | ||
2062 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
2063 | genfs_p = genfs, genfs = genfs->next) { | ||
2064 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
2065 | printk(KERN_ERR "SELinux: dup genfs " | ||
2066 | "fstype %s\n", newgenfs->fstype); | ||
2067 | kfree(newgenfs->fstype); | ||
2068 | kfree(newgenfs); | ||
2069 | goto bad; | ||
2070 | } | ||
2071 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
2072 | break; | ||
2073 | } | ||
2074 | newgenfs->next = genfs; | ||
2075 | if (genfs_p) | ||
2076 | genfs_p->next = newgenfs; | ||
2077 | else | ||
2078 | p->genfs = newgenfs; | ||
2079 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2080 | if (rc < 0) | ||
2081 | goto bad; | ||
2082 | nel2 = le32_to_cpu(buf[0]); | ||
2083 | for (j = 0; j < nel2; j++) { | ||
2084 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2085 | if (rc < 0) | ||
2086 | goto bad; | ||
2087 | len = le32_to_cpu(buf[0]); | ||
2088 | |||
2089 | newc = kzalloc(sizeof(*newc), GFP_KERNEL); | ||
2090 | if (!newc) { | ||
2091 | rc = -ENOMEM; | ||
2092 | goto bad; | ||
2093 | } | ||
2094 | |||
2095 | newc->u.name = kmalloc(len + 1, GFP_KERNEL); | ||
2096 | if (!newc->u.name) { | ||
2097 | rc = -ENOMEM; | ||
2098 | goto bad_newc; | ||
2099 | } | ||
2100 | rc = next_entry(newc->u.name, fp, len); | ||
2101 | if (rc < 0) | ||
2102 | goto bad_newc; | ||
2103 | newc->u.name[len] = 0; | ||
2104 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2105 | if (rc < 0) | ||
2106 | goto bad_newc; | ||
2107 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
2108 | if (context_read_and_validate(&newc->context[0], p, fp)) | ||
2109 | goto bad_newc; | ||
2110 | for (l = NULL, c = newgenfs->head; c; | ||
2111 | l = c, c = c->next) { | ||
2112 | if (!strcmp(newc->u.name, c->u.name) && | ||
2113 | (!c->v.sclass || !newc->v.sclass || | ||
2114 | newc->v.sclass == c->v.sclass)) { | ||
2115 | printk(KERN_ERR "SELinux: dup genfs " | ||
2116 | "entry (%s,%s)\n", | ||
2117 | newgenfs->fstype, c->u.name); | ||
2118 | goto bad_newc; | ||
2119 | } | ||
2120 | len = strlen(newc->u.name); | ||
2121 | len2 = strlen(c->u.name); | ||
2122 | if (len > len2) | ||
2123 | break; | ||
2124 | } | ||
2125 | 2262 | ||
2126 | newc->next = c; | 2263 | rc = range_read(p, fp); |
2127 | if (l) | 2264 | if (rc) |
2128 | l->next = newc; | 2265 | goto bad; |
2129 | else | ||
2130 | newgenfs->head = newc; | ||
2131 | } | ||
2132 | } | ||
2133 | 2266 | ||
2134 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | 2267 | rc = -ENOMEM; |
2135 | int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS; | 2268 | p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), |
2136 | rc = next_entry(buf, fp, sizeof(u32)); | 2269 | p->p_types.nprim, |
2137 | if (rc < 0) | 2270 | GFP_KERNEL | __GFP_ZERO); |
2138 | goto bad; | 2271 | if (!p->type_attr_map_array) |
2139 | nel = le32_to_cpu(buf[0]); | 2272 | goto bad; |
2140 | for (i = 0; i < nel; i++) { | ||
2141 | rt = kzalloc(sizeof(*rt), GFP_KERNEL); | ||
2142 | if (!rt) { | ||
2143 | rc = -ENOMEM; | ||
2144 | goto bad; | ||
2145 | } | ||
2146 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
2147 | if (rc < 0) { | ||
2148 | kfree(rt); | ||
2149 | goto bad; | ||
2150 | } | ||
2151 | rt->source_type = le32_to_cpu(buf[0]); | ||
2152 | rt->target_type = le32_to_cpu(buf[1]); | ||
2153 | if (new_rangetr) { | ||
2154 | rc = next_entry(buf, fp, sizeof(u32)); | ||
2155 | if (rc < 0) { | ||
2156 | kfree(rt); | ||
2157 | goto bad; | ||
2158 | } | ||
2159 | rt->target_class = le32_to_cpu(buf[0]); | ||
2160 | } else | ||
2161 | rt->target_class = p->process_class; | ||
2162 | if (!policydb_type_isvalid(p, rt->source_type) || | ||
2163 | !policydb_type_isvalid(p, rt->target_type) || | ||
2164 | !policydb_class_isvalid(p, rt->target_class)) { | ||
2165 | kfree(rt); | ||
2166 | rc = -EINVAL; | ||
2167 | goto bad; | ||
2168 | } | ||
2169 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
2170 | if (!r) { | ||
2171 | kfree(rt); | ||
2172 | rc = -ENOMEM; | ||
2173 | goto bad; | ||
2174 | } | ||
2175 | rc = mls_read_range_helper(r, fp); | ||
2176 | if (rc) { | ||
2177 | kfree(rt); | ||
2178 | kfree(r); | ||
2179 | goto bad; | ||
2180 | } | ||
2181 | if (!mls_range_isvalid(p, r)) { | ||
2182 | printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); | ||
2183 | kfree(rt); | ||
2184 | kfree(r); | ||
2185 | goto bad; | ||
2186 | } | ||
2187 | rc = hashtab_insert(p->range_tr, rt, r); | ||
2188 | if (rc) { | ||
2189 | kfree(rt); | ||
2190 | kfree(r); | ||
2191 | goto bad; | ||
2192 | } | ||
2193 | } | ||
2194 | rangetr_hash_eval(p->range_tr); | ||
2195 | } | ||
2196 | 2273 | ||
2197 | p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL); | 2274 | /* preallocate so we don't have to worry about the put ever failing */ |
2198 | if (!p->type_attr_map) | 2275 | rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, |
2276 | GFP_KERNEL | __GFP_ZERO); | ||
2277 | if (rc) | ||
2199 | goto bad; | 2278 | goto bad; |
2200 | 2279 | ||
2201 | for (i = 0; i < p->p_types.nprim; i++) { | 2280 | for (i = 0; i < p->p_types.nprim; i++) { |
2202 | ebitmap_init(&p->type_attr_map[i]); | 2281 | struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); |
2282 | |||
2283 | BUG_ON(!e); | ||
2284 | ebitmap_init(e); | ||
2203 | if (p->policyvers >= POLICYDB_VERSION_AVTAB) { | 2285 | if (p->policyvers >= POLICYDB_VERSION_AVTAB) { |
2204 | if (ebitmap_read(&p->type_attr_map[i], fp)) | 2286 | rc = ebitmap_read(e, fp); |
2287 | if (rc) | ||
2205 | goto bad; | 2288 | goto bad; |
2206 | } | 2289 | } |
2207 | /* add the type itself as the degenerate case */ | 2290 | /* add the type itself as the degenerate case */ |
2208 | if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) | 2291 | rc = ebitmap_set_bit(e, i, 1); |
2209 | goto bad; | 2292 | if (rc) |
2293 | goto bad; | ||
2210 | } | 2294 | } |
2211 | 2295 | ||
2212 | rc = policydb_bounds_sanity_check(p); | 2296 | rc = policydb_bounds_sanity_check(p); |
@@ -2216,8 +2300,6 @@ int policydb_read(struct policydb *p, void *fp) | |||
2216 | rc = 0; | 2300 | rc = 0; |
2217 | out: | 2301 | out: |
2218 | return rc; | 2302 | return rc; |
2219 | bad_newc: | ||
2220 | ocontext_destroy(newc, OCON_FSUSE); | ||
2221 | bad: | 2303 | bad: |
2222 | if (!rc) | 2304 | if (!rc) |
2223 | rc = -EINVAL; | 2305 | rc = -EINVAL; |
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 26d9adf8542..310e94442cb 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h | |||
@@ -24,6 +24,8 @@ | |||
24 | #ifndef _SS_POLICYDB_H_ | 24 | #ifndef _SS_POLICYDB_H_ |
25 | #define _SS_POLICYDB_H_ | 25 | #define _SS_POLICYDB_H_ |
26 | 26 | ||
27 | #include <linux/flex_array.h> | ||
28 | |||
27 | #include "symtab.h" | 29 | #include "symtab.h" |
28 | #include "avtab.h" | 30 | #include "avtab.h" |
29 | #include "sidtab.h" | 31 | #include "sidtab.h" |
@@ -246,7 +248,7 @@ struct policydb { | |||
246 | struct hashtab *range_tr; | 248 | struct hashtab *range_tr; |
247 | 249 | ||
248 | /* type -> attribute reverse mapping */ | 250 | /* type -> attribute reverse mapping */ |
249 | struct ebitmap *type_attr_map; | 251 | struct flex_array *type_attr_map_array; |
250 | 252 | ||
251 | struct ebitmap policycaps; | 253 | struct ebitmap policycaps; |
252 | 254 | ||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1de60ce90d9..9ea2feca3cd 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <linux/audit.h> | 50 | #include <linux/audit.h> |
51 | #include <linux/mutex.h> | 51 | #include <linux/mutex.h> |
52 | #include <linux/selinux.h> | 52 | #include <linux/selinux.h> |
53 | #include <linux/flex_array.h> | ||
53 | #include <net/netlabel.h> | 54 | #include <net/netlabel.h> |
54 | 55 | ||
55 | #include "flask.h" | 56 | #include "flask.h" |
@@ -626,8 +627,10 @@ static void context_struct_compute_av(struct context *scontext, | |||
626 | */ | 627 | */ |
627 | avkey.target_class = tclass; | 628 | avkey.target_class = tclass; |
628 | avkey.specified = AVTAB_AV; | 629 | avkey.specified = AVTAB_AV; |
629 | sattr = &policydb.type_attr_map[scontext->type - 1]; | 630 | sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); |
630 | tattr = &policydb.type_attr_map[tcontext->type - 1]; | 631 | BUG_ON(!sattr); |
632 | tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); | ||
633 | BUG_ON(!tattr); | ||
631 | ebitmap_for_each_positive_bit(sattr, snode, i) { | 634 | ebitmap_for_each_positive_bit(sattr, snode, i) { |
632 | ebitmap_for_each_positive_bit(tattr, tnode, j) { | 635 | ebitmap_for_each_positive_bit(tattr, tnode, j) { |
633 | avkey.source_type = i + 1; | 636 | avkey.source_type = i + 1; |
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index bcf9f620426..160326ee99e 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c | |||
@@ -36,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) | |||
36 | { | 36 | { |
37 | s->table = hashtab_create(symhash, symcmp, size); | 37 | s->table = hashtab_create(symhash, symcmp, size); |
38 | if (!s->table) | 38 | if (!s->table) |
39 | return -1; | 39 | return -ENOMEM; |
40 | s->nprim = 0; | 40 | s->nprim = 0; |
41 | return 0; | 41 | return 0; |
42 | } | 42 | } |
diff --git a/security/smack/smack.h b/security/smack/smack.h index c6e9acae72e..43ae747a5aa 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h | |||
@@ -123,16 +123,6 @@ struct smack_known { | |||
123 | #define SMK_FSHAT "smackfshat=" | 123 | #define SMK_FSHAT "smackfshat=" |
124 | #define SMK_FSROOT "smackfsroot=" | 124 | #define SMK_FSROOT "smackfsroot=" |
125 | 125 | ||
126 | /* | ||
127 | * xattr names | ||
128 | */ | ||
129 | #define XATTR_SMACK_SUFFIX "SMACK64" | ||
130 | #define XATTR_SMACK_IPIN "SMACK64IPIN" | ||
131 | #define XATTR_SMACK_IPOUT "SMACK64IPOUT" | ||
132 | #define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX | ||
133 | #define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN | ||
134 | #define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT | ||
135 | |||
136 | #define SMACK_CIPSO_OPTION "-CIPSO" | 126 | #define SMACK_CIPSO_OPTION "-CIPSO" |
137 | 127 | ||
138 | /* | 128 | /* |
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0f2fc480fc6..c448d57ae2b 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c | |||
@@ -598,6 +598,8 @@ static int smack_inode_rename(struct inode *old_inode, | |||
598 | static int smack_inode_permission(struct inode *inode, int mask) | 598 | static int smack_inode_permission(struct inode *inode, int mask) |
599 | { | 599 | { |
600 | struct smk_audit_info ad; | 600 | struct smk_audit_info ad; |
601 | |||
602 | mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); | ||
601 | /* | 603 | /* |
602 | * No permission to check. Existence test. Yup, it's there. | 604 | * No permission to check. Existence test. Yup, it's there. |
603 | */ | 605 | */ |
@@ -2191,7 +2193,7 @@ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) | |||
2191 | 2193 | ||
2192 | /** | 2194 | /** |
2193 | * smack_d_instantiate - Make sure the blob is correct on an inode | 2195 | * smack_d_instantiate - Make sure the blob is correct on an inode |
2194 | * @opt_dentry: unused | 2196 | * @opt_dentry: dentry where inode will be attached |
2195 | * @inode: the object | 2197 | * @inode: the object |
2196 | * | 2198 | * |
2197 | * Set the inode's security blob if it hasn't been done already. | 2199 | * Set the inode's security blob if it hasn't been done already. |
@@ -2310,20 +2312,10 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) | |||
2310 | /* | 2312 | /* |
2311 | * Get the dentry for xattr. | 2313 | * Get the dentry for xattr. |
2312 | */ | 2314 | */ |
2313 | if (opt_dentry == NULL) { | 2315 | dp = dget(opt_dentry); |
2314 | dp = d_find_alias(inode); | ||
2315 | if (dp == NULL) | ||
2316 | break; | ||
2317 | } else { | ||
2318 | dp = dget(opt_dentry); | ||
2319 | if (dp == NULL) | ||
2320 | break; | ||
2321 | } | ||
2322 | |||
2323 | fetched = smk_fetch(inode, dp); | 2316 | fetched = smk_fetch(inode, dp); |
2324 | if (fetched != NULL) | 2317 | if (fetched != NULL) |
2325 | final = fetched; | 2318 | final = fetched; |
2326 | |||
2327 | dput(dp); | 2319 | dput(dp); |
2328 | break; | 2320 | break; |
2329 | } | 2321 | } |
@@ -3227,7 +3219,7 @@ static __init int smack_init(void) | |||
3227 | cred = (struct cred *) current->cred; | 3219 | cred = (struct cred *) current->cred; |
3228 | cred->security = &smack_known_floor.smk_known; | 3220 | cred->security = &smack_known_floor.smk_known; |
3229 | 3221 | ||
3230 | /* initilize the smack_know_list */ | 3222 | /* initialize the smack_know_list */ |
3231 | init_smack_know_list(); | 3223 | init_smack_know_list(); |
3232 | /* | 3224 | /* |
3233 | * Initialize locks | 3225 | * Initialize locks |
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 4fb39030f6b..91640e96bd0 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile | |||
@@ -1 +1 @@ | |||
obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o | obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o | ||
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index b5dbdc9ff73..ef43995119a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
@@ -3,974 +3,424 @@ | |||
3 | * | 3 | * |
4 | * Common functions for TOMOYO. | 4 | * Common functions for TOMOYO. |
5 | * | 5 | * |
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
7 | * | ||
8 | * Version: 2.2.0 2009/04/01 | ||
9 | * | ||
10 | */ | 7 | */ |
11 | 8 | ||
12 | #include <linux/uaccess.h> | 9 | #include <linux/uaccess.h> |
13 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
14 | #include <linux/security.h> | 11 | #include <linux/security.h> |
15 | #include <linux/hardirq.h> | ||
16 | #include "common.h" | 12 | #include "common.h" |
17 | 13 | ||
18 | /* Lock for protecting policy. */ | 14 | static struct tomoyo_profile tomoyo_default_profile = { |
19 | DEFINE_MUTEX(tomoyo_policy_lock); | 15 | .learning = &tomoyo_default_profile.preference, |
16 | .permissive = &tomoyo_default_profile.preference, | ||
17 | .enforcing = &tomoyo_default_profile.preference, | ||
18 | .preference.enforcing_verbose = true, | ||
19 | .preference.learning_max_entry = 2048, | ||
20 | .preference.learning_verbose = false, | ||
21 | .preference.permissive_verbose = true | ||
22 | }; | ||
23 | |||
24 | /* Profile version. Currently only 20090903 is defined. */ | ||
25 | static unsigned int tomoyo_profile_version; | ||
20 | 26 | ||
21 | /* Has loading policy done? */ | 27 | /* Profile table. Memory is allocated as needed. */ |
22 | bool tomoyo_policy_loaded; | 28 | static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; |
23 | 29 | ||
24 | /* String table for functionality that takes 4 modes. */ | 30 | /* String table for functionality that takes 4 modes. */ |
25 | static const char *tomoyo_mode_4[4] = { | 31 | static const char *tomoyo_mode[4] = { |
26 | "disabled", "learning", "permissive", "enforcing" | 32 | "disabled", "learning", "permissive", "enforcing" |
27 | }; | 33 | }; |
28 | /* String table for functionality that takes 2 modes. */ | ||
29 | static const char *tomoyo_mode_2[4] = { | ||
30 | "disabled", "enabled", "enabled", "enabled" | ||
31 | }; | ||
32 | 34 | ||
33 | /* | 35 | /* String table for /sys/kernel/security/tomoyo/profile */ |
34 | * tomoyo_control_array is a static data which contains | 36 | static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX |
35 | * | 37 | + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
36 | * (1) functionality name used by /sys/kernel/security/tomoyo/profile . | 38 | [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", |
37 | * (2) initial values for "struct tomoyo_profile". | 39 | [TOMOYO_MAC_FILE_OPEN] = "file::open", |
38 | * (3) max values for "struct tomoyo_profile". | 40 | [TOMOYO_MAC_FILE_CREATE] = "file::create", |
39 | */ | 41 | [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", |
40 | static struct { | 42 | [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", |
41 | const char *keyword; | 43 | [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", |
42 | unsigned int current_value; | 44 | [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", |
43 | const unsigned int max_value; | 45 | [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", |
44 | } tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = { | 46 | [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", |
45 | [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, | 47 | [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", |
46 | [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX }, | 48 | [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", |
47 | [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, | 49 | [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", |
50 | [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", | ||
51 | [TOMOYO_MAC_FILE_LINK] = "file::link", | ||
52 | [TOMOYO_MAC_FILE_RENAME] = "file::rename", | ||
53 | [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", | ||
54 | [TOMOYO_MAC_FILE_CHOWN] = "file::chown", | ||
55 | [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", | ||
56 | [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", | ||
57 | [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", | ||
58 | [TOMOYO_MAC_FILE_MOUNT] = "file::mount", | ||
59 | [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", | ||
60 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", | ||
61 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", | ||
48 | }; | 62 | }; |
49 | 63 | ||
50 | /* | ||
51 | * tomoyo_profile is a structure which is used for holding the mode of access | ||
52 | * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing. | ||
53 | * An administrator can define up to 256 profiles. | ||
54 | * The ->profile of "struct tomoyo_domain_info" is used for remembering | ||
55 | * the profile's number (0 - 255) assigned to that domain. | ||
56 | */ | ||
57 | static struct tomoyo_profile { | ||
58 | unsigned int value[TOMOYO_MAX_CONTROL_INDEX]; | ||
59 | const struct tomoyo_path_info *comment; | ||
60 | } *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; | ||
61 | |||
62 | /* Permit policy management by non-root user? */ | 64 | /* Permit policy management by non-root user? */ |
63 | static bool tomoyo_manage_by_non_root; | 65 | static bool tomoyo_manage_by_non_root; |
64 | 66 | ||
65 | /* Utility functions. */ | 67 | /* Utility functions. */ |
66 | 68 | ||
67 | /* Open operation for /sys/kernel/security/tomoyo/ interface. */ | ||
68 | static int tomoyo_open_control(const u8 type, struct file *file); | ||
69 | /* Close /sys/kernel/security/tomoyo/ interface. */ | ||
70 | static int tomoyo_close_control(struct file *file); | ||
71 | /* Read operation for /sys/kernel/security/tomoyo/ interface. */ | ||
72 | static int tomoyo_read_control(struct file *file, char __user *buffer, | ||
73 | const int buffer_len); | ||
74 | /* Write operation for /sys/kernel/security/tomoyo/ interface. */ | ||
75 | static int tomoyo_write_control(struct file *file, const char __user *buffer, | ||
76 | const int buffer_len); | ||
77 | |||
78 | /** | 69 | /** |
79 | * tomoyo_parse_name_union - Parse a tomoyo_name_union. | 70 | * tomoyo_yesno - Return "yes" or "no". |
80 | * | 71 | * |
81 | * @filename: Name or name group. | 72 | * @value: Bool value. |
82 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
83 | * | ||
84 | * Returns true on success, false otherwise. | ||
85 | */ | 73 | */ |
86 | bool tomoyo_parse_name_union(const char *filename, | 74 | static const char *tomoyo_yesno(const unsigned int value) |
87 | struct tomoyo_name_union *ptr) | ||
88 | { | 75 | { |
89 | if (!tomoyo_is_correct_path(filename, 0, 0, 0)) | 76 | return value ? "yes" : "no"; |
90 | return false; | ||
91 | if (filename[0] == '@') { | ||
92 | ptr->group = tomoyo_get_path_group(filename + 1); | ||
93 | ptr->is_group = true; | ||
94 | return ptr->group != NULL; | ||
95 | } | ||
96 | ptr->filename = tomoyo_get_name(filename); | ||
97 | ptr->is_group = false; | ||
98 | return ptr->filename != NULL; | ||
99 | } | 77 | } |
100 | 78 | ||
101 | /** | 79 | static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) |
102 | * tomoyo_print_name_union - Print a tomoyo_name_union. | ||
103 | * | ||
104 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
105 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
106 | * | ||
107 | * Returns true on success, false otherwise. | ||
108 | */ | ||
109 | static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, | ||
110 | const struct tomoyo_name_union *ptr) | ||
111 | { | 80 | { |
112 | int pos = head->read_avail; | 81 | va_list args; |
113 | if (pos && head->read_buf[pos - 1] == ' ') | 82 | const int pos = strlen(buffer); |
114 | head->read_avail--; | 83 | va_start(args, fmt); |
115 | if (ptr->is_group) | 84 | vsnprintf(buffer + pos, len - pos - 1, fmt, args); |
116 | return tomoyo_io_printf(head, " @%s", | 85 | va_end(args); |
117 | ptr->group->group_name->name); | ||
118 | return tomoyo_io_printf(head, " %s", ptr->filename->name); | ||
119 | } | 86 | } |
120 | 87 | ||
121 | /** | 88 | /** |
122 | * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. | 89 | * tomoyo_flush - Flush queued string to userspace's buffer. |
123 | * | ||
124 | * @str: Pointer to the string. | ||
125 | * | 90 | * |
126 | * Returns true if @str is a \ooo style octal value, false otherwise. | 91 | * @head: Pointer to "struct tomoyo_io_buffer". |
127 | * | 92 | * |
128 | * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. | 93 | * Returns true if all data was flushed, false otherwise. |
129 | * This function verifies that \ooo is in valid range. | ||
130 | */ | 94 | */ |
131 | static inline bool tomoyo_is_byte_range(const char *str) | 95 | static bool tomoyo_flush(struct tomoyo_io_buffer *head) |
132 | { | 96 | { |
133 | return *str >= '0' && *str++ <= '3' && | 97 | while (head->r.w_pos) { |
134 | *str >= '0' && *str++ <= '7' && | 98 | const char *w = head->r.w[0]; |
135 | *str >= '0' && *str <= '7'; | 99 | int len = strlen(w); |
100 | if (len) { | ||
101 | if (len > head->read_user_buf_avail) | ||
102 | len = head->read_user_buf_avail; | ||
103 | if (!len) | ||
104 | return false; | ||
105 | if (copy_to_user(head->read_user_buf, w, len)) | ||
106 | return false; | ||
107 | head->read_user_buf_avail -= len; | ||
108 | head->read_user_buf += len; | ||
109 | w += len; | ||
110 | } | ||
111 | if (*w) { | ||
112 | head->r.w[0] = w; | ||
113 | return false; | ||
114 | } | ||
115 | /* Add '\0' for query. */ | ||
116 | if (head->poll) { | ||
117 | if (!head->read_user_buf_avail || | ||
118 | copy_to_user(head->read_user_buf, "", 1)) | ||
119 | return false; | ||
120 | head->read_user_buf_avail--; | ||
121 | head->read_user_buf++; | ||
122 | } | ||
123 | head->r.w_pos--; | ||
124 | for (len = 0; len < head->r.w_pos; len++) | ||
125 | head->r.w[len] = head->r.w[len + 1]; | ||
126 | } | ||
127 | head->r.avail = 0; | ||
128 | return true; | ||
136 | } | 129 | } |
137 | 130 | ||
138 | /** | 131 | /** |
139 | * tomoyo_is_alphabet_char - Check whether the character is an alphabet. | 132 | * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure. |
140 | * | 133 | * |
141 | * @c: The character to check. | 134 | * @head: Pointer to "struct tomoyo_io_buffer". |
135 | * @string: String to print. | ||
142 | * | 136 | * |
143 | * Returns true if @c is an alphabet character, false otherwise. | 137 | * Note that @string has to be kept valid until @head is kfree()d. |
138 | * This means that char[] allocated on stack memory cannot be passed to | ||
139 | * this function. Use tomoyo_io_printf() for char[] allocated on stack memory. | ||
144 | */ | 140 | */ |
145 | static inline bool tomoyo_is_alphabet_char(const char c) | 141 | static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) |
146 | { | 142 | { |
147 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | 143 | if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) { |
144 | head->r.w[head->r.w_pos++] = string; | ||
145 | tomoyo_flush(head); | ||
146 | } else | ||
147 | WARN_ON(1); | ||
148 | } | 148 | } |
149 | 149 | ||
150 | /** | 150 | /** |
151 | * tomoyo_make_byte - Make byte value from three octal characters. | 151 | * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. |
152 | * | 152 | * |
153 | * @c1: The first character. | 153 | * @head: Pointer to "struct tomoyo_io_buffer". |
154 | * @c2: The second character. | 154 | * @fmt: The printf()'s format string, followed by parameters. |
155 | * @c3: The third character. | ||
156 | * | ||
157 | * Returns byte value. | ||
158 | */ | 155 | */ |
159 | static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) | 156 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) |
160 | { | 157 | { |
161 | return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | 158 | va_list args; |
159 | int len; | ||
160 | int pos = head->r.avail; | ||
161 | int size = head->readbuf_size - pos; | ||
162 | if (size <= 0) | ||
163 | return; | ||
164 | va_start(args, fmt); | ||
165 | len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | ||
166 | va_end(args); | ||
167 | if (pos + len >= head->readbuf_size) { | ||
168 | WARN_ON(1); | ||
169 | return; | ||
170 | } | ||
171 | head->r.avail += len; | ||
172 | tomoyo_set_string(head, head->read_buf + pos); | ||
162 | } | 173 | } |
163 | 174 | ||
164 | /** | 175 | static void tomoyo_set_space(struct tomoyo_io_buffer *head) |
165 | * tomoyo_str_starts - Check whether the given string starts with the given keyword. | ||
166 | * | ||
167 | * @src: Pointer to pointer to the string. | ||
168 | * @find: Pointer to the keyword. | ||
169 | * | ||
170 | * Returns true if @src starts with @find, false otherwise. | ||
171 | * | ||
172 | * The @src is updated to point the first character after the @find | ||
173 | * if @src starts with @find. | ||
174 | */ | ||
175 | static bool tomoyo_str_starts(char **src, const char *find) | ||
176 | { | 176 | { |
177 | const int len = strlen(find); | 177 | tomoyo_set_string(head, " "); |
178 | char *tmp = *src; | ||
179 | |||
180 | if (strncmp(tmp, find, len)) | ||
181 | return false; | ||
182 | tmp += len; | ||
183 | *src = tmp; | ||
184 | return true; | ||
185 | } | 178 | } |
186 | 179 | ||
187 | /** | 180 | static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) |
188 | * tomoyo_normalize_line - Format string. | ||
189 | * | ||
190 | * @buffer: The line to normalize. | ||
191 | * | ||
192 | * Leading and trailing whitespaces are removed. | ||
193 | * Multiple whitespaces are packed into single space. | ||
194 | * | ||
195 | * Returns nothing. | ||
196 | */ | ||
197 | static void tomoyo_normalize_line(unsigned char *buffer) | ||
198 | { | 181 | { |
199 | unsigned char *sp = buffer; | 182 | tomoyo_set_string(head, "\n"); |
200 | unsigned char *dp = buffer; | 183 | return !head->r.w_pos; |
201 | bool first = true; | ||
202 | |||
203 | while (tomoyo_is_invalid(*sp)) | ||
204 | sp++; | ||
205 | while (*sp) { | ||
206 | if (!first) | ||
207 | *dp++ = ' '; | ||
208 | first = false; | ||
209 | while (tomoyo_is_valid(*sp)) | ||
210 | *dp++ = *sp++; | ||
211 | while (tomoyo_is_invalid(*sp)) | ||
212 | sp++; | ||
213 | } | ||
214 | *dp = '\0'; | ||
215 | } | 184 | } |
216 | 185 | ||
217 | /** | 186 | /** |
218 | * tomoyo_tokenize - Tokenize string. | 187 | * tomoyo_print_name_union - Print a tomoyo_name_union. |
219 | * | ||
220 | * @buffer: The line to tokenize. | ||
221 | * @w: Pointer to "char *". | ||
222 | * @size: Sizeof @w . | ||
223 | * | 188 | * |
224 | * Returns true on success, false otherwise. | 189 | * @head: Pointer to "struct tomoyo_io_buffer". |
190 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
225 | */ | 191 | */ |
226 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size) | 192 | static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, |
193 | const struct tomoyo_name_union *ptr) | ||
227 | { | 194 | { |
228 | int count = size / sizeof(char *); | 195 | tomoyo_set_space(head); |
229 | int i; | 196 | if (ptr->is_group) { |
230 | for (i = 0; i < count; i++) | 197 | tomoyo_set_string(head, "@"); |
231 | w[i] = ""; | 198 | tomoyo_set_string(head, ptr->group->group_name->name); |
232 | for (i = 0; i < count; i++) { | 199 | } else { |
233 | char *cp = strchr(buffer, ' '); | 200 | tomoyo_set_string(head, ptr->filename->name); |
234 | if (cp) | ||
235 | *cp = '\0'; | ||
236 | w[i] = buffer; | ||
237 | if (!cp) | ||
238 | break; | ||
239 | buffer = cp + 1; | ||
240 | } | 201 | } |
241 | return i < count || !*buffer; | ||
242 | } | 202 | } |
243 | 203 | ||
244 | /** | 204 | /** |
245 | * tomoyo_is_correct_path - Validate a pathname. | 205 | * tomoyo_print_number_union - Print a tomoyo_number_union. |
246 | * @filename: The pathname to check. | 206 | * |
247 | * @start_type: Should the pathname start with '/'? | 207 | * @head: Pointer to "struct tomoyo_io_buffer". |
248 | * 1 = must / -1 = must not / 0 = don't care | 208 | * @ptr: Pointer to "struct tomoyo_number_union". |
249 | * @pattern_type: Can the pathname contain a wildcard? | ||
250 | * 1 = must / -1 = must not / 0 = don't care | ||
251 | * @end_type: Should the pathname end with '/'? | ||
252 | * 1 = must / -1 = must not / 0 = don't care | ||
253 | * | ||
254 | * Check whether the given filename follows the naming rules. | ||
255 | * Returns true if @filename follows the naming rules, false otherwise. | ||
256 | */ | 209 | */ |
257 | bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | 210 | static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, |
258 | const s8 pattern_type, const s8 end_type) | 211 | const struct tomoyo_number_union *ptr) |
259 | { | 212 | { |
260 | const char *const start = filename; | 213 | tomoyo_set_space(head); |
261 | bool in_repetition = false; | 214 | if (ptr->is_group) { |
262 | bool contains_pattern = false; | 215 | tomoyo_set_string(head, "@"); |
263 | unsigned char c; | 216 | tomoyo_set_string(head, ptr->group->group_name->name); |
264 | unsigned char d; | 217 | } else { |
265 | unsigned char e; | 218 | int i; |
266 | 219 | unsigned long min = ptr->values[0]; | |
267 | if (!filename) | 220 | const unsigned long max = ptr->values[1]; |
268 | goto out; | 221 | u8 min_type = ptr->min_type; |
269 | c = *filename; | 222 | const u8 max_type = ptr->max_type; |
270 | if (start_type == 1) { /* Must start with '/' */ | 223 | char buffer[128]; |
271 | if (c != '/') | 224 | buffer[0] = '\0'; |
272 | goto out; | 225 | for (i = 0; i < 2; i++) { |
273 | } else if (start_type == -1) { /* Must not start with '/' */ | 226 | switch (min_type) { |
274 | if (c == '/') | 227 | case TOMOYO_VALUE_TYPE_HEXADECIMAL: |
275 | goto out; | 228 | tomoyo_addprintf(buffer, sizeof(buffer), |
276 | } | 229 | "0x%lX", min); |
277 | if (c) | 230 | break; |
278 | c = *(filename + strlen(filename) - 1); | 231 | case TOMOYO_VALUE_TYPE_OCTAL: |
279 | if (end_type == 1) { /* Must end with '/' */ | 232 | tomoyo_addprintf(buffer, sizeof(buffer), |
280 | if (c != '/') | 233 | "0%lo", min); |
281 | goto out; | 234 | break; |
282 | } else if (end_type == -1) { /* Must not end with '/' */ | 235 | default: |
283 | if (c == '/') | 236 | tomoyo_addprintf(buffer, sizeof(buffer), |
284 | goto out; | 237 | "%lu", min); |
285 | } | 238 | break; |
286 | while (1) { | ||
287 | c = *filename++; | ||
288 | if (!c) | ||
289 | break; | ||
290 | if (c == '\\') { | ||
291 | c = *filename++; | ||
292 | switch (c) { | ||
293 | case '\\': /* "\\" */ | ||
294 | continue; | ||
295 | case '$': /* "\$" */ | ||
296 | case '+': /* "\+" */ | ||
297 | case '?': /* "\?" */ | ||
298 | case '*': /* "\*" */ | ||
299 | case '@': /* "\@" */ | ||
300 | case 'x': /* "\x" */ | ||
301 | case 'X': /* "\X" */ | ||
302 | case 'a': /* "\a" */ | ||
303 | case 'A': /* "\A" */ | ||
304 | case '-': /* "\-" */ | ||
305 | if (pattern_type == -1) | ||
306 | break; /* Must not contain pattern */ | ||
307 | contains_pattern = true; | ||
308 | continue; | ||
309 | case '{': /* "/\{" */ | ||
310 | if (filename - 3 < start || | ||
311 | *(filename - 3) != '/') | ||
312 | break; | ||
313 | if (pattern_type == -1) | ||
314 | break; /* Must not contain pattern */ | ||
315 | contains_pattern = true; | ||
316 | in_repetition = true; | ||
317 | continue; | ||
318 | case '}': /* "\}/" */ | ||
319 | if (*filename != '/') | ||
320 | break; | ||
321 | if (!in_repetition) | ||
322 | break; | ||
323 | in_repetition = false; | ||
324 | continue; | ||
325 | case '0': /* "\ooo" */ | ||
326 | case '1': | ||
327 | case '2': | ||
328 | case '3': | ||
329 | d = *filename++; | ||
330 | if (d < '0' || d > '7') | ||
331 | break; | ||
332 | e = *filename++; | ||
333 | if (e < '0' || e > '7') | ||
334 | break; | ||
335 | c = tomoyo_make_byte(c, d, e); | ||
336 | if (tomoyo_is_invalid(c)) | ||
337 | continue; /* pattern is not \000 */ | ||
338 | } | 239 | } |
339 | goto out; | 240 | if (min == max && min_type == max_type) |
340 | } else if (in_repetition && c == '/') { | 241 | break; |
341 | goto out; | 242 | tomoyo_addprintf(buffer, sizeof(buffer), "-"); |
342 | } else if (tomoyo_is_invalid(c)) { | 243 | min_type = max_type; |
343 | goto out; | 244 | min = max; |
344 | } | 245 | } |
246 | tomoyo_io_printf(head, "%s", buffer); | ||
345 | } | 247 | } |
346 | if (pattern_type == 1) { /* Must contain pattern */ | ||
347 | if (!contains_pattern) | ||
348 | goto out; | ||
349 | } | ||
350 | if (in_repetition) | ||
351 | goto out; | ||
352 | return true; | ||
353 | out: | ||
354 | return false; | ||
355 | } | 248 | } |
356 | 249 | ||
357 | /** | 250 | /** |
358 | * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. | 251 | * tomoyo_assign_profile - Create a new profile. |
359 | * @domainname: The domainname to check. | ||
360 | * | 252 | * |
361 | * Returns true if @domainname follows the naming rules, false otherwise. | 253 | * @profile: Profile number to create. |
254 | * | ||
255 | * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. | ||
362 | */ | 256 | */ |
363 | bool tomoyo_is_correct_domain(const unsigned char *domainname) | 257 | static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) |
364 | { | 258 | { |
365 | unsigned char c; | 259 | struct tomoyo_profile *ptr; |
366 | unsigned char d; | 260 | struct tomoyo_profile *entry; |
367 | unsigned char e; | 261 | if (profile >= TOMOYO_MAX_PROFILES) |
368 | 262 | return NULL; | |
369 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | 263 | ptr = tomoyo_profile_ptr[profile]; |
370 | TOMOYO_ROOT_NAME_LEN)) | 264 | if (ptr) |
265 | return ptr; | ||
266 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
267 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
371 | goto out; | 268 | goto out; |
372 | domainname += TOMOYO_ROOT_NAME_LEN; | 269 | ptr = tomoyo_profile_ptr[profile]; |
373 | if (!*domainname) | 270 | if (!ptr && tomoyo_memory_ok(entry)) { |
374 | return true; | 271 | ptr = entry; |
375 | do { | 272 | ptr->learning = &tomoyo_default_profile.preference; |
376 | if (*domainname++ != ' ') | 273 | ptr->permissive = &tomoyo_default_profile.preference; |
377 | goto out; | 274 | ptr->enforcing = &tomoyo_default_profile.preference; |
378 | if (*domainname++ != '/') | 275 | ptr->default_config = TOMOYO_CONFIG_DISABLED; |
379 | goto out; | 276 | memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, |
380 | while ((c = *domainname) != '\0' && c != ' ') { | 277 | sizeof(ptr->config)); |
381 | domainname++; | 278 | mb(); /* Avoid out-of-order execution. */ |
382 | if (c == '\\') { | 279 | tomoyo_profile_ptr[profile] = ptr; |
383 | c = *domainname++; | 280 | entry = NULL; |
384 | switch ((c)) { | 281 | } |
385 | case '\\': /* "\\" */ | 282 | mutex_unlock(&tomoyo_policy_lock); |
386 | continue; | ||
387 | case '0': /* "\ooo" */ | ||
388 | case '1': | ||
389 | case '2': | ||
390 | case '3': | ||
391 | d = *domainname++; | ||
392 | if (d < '0' || d > '7') | ||
393 | break; | ||
394 | e = *domainname++; | ||
395 | if (e < '0' || e > '7') | ||
396 | break; | ||
397 | c = tomoyo_make_byte(c, d, e); | ||
398 | if (tomoyo_is_invalid(c)) | ||
399 | /* pattern is not \000 */ | ||
400 | continue; | ||
401 | } | ||
402 | goto out; | ||
403 | } else if (tomoyo_is_invalid(c)) { | ||
404 | goto out; | ||
405 | } | ||
406 | } | ||
407 | } while (*domainname); | ||
408 | return true; | ||
409 | out: | 283 | out: |
410 | return false; | 284 | kfree(entry); |
285 | return ptr; | ||
411 | } | 286 | } |
412 | 287 | ||
413 | /** | 288 | /** |
414 | * tomoyo_is_domain_def - Check whether the given token can be a domainname. | 289 | * tomoyo_profile - Find a profile. |
415 | * | 290 | * |
416 | * @buffer: The token to check. | 291 | * @profile: Profile number to find. |
417 | * | 292 | * |
418 | * Returns true if @buffer possibly be a domainname, false otherwise. | 293 | * Returns pointer to "struct tomoyo_profile". |
419 | */ | 294 | */ |
420 | bool tomoyo_is_domain_def(const unsigned char *buffer) | 295 | struct tomoyo_profile *tomoyo_profile(const u8 profile) |
421 | { | 296 | { |
422 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | 297 | struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; |
298 | if (!tomoyo_policy_loaded) | ||
299 | return &tomoyo_default_profile; | ||
300 | BUG_ON(!ptr); | ||
301 | return ptr; | ||
423 | } | 302 | } |
424 | 303 | ||
425 | /** | 304 | static s8 tomoyo_find_yesno(const char *string, const char *find) |
426 | * tomoyo_find_domain - Find a domain by the given name. | ||
427 | * | ||
428 | * @domainname: The domainname to find. | ||
429 | * | ||
430 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
431 | * | ||
432 | * Caller holds tomoyo_read_lock(). | ||
433 | */ | ||
434 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
435 | { | 305 | { |
436 | struct tomoyo_domain_info *domain; | 306 | const char *cp = strstr(string, find); |
437 | struct tomoyo_path_info name; | 307 | if (cp) { |
438 | 308 | cp += strlen(find); | |
439 | name.name = domainname; | 309 | if (!strncmp(cp, "=yes", 4)) |
440 | tomoyo_fill_path_info(&name); | 310 | return 1; |
441 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 311 | else if (!strncmp(cp, "=no", 3)) |
442 | if (!domain->is_deleted && | 312 | return 0; |
443 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
444 | return domain; | ||
445 | } | 313 | } |
446 | return NULL; | 314 | return -1; |
447 | } | 315 | } |
448 | 316 | ||
449 | /** | 317 | static void tomoyo_set_bool(bool *b, const char *string, const char *find) |
450 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
451 | * | ||
452 | * @filename: The string to evaluate. | ||
453 | * | ||
454 | * Returns the initial length without a pattern in @filename. | ||
455 | */ | ||
456 | static int tomoyo_const_part_length(const char *filename) | ||
457 | { | 318 | { |
458 | char c; | 319 | switch (tomoyo_find_yesno(string, find)) { |
459 | int len = 0; | 320 | case 1: |
460 | 321 | *b = true; | |
461 | if (!filename) | 322 | break; |
462 | return 0; | 323 | case 0: |
463 | while ((c = *filename++) != '\0') { | 324 | *b = false; |
464 | if (c != '\\') { | ||
465 | len++; | ||
466 | continue; | ||
467 | } | ||
468 | c = *filename++; | ||
469 | switch (c) { | ||
470 | case '\\': /* "\\" */ | ||
471 | len += 2; | ||
472 | continue; | ||
473 | case '0': /* "\ooo" */ | ||
474 | case '1': | ||
475 | case '2': | ||
476 | case '3': | ||
477 | c = *filename++; | ||
478 | if (c < '0' || c > '7') | ||
479 | break; | ||
480 | c = *filename++; | ||
481 | if (c < '0' || c > '7') | ||
482 | break; | ||
483 | len += 4; | ||
484 | continue; | ||
485 | } | ||
486 | break; | 325 | break; |
487 | } | 326 | } |
488 | return len; | ||
489 | } | 327 | } |
490 | 328 | ||
491 | /** | 329 | static void tomoyo_set_uint(unsigned int *i, const char *string, |
492 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | 330 | const char *find) |
493 | * | ||
494 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
495 | * | ||
496 | * The caller sets "struct tomoyo_path_info"->name. | ||
497 | */ | ||
498 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
499 | { | 331 | { |
500 | const char *name = ptr->name; | 332 | const char *cp = strstr(string, find); |
501 | const int len = strlen(name); | 333 | if (cp) |
502 | 334 | sscanf(cp + strlen(find), "=%u", i); | |
503 | ptr->const_len = tomoyo_const_part_length(name); | ||
504 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
505 | ptr->is_patterned = (ptr->const_len < len); | ||
506 | ptr->hash = full_name_hash(name, len); | ||
507 | } | 335 | } |
508 | 336 | ||
509 | /** | 337 | static void tomoyo_set_pref(const char *name, const char *value, |
510 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character | 338 | const bool use_default, |
511 | * and "\-" pattern. | 339 | struct tomoyo_profile *profile) |
512 | * | ||
513 | * @filename: The start of string to check. | ||
514 | * @filename_end: The end of string to check. | ||
515 | * @pattern: The start of pattern to compare. | ||
516 | * @pattern_end: The end of pattern to compare. | ||
517 | * | ||
518 | * Returns true if @filename matches @pattern, false otherwise. | ||
519 | */ | ||
520 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
521 | const char *filename_end, | ||
522 | const char *pattern, | ||
523 | const char *pattern_end) | ||
524 | { | 340 | { |
525 | while (filename < filename_end && pattern < pattern_end) { | 341 | struct tomoyo_preference **pref; |
526 | char c; | 342 | bool *verbose; |
527 | if (*pattern != '\\') { | 343 | if (!strcmp(name, "enforcing")) { |
528 | if (*filename++ != *pattern++) | 344 | if (use_default) { |
529 | return false; | 345 | pref = &profile->enforcing; |
530 | continue; | 346 | goto set_default; |
531 | } | 347 | } |
532 | c = *filename; | 348 | profile->enforcing = &profile->preference; |
533 | pattern++; | 349 | verbose = &profile->preference.enforcing_verbose; |
534 | switch (*pattern) { | 350 | goto set_verbose; |
535 | int i; | ||
536 | int j; | ||
537 | case '?': | ||
538 | if (c == '/') { | ||
539 | return false; | ||
540 | } else if (c == '\\') { | ||
541 | if (filename[1] == '\\') | ||
542 | filename++; | ||
543 | else if (tomoyo_is_byte_range(filename + 1)) | ||
544 | filename += 3; | ||
545 | else | ||
546 | return false; | ||
547 | } | ||
548 | break; | ||
549 | case '\\': | ||
550 | if (c != '\\') | ||
551 | return false; | ||
552 | if (*++filename != '\\') | ||
553 | return false; | ||
554 | break; | ||
555 | case '+': | ||
556 | if (!isdigit(c)) | ||
557 | return false; | ||
558 | break; | ||
559 | case 'x': | ||
560 | if (!isxdigit(c)) | ||
561 | return false; | ||
562 | break; | ||
563 | case 'a': | ||
564 | if (!tomoyo_is_alphabet_char(c)) | ||
565 | return false; | ||
566 | break; | ||
567 | case '0': | ||
568 | case '1': | ||
569 | case '2': | ||
570 | case '3': | ||
571 | if (c == '\\' && tomoyo_is_byte_range(filename + 1) | ||
572 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
573 | filename += 3; | ||
574 | pattern += 2; | ||
575 | break; | ||
576 | } | ||
577 | return false; /* Not matched. */ | ||
578 | case '*': | ||
579 | case '@': | ||
580 | for (i = 0; i <= filename_end - filename; i++) { | ||
581 | if (tomoyo_file_matches_pattern2( | ||
582 | filename + i, filename_end, | ||
583 | pattern + 1, pattern_end)) | ||
584 | return true; | ||
585 | c = filename[i]; | ||
586 | if (c == '.' && *pattern == '@') | ||
587 | break; | ||
588 | if (c != '\\') | ||
589 | continue; | ||
590 | if (filename[i + 1] == '\\') | ||
591 | i++; | ||
592 | else if (tomoyo_is_byte_range(filename + i + 1)) | ||
593 | i += 3; | ||
594 | else | ||
595 | break; /* Bad pattern. */ | ||
596 | } | ||
597 | return false; /* Not matched. */ | ||
598 | default: | ||
599 | j = 0; | ||
600 | c = *pattern; | ||
601 | if (c == '$') { | ||
602 | while (isdigit(filename[j])) | ||
603 | j++; | ||
604 | } else if (c == 'X') { | ||
605 | while (isxdigit(filename[j])) | ||
606 | j++; | ||
607 | } else if (c == 'A') { | ||
608 | while (tomoyo_is_alphabet_char(filename[j])) | ||
609 | j++; | ||
610 | } | ||
611 | for (i = 1; i <= j; i++) { | ||
612 | if (tomoyo_file_matches_pattern2( | ||
613 | filename + i, filename_end, | ||
614 | pattern + 1, pattern_end)) | ||
615 | return true; | ||
616 | } | ||
617 | return false; /* Not matched or bad pattern. */ | ||
618 | } | ||
619 | filename++; | ||
620 | pattern++; | ||
621 | } | ||
622 | while (*pattern == '\\' && | ||
623 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
624 | pattern += 2; | ||
625 | return filename == filename_end && pattern == pattern_end; | ||
626 | } | ||
627 | |||
628 | /** | ||
629 | * tomoyo_file_matches_pattern - Pattern matching without without '/' character. | ||
630 | * | ||
631 | * @filename: The start of string to check. | ||
632 | * @filename_end: The end of string to check. | ||
633 | * @pattern: The start of pattern to compare. | ||
634 | * @pattern_end: The end of pattern to compare. | ||
635 | * | ||
636 | * Returns true if @filename matches @pattern, false otherwise. | ||
637 | */ | ||
638 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
639 | const char *filename_end, | ||
640 | const char *pattern, | ||
641 | const char *pattern_end) | ||
642 | { | ||
643 | const char *pattern_start = pattern; | ||
644 | bool first = true; | ||
645 | bool result; | ||
646 | |||
647 | while (pattern < pattern_end - 1) { | ||
648 | /* Split at "\-" pattern. */ | ||
649 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
650 | continue; | ||
651 | result = tomoyo_file_matches_pattern2(filename, | ||
652 | filename_end, | ||
653 | pattern_start, | ||
654 | pattern - 2); | ||
655 | if (first) | ||
656 | result = !result; | ||
657 | if (result) | ||
658 | return false; | ||
659 | first = false; | ||
660 | pattern_start = pattern; | ||
661 | } | 351 | } |
662 | result = tomoyo_file_matches_pattern2(filename, filename_end, | 352 | if (!strcmp(name, "permissive")) { |
663 | pattern_start, pattern_end); | 353 | if (use_default) { |
664 | return first ? result : !result; | 354 | pref = &profile->permissive; |
665 | } | 355 | goto set_default; |
666 | 356 | } | |
667 | /** | 357 | profile->permissive = &profile->preference; |
668 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | 358 | verbose = &profile->preference.permissive_verbose; |
669 | * | 359 | goto set_verbose; |
670 | * @f: The start of string to check. | ||
671 | * @p: The start of pattern to compare. | ||
672 | * | ||
673 | * Returns true if @f matches @p, false otherwise. | ||
674 | */ | ||
675 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
676 | { | ||
677 | const char *f_delimiter; | ||
678 | const char *p_delimiter; | ||
679 | |||
680 | while (*f && *p) { | ||
681 | f_delimiter = strchr(f, '/'); | ||
682 | if (!f_delimiter) | ||
683 | f_delimiter = f + strlen(f); | ||
684 | p_delimiter = strchr(p, '/'); | ||
685 | if (!p_delimiter) | ||
686 | p_delimiter = p + strlen(p); | ||
687 | if (*p == '\\' && *(p + 1) == '{') | ||
688 | goto recursive; | ||
689 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
690 | p_delimiter)) | ||
691 | return false; | ||
692 | f = f_delimiter; | ||
693 | if (*f) | ||
694 | f++; | ||
695 | p = p_delimiter; | ||
696 | if (*p) | ||
697 | p++; | ||
698 | } | 360 | } |
699 | /* Ignore trailing "\*" and "\@" in @pattern. */ | 361 | if (!strcmp(name, "learning")) { |
700 | while (*p == '\\' && | 362 | if (use_default) { |
701 | (*(p + 1) == '*' || *(p + 1) == '@')) | 363 | pref = &profile->learning; |
702 | p += 2; | 364 | goto set_default; |
703 | return !*f && !*p; | ||
704 | recursive: | ||
705 | /* | ||
706 | * The "\{" pattern is permitted only after '/' character. | ||
707 | * This guarantees that below "*(p - 1)" is safe. | ||
708 | * Also, the "\}" pattern is permitted only before '/' character | ||
709 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
710 | */ | ||
711 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
712 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
713 | return false; /* Bad pattern. */ | ||
714 | do { | ||
715 | /* Compare current component with pattern. */ | ||
716 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
717 | p_delimiter - 2)) | ||
718 | break; | ||
719 | /* Proceed to next component. */ | ||
720 | f = f_delimiter; | ||
721 | if (!*f) | ||
722 | break; | ||
723 | f++; | ||
724 | /* Continue comparison. */ | ||
725 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
726 | return true; | ||
727 | f_delimiter = strchr(f, '/'); | ||
728 | } while (f_delimiter); | ||
729 | return false; /* Not matched. */ | ||
730 | } | ||
731 | |||
732 | /** | ||
733 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
734 | * | ||
735 | * @filename: The filename to check. | ||
736 | * @pattern: The pattern to compare. | ||
737 | * | ||
738 | * Returns true if matches, false otherwise. | ||
739 | * | ||
740 | * The following patterns are available. | ||
741 | * \\ \ itself. | ||
742 | * \ooo Octal representation of a byte. | ||
743 | * \* Zero or more repetitions of characters other than '/'. | ||
744 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
745 | * \? 1 byte character other than '/'. | ||
746 | * \$ One or more repetitions of decimal digits. | ||
747 | * \+ 1 decimal digit. | ||
748 | * \X One or more repetitions of hexadecimal digits. | ||
749 | * \x 1 hexadecimal digit. | ||
750 | * \A One or more repetitions of alphabet characters. | ||
751 | * \a 1 alphabet character. | ||
752 | * | ||
753 | * \- Subtraction operator. | ||
754 | * | ||
755 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
756 | * /dir/dir/dir/ ). | ||
757 | */ | ||
758 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
759 | const struct tomoyo_path_info *pattern) | ||
760 | { | ||
761 | const char *f = filename->name; | ||
762 | const char *p = pattern->name; | ||
763 | const int len = pattern->const_len; | ||
764 | |||
765 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
766 | if (!pattern->is_patterned) | ||
767 | return !tomoyo_pathcmp(filename, pattern); | ||
768 | /* Don't compare directory and non-directory. */ | ||
769 | if (filename->is_dir != pattern->is_dir) | ||
770 | return false; | ||
771 | /* Compare the initial length without patterns. */ | ||
772 | if (strncmp(f, p, len)) | ||
773 | return false; | ||
774 | f += len; | ||
775 | p += len; | ||
776 | return tomoyo_path_matches_pattern2(f, p); | ||
777 | } | ||
778 | |||
779 | /** | ||
780 | * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure. | ||
781 | * | ||
782 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
783 | * @fmt: The printf()'s format string, followed by parameters. | ||
784 | * | ||
785 | * Returns true if output was written, false otherwise. | ||
786 | * | ||
787 | * The snprintf() will truncate, but tomoyo_io_printf() won't. | ||
788 | */ | ||
789 | bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | ||
790 | { | ||
791 | va_list args; | ||
792 | int len; | ||
793 | int pos = head->read_avail; | ||
794 | int size = head->readbuf_size - pos; | ||
795 | |||
796 | if (size <= 0) | ||
797 | return false; | ||
798 | va_start(args, fmt); | ||
799 | len = vsnprintf(head->read_buf + pos, size, fmt, args); | ||
800 | va_end(args); | ||
801 | if (pos + len >= head->readbuf_size) | ||
802 | return false; | ||
803 | head->read_avail += len; | ||
804 | return true; | ||
805 | } | ||
806 | |||
807 | /** | ||
808 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
809 | * | ||
810 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
811 | * | ||
812 | * This function uses kzalloc(), so the caller must call kfree() | ||
813 | * if this function didn't return NULL. | ||
814 | */ | ||
815 | static const char *tomoyo_get_exe(void) | ||
816 | { | ||
817 | struct mm_struct *mm = current->mm; | ||
818 | struct vm_area_struct *vma; | ||
819 | const char *cp = NULL; | ||
820 | |||
821 | if (!mm) | ||
822 | return NULL; | ||
823 | down_read(&mm->mmap_sem); | ||
824 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
825 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
826 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
827 | break; | ||
828 | } | 365 | } |
366 | profile->learning = &profile->preference; | ||
367 | tomoyo_set_uint(&profile->preference.learning_max_entry, value, | ||
368 | "max_entry"); | ||
369 | verbose = &profile->preference.learning_verbose; | ||
370 | goto set_verbose; | ||
829 | } | 371 | } |
830 | up_read(&mm->mmap_sem); | 372 | return; |
831 | return cp; | 373 | set_default: |
374 | *pref = &tomoyo_default_profile.preference; | ||
375 | return; | ||
376 | set_verbose: | ||
377 | tomoyo_set_bool(verbose, value, "verbose"); | ||
832 | } | 378 | } |
833 | 379 | ||
834 | /** | 380 | static int tomoyo_set_mode(char *name, const char *value, |
835 | * tomoyo_get_msg - Get warning message. | 381 | const bool use_default, |
836 | * | 382 | struct tomoyo_profile *profile) |
837 | * @is_enforce: Is it enforcing mode? | ||
838 | * | ||
839 | * Returns "ERROR" or "WARNING". | ||
840 | */ | ||
841 | const char *tomoyo_get_msg(const bool is_enforce) | ||
842 | { | 383 | { |
843 | if (is_enforce) | 384 | u8 i; |
844 | return "ERROR"; | 385 | u8 config; |
845 | else | 386 | if (!strcmp(name, "CONFIG")) { |
846 | return "WARNING"; | 387 | i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; |
847 | } | 388 | config = profile->default_config; |
848 | 389 | } else if (tomoyo_str_starts(&name, "CONFIG::")) { | |
849 | /** | 390 | config = 0; |
850 | * tomoyo_check_flags - Check mode for specified functionality. | 391 | for (i = 0; i < TOMOYO_MAX_MAC_INDEX |
851 | * | 392 | + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { |
852 | * @domain: Pointer to "struct tomoyo_domain_info". | 393 | if (strcmp(name, tomoyo_mac_keywords[i])) |
853 | * @index: The functionality to check mode. | 394 | continue; |
854 | * | 395 | config = profile->config[i]; |
855 | * TOMOYO checks only process context. | ||
856 | * This code disables TOMOYO's enforcement in case the function is called from | ||
857 | * interrupt context. | ||
858 | */ | ||
859 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, | ||
860 | const u8 index) | ||
861 | { | ||
862 | const u8 profile = domain->profile; | ||
863 | |||
864 | if (WARN_ON(in_interrupt())) | ||
865 | return 0; | ||
866 | return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX | ||
867 | #if TOMOYO_MAX_PROFILES != 256 | ||
868 | && profile < TOMOYO_MAX_PROFILES | ||
869 | #endif | ||
870 | && tomoyo_profile_ptr[profile] ? | ||
871 | tomoyo_profile_ptr[profile]->value[index] : 0; | ||
872 | } | ||
873 | |||
874 | /** | ||
875 | * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. | ||
876 | * | ||
877 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
878 | * | ||
879 | * Returns true if domain policy violation warning should be printed to | ||
880 | * console. | ||
881 | */ | ||
882 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) | ||
883 | { | ||
884 | return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; | ||
885 | } | ||
886 | |||
887 | /** | ||
888 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
889 | * | ||
890 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
891 | * | ||
892 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
893 | * | ||
894 | * Caller holds tomoyo_read_lock(). | ||
895 | */ | ||
896 | bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) | ||
897 | { | ||
898 | unsigned int count = 0; | ||
899 | struct tomoyo_acl_info *ptr; | ||
900 | |||
901 | if (!domain) | ||
902 | return true; | ||
903 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
904 | switch (ptr->type) { | ||
905 | struct tomoyo_path_acl *acl; | ||
906 | u32 perm; | ||
907 | u8 i; | ||
908 | case TOMOYO_TYPE_PATH_ACL: | ||
909 | acl = container_of(ptr, struct tomoyo_path_acl, head); | ||
910 | perm = acl->perm | (((u32) acl->perm_high) << 16); | ||
911 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
912 | if (perm & (1 << i)) | ||
913 | count++; | ||
914 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
915 | count -= 2; | ||
916 | break; | ||
917 | case TOMOYO_TYPE_PATH2_ACL: | ||
918 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
919 | ->perm; | ||
920 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
921 | if (perm & (1 << i)) | ||
922 | count++; | ||
923 | break; | 396 | break; |
924 | } | 397 | } |
398 | if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) | ||
399 | return -EINVAL; | ||
400 | } else { | ||
401 | return -EINVAL; | ||
925 | } | 402 | } |
926 | if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) | 403 | if (use_default) { |
927 | return true; | 404 | config = TOMOYO_CONFIG_USE_DEFAULT; |
928 | if (!domain->quota_warned) { | 405 | } else { |
929 | domain->quota_warned = true; | 406 | u8 mode; |
930 | printk(KERN_WARNING "TOMOYO-WARNING: " | 407 | for (mode = 0; mode < 4; mode++) |
931 | "Domain '%s' has so many ACLs to hold. " | 408 | if (strstr(value, tomoyo_mode[mode])) |
932 | "Stopped learning mode.\n", domain->domainname->name); | 409 | /* |
933 | } | 410 | * Update lower 3 bits in order to distinguish |
934 | return false; | 411 | * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. |
935 | } | 412 | */ |
936 | 413 | config = (config & ~7) | mode; | |
937 | /** | ||
938 | * tomoyo_find_or_assign_new_profile - Create a new profile. | ||
939 | * | ||
940 | * @profile: Profile number to create. | ||
941 | * | ||
942 | * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. | ||
943 | */ | ||
944 | static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned | ||
945 | int profile) | ||
946 | { | ||
947 | struct tomoyo_profile *ptr = NULL; | ||
948 | int i; | ||
949 | |||
950 | if (profile >= TOMOYO_MAX_PROFILES) | ||
951 | return NULL; | ||
952 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
953 | return NULL; | ||
954 | ptr = tomoyo_profile_ptr[profile]; | ||
955 | if (ptr) | ||
956 | goto ok; | ||
957 | ptr = kmalloc(sizeof(*ptr), GFP_NOFS); | ||
958 | if (!tomoyo_memory_ok(ptr)) { | ||
959 | kfree(ptr); | ||
960 | ptr = NULL; | ||
961 | goto ok; | ||
962 | } | 414 | } |
963 | for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) | 415 | if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) |
964 | ptr->value[i] = tomoyo_control_array[i].current_value; | 416 | profile->config[i] = config; |
965 | mb(); /* Avoid out-of-order execution. */ | 417 | else if (config != TOMOYO_CONFIG_USE_DEFAULT) |
966 | tomoyo_profile_ptr[profile] = ptr; | 418 | profile->default_config = config; |
967 | ok: | 419 | return 0; |
968 | mutex_unlock(&tomoyo_policy_lock); | ||
969 | return ptr; | ||
970 | } | 420 | } |
971 | 421 | ||
972 | /** | 422 | /** |
973 | * tomoyo_write_profile - Write to profile table. | 423 | * tomoyo_write_profile - Write profile table. |
974 | * | 424 | * |
975 | * @head: Pointer to "struct tomoyo_io_buffer". | 425 | * @head: Pointer to "struct tomoyo_io_buffer". |
976 | * | 426 | * |
@@ -980,153 +430,165 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) | |||
980 | { | 430 | { |
981 | char *data = head->write_buf; | 431 | char *data = head->write_buf; |
982 | unsigned int i; | 432 | unsigned int i; |
983 | unsigned int value; | 433 | bool use_default = false; |
984 | char *cp; | 434 | char *cp; |
985 | struct tomoyo_profile *profile; | 435 | struct tomoyo_profile *profile; |
986 | unsigned long num; | 436 | if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) |
987 | 437 | return 0; | |
988 | cp = strchr(data, '-'); | 438 | i = simple_strtoul(data, &cp, 10); |
989 | if (cp) | 439 | if (data == cp) { |
990 | *cp = '\0'; | 440 | profile = &tomoyo_default_profile; |
991 | if (strict_strtoul(data, 10, &num)) | 441 | } else { |
992 | return -EINVAL; | 442 | if (*cp != '-') |
993 | if (cp) | 443 | return -EINVAL; |
994 | data = cp + 1; | 444 | data = cp + 1; |
995 | profile = tomoyo_find_or_assign_new_profile(num); | 445 | profile = tomoyo_assign_profile(i); |
996 | if (!profile) | 446 | if (!profile) |
997 | return -EINVAL; | 447 | return -EINVAL; |
448 | } | ||
998 | cp = strchr(data, '='); | 449 | cp = strchr(data, '='); |
999 | if (!cp) | 450 | if (!cp) |
1000 | return -EINVAL; | 451 | return -EINVAL; |
1001 | *cp = '\0'; | 452 | *cp++ = '\0'; |
453 | if (profile != &tomoyo_default_profile) | ||
454 | use_default = strstr(cp, "use_default") != NULL; | ||
455 | if (tomoyo_str_starts(&data, "PREFERENCE::")) { | ||
456 | tomoyo_set_pref(data, cp, use_default, profile); | ||
457 | return 0; | ||
458 | } | ||
459 | if (profile == &tomoyo_default_profile) | ||
460 | return -EINVAL; | ||
1002 | if (!strcmp(data, "COMMENT")) { | 461 | if (!strcmp(data, "COMMENT")) { |
1003 | const struct tomoyo_path_info *old_comment = profile->comment; | 462 | const struct tomoyo_path_info *old_comment = profile->comment; |
1004 | profile->comment = tomoyo_get_name(cp + 1); | 463 | profile->comment = tomoyo_get_name(cp); |
1005 | tomoyo_put_name(old_comment); | 464 | tomoyo_put_name(old_comment); |
1006 | return 0; | 465 | return 0; |
1007 | } | 466 | } |
1008 | for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) { | 467 | return tomoyo_set_mode(data, cp, use_default, profile); |
1009 | if (strcmp(data, tomoyo_control_array[i].keyword)) | 468 | } |
1010 | continue; | 469 | |
1011 | if (sscanf(cp + 1, "%u", &value) != 1) { | 470 | static void tomoyo_print_preference(struct tomoyo_io_buffer *head, |
1012 | int j; | 471 | const int idx) |
1013 | const char **modes; | 472 | { |
1014 | switch (i) { | 473 | struct tomoyo_preference *pref = &tomoyo_default_profile.preference; |
1015 | case TOMOYO_VERBOSE: | 474 | const struct tomoyo_profile *profile = idx >= 0 ? |
1016 | modes = tomoyo_mode_2; | 475 | tomoyo_profile_ptr[idx] : NULL; |
1017 | break; | 476 | char buffer[16] = ""; |
1018 | default: | 477 | if (profile) { |
1019 | modes = tomoyo_mode_4; | 478 | buffer[sizeof(buffer) - 1] = '\0'; |
1020 | break; | 479 | snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); |
1021 | } | ||
1022 | for (j = 0; j < 4; j++) { | ||
1023 | if (strcmp(cp + 1, modes[j])) | ||
1024 | continue; | ||
1025 | value = j; | ||
1026 | break; | ||
1027 | } | ||
1028 | if (j == 4) | ||
1029 | return -EINVAL; | ||
1030 | } else if (value > tomoyo_control_array[i].max_value) { | ||
1031 | value = tomoyo_control_array[i].max_value; | ||
1032 | } | ||
1033 | profile->value[i] = value; | ||
1034 | return 0; | ||
1035 | } | 480 | } |
1036 | return -EINVAL; | 481 | if (profile) { |
482 | pref = profile->learning; | ||
483 | if (pref == &tomoyo_default_profile.preference) | ||
484 | goto skip1; | ||
485 | } | ||
486 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ " | ||
487 | "verbose=%s max_entry=%u }\n", | ||
488 | buffer, "learning", | ||
489 | tomoyo_yesno(pref->learning_verbose), | ||
490 | pref->learning_max_entry); | ||
491 | skip1: | ||
492 | if (profile) { | ||
493 | pref = profile->permissive; | ||
494 | if (pref == &tomoyo_default_profile.preference) | ||
495 | goto skip2; | ||
496 | } | ||
497 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", | ||
498 | buffer, "permissive", | ||
499 | tomoyo_yesno(pref->permissive_verbose)); | ||
500 | skip2: | ||
501 | if (profile) { | ||
502 | pref = profile->enforcing; | ||
503 | if (pref == &tomoyo_default_profile.preference) | ||
504 | return; | ||
505 | } | ||
506 | tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", | ||
507 | buffer, "enforcing", | ||
508 | tomoyo_yesno(pref->enforcing_verbose)); | ||
509 | } | ||
510 | |||
511 | static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) | ||
512 | { | ||
513 | tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); | ||
1037 | } | 514 | } |
1038 | 515 | ||
1039 | /** | 516 | /** |
1040 | * tomoyo_read_profile - Read from profile table. | 517 | * tomoyo_read_profile - Read profile table. |
1041 | * | 518 | * |
1042 | * @head: Pointer to "struct tomoyo_io_buffer". | 519 | * @head: Pointer to "struct tomoyo_io_buffer". |
1043 | * | ||
1044 | * Returns 0. | ||
1045 | */ | 520 | */ |
1046 | static int tomoyo_read_profile(struct tomoyo_io_buffer *head) | 521 | static void tomoyo_read_profile(struct tomoyo_io_buffer *head) |
1047 | { | 522 | { |
1048 | static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; | 523 | u8 index; |
1049 | int step; | 524 | const struct tomoyo_profile *profile; |
1050 | 525 | next: | |
1051 | if (head->read_eof) | 526 | index = head->r.index; |
1052 | return 0; | 527 | profile = tomoyo_profile_ptr[index]; |
1053 | for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total; | 528 | switch (head->r.step) { |
1054 | step++) { | 529 | case 0: |
1055 | const u8 index = step / total; | 530 | tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); |
1056 | u8 type = step % total; | 531 | tomoyo_print_preference(head, -1); |
1057 | const struct tomoyo_profile *profile | 532 | head->r.step++; |
1058 | = tomoyo_profile_ptr[index]; | 533 | break; |
1059 | head->read_step = step; | 534 | case 1: |
1060 | if (!profile) | 535 | for ( ; head->r.index < TOMOYO_MAX_PROFILES; |
1061 | continue; | 536 | head->r.index++) |
1062 | if (!type) { /* Print profile' comment tag. */ | 537 | if (tomoyo_profile_ptr[head->r.index]) |
1063 | if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n", | ||
1064 | index, profile->comment ? | ||
1065 | profile->comment->name : "")) | ||
1066 | break; | 538 | break; |
1067 | continue; | 539 | if (head->r.index == TOMOYO_MAX_PROFILES) |
540 | return; | ||
541 | head->r.step++; | ||
542 | break; | ||
543 | case 2: | ||
544 | { | ||
545 | const struct tomoyo_path_info *comment = | ||
546 | profile->comment; | ||
547 | tomoyo_io_printf(head, "%u-COMMENT=", index); | ||
548 | tomoyo_set_string(head, comment ? comment->name : ""); | ||
549 | tomoyo_set_lf(head); | ||
550 | head->r.step++; | ||
1068 | } | 551 | } |
1069 | type--; | 552 | break; |
1070 | if (type < TOMOYO_MAX_CONTROL_INDEX) { | 553 | case 3: |
1071 | const unsigned int value = profile->value[type]; | 554 | { |
1072 | const char **modes = NULL; | 555 | tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); |
1073 | const char *keyword | 556 | tomoyo_print_config(head, profile->default_config); |
1074 | = tomoyo_control_array[type].keyword; | 557 | head->r.bit = 0; |
1075 | switch (tomoyo_control_array[type].max_value) { | 558 | head->r.step++; |
1076 | case 3: | 559 | } |
1077 | modes = tomoyo_mode_4; | 560 | break; |
1078 | break; | 561 | case 4: |
1079 | case 1: | 562 | for ( ; head->r.bit < TOMOYO_MAX_MAC_INDEX |
1080 | modes = tomoyo_mode_2; | 563 | + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { |
1081 | break; | 564 | const u8 i = head->r.bit; |
1082 | } | 565 | const u8 config = profile->config[i]; |
1083 | if (modes) { | 566 | if (config == TOMOYO_CONFIG_USE_DEFAULT) |
1084 | if (!tomoyo_io_printf(head, "%u-%s=%s\n", index, | 567 | continue; |
1085 | keyword, modes[value])) | 568 | tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", |
1086 | break; | 569 | tomoyo_mac_keywords[i]); |
1087 | } else { | 570 | tomoyo_print_config(head, config); |
1088 | if (!tomoyo_io_printf(head, "%u-%s=%u\n", index, | 571 | head->r.bit++; |
1089 | keyword, value)) | 572 | break; |
1090 | break; | 573 | } |
1091 | } | 574 | if (head->r.bit == TOMOYO_MAX_MAC_INDEX |
575 | + TOMOYO_MAX_MAC_CATEGORY_INDEX) { | ||
576 | tomoyo_print_preference(head, index); | ||
577 | head->r.index++; | ||
578 | head->r.step = 1; | ||
1092 | } | 579 | } |
580 | break; | ||
1093 | } | 581 | } |
1094 | if (step == TOMOYO_MAX_PROFILES * total) | 582 | if (tomoyo_flush(head)) |
1095 | head->read_eof = true; | 583 | goto next; |
1096 | return 0; | ||
1097 | } | 584 | } |
1098 | 585 | ||
1099 | /* | 586 | static bool tomoyo_same_manager(const struct tomoyo_acl_head *a, |
1100 | * tomoyo_policy_manager_list is used for holding list of domainnames or | 587 | const struct tomoyo_acl_head *b) |
1101 | * programs which are permitted to modify configuration via | 588 | { |
1102 | * /sys/kernel/security/tomoyo/ interface. | 589 | return container_of(a, struct tomoyo_manager, head)->manager == |
1103 | * | 590 | container_of(b, struct tomoyo_manager, head)->manager; |
1104 | * An entry is added by | 591 | } |
1105 | * | ||
1106 | * # echo '<kernel> /sbin/mingetty /bin/login /bin/bash' > \ | ||
1107 | * /sys/kernel/security/tomoyo/manager | ||
1108 | * (if you want to specify by a domainname) | ||
1109 | * | ||
1110 | * or | ||
1111 | * | ||
1112 | * # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager | ||
1113 | * (if you want to specify by a program's location) | ||
1114 | * | ||
1115 | * and is deleted by | ||
1116 | * | ||
1117 | * # echo 'delete <kernel> /sbin/mingetty /bin/login /bin/bash' > \ | ||
1118 | * /sys/kernel/security/tomoyo/manager | ||
1119 | * | ||
1120 | * or | ||
1121 | * | ||
1122 | * # echo 'delete /usr/lib/ccs/editpolicy' > \ | ||
1123 | * /sys/kernel/security/tomoyo/manager | ||
1124 | * | ||
1125 | * and all entries are retrieved by | ||
1126 | * | ||
1127 | * # cat /sys/kernel/security/tomoyo/manager | ||
1128 | */ | ||
1129 | LIST_HEAD(tomoyo_policy_manager_list); | ||
1130 | 592 | ||
1131 | /** | 593 | /** |
1132 | * tomoyo_update_manager_entry - Add a manager entry. | 594 | * tomoyo_update_manager_entry - Add a manager entry. |
@@ -1141,47 +603,29 @@ LIST_HEAD(tomoyo_policy_manager_list); | |||
1141 | static int tomoyo_update_manager_entry(const char *manager, | 603 | static int tomoyo_update_manager_entry(const char *manager, |
1142 | const bool is_delete) | 604 | const bool is_delete) |
1143 | { | 605 | { |
1144 | struct tomoyo_policy_manager_entry *ptr; | 606 | struct tomoyo_manager e = { }; |
1145 | struct tomoyo_policy_manager_entry e = { }; | 607 | int error; |
1146 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
1147 | 608 | ||
1148 | if (tomoyo_is_domain_def(manager)) { | 609 | if (tomoyo_domain_def(manager)) { |
1149 | if (!tomoyo_is_correct_domain(manager)) | 610 | if (!tomoyo_correct_domain(manager)) |
1150 | return -EINVAL; | 611 | return -EINVAL; |
1151 | e.is_domain = true; | 612 | e.is_domain = true; |
1152 | } else { | 613 | } else { |
1153 | if (!tomoyo_is_correct_path(manager, 1, -1, -1)) | 614 | if (!tomoyo_correct_path(manager)) |
1154 | return -EINVAL; | 615 | return -EINVAL; |
1155 | } | 616 | } |
1156 | e.manager = tomoyo_get_name(manager); | 617 | e.manager = tomoyo_get_name(manager); |
1157 | if (!e.manager) | 618 | if (!e.manager) |
1158 | return -ENOMEM; | 619 | return -ENOMEM; |
1159 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 620 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
1160 | goto out; | 621 | &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
1161 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 622 | tomoyo_same_manager); |
1162 | if (ptr->manager != e.manager) | ||
1163 | continue; | ||
1164 | ptr->is_deleted = is_delete; | ||
1165 | error = 0; | ||
1166 | break; | ||
1167 | } | ||
1168 | if (!is_delete && error) { | ||
1169 | struct tomoyo_policy_manager_entry *entry = | ||
1170 | tomoyo_commit_ok(&e, sizeof(e)); | ||
1171 | if (entry) { | ||
1172 | list_add_tail_rcu(&entry->list, | ||
1173 | &tomoyo_policy_manager_list); | ||
1174 | error = 0; | ||
1175 | } | ||
1176 | } | ||
1177 | mutex_unlock(&tomoyo_policy_lock); | ||
1178 | out: | ||
1179 | tomoyo_put_name(e.manager); | 623 | tomoyo_put_name(e.manager); |
1180 | return error; | 624 | return error; |
1181 | } | 625 | } |
1182 | 626 | ||
1183 | /** | 627 | /** |
1184 | * tomoyo_write_manager_policy - Write manager policy. | 628 | * tomoyo_write_manager - Write manager policy. |
1185 | * | 629 | * |
1186 | * @head: Pointer to "struct tomoyo_io_buffer". | 630 | * @head: Pointer to "struct tomoyo_io_buffer". |
1187 | * | 631 | * |
@@ -1189,7 +633,7 @@ static int tomoyo_update_manager_entry(const char *manager, | |||
1189 | * | 633 | * |
1190 | * Caller holds tomoyo_read_lock(). | 634 | * Caller holds tomoyo_read_lock(). |
1191 | */ | 635 | */ |
1192 | static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) | 636 | static int tomoyo_write_manager(struct tomoyo_io_buffer *head) |
1193 | { | 637 | { |
1194 | char *data = head->write_buf; | 638 | char *data = head->write_buf; |
1195 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); | 639 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); |
@@ -1202,47 +646,41 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) | |||
1202 | } | 646 | } |
1203 | 647 | ||
1204 | /** | 648 | /** |
1205 | * tomoyo_read_manager_policy - Read manager policy. | 649 | * tomoyo_read_manager - Read manager policy. |
1206 | * | 650 | * |
1207 | * @head: Pointer to "struct tomoyo_io_buffer". | 651 | * @head: Pointer to "struct tomoyo_io_buffer". |
1208 | * | 652 | * |
1209 | * Returns 0. | ||
1210 | * | ||
1211 | * Caller holds tomoyo_read_lock(). | 653 | * Caller holds tomoyo_read_lock(). |
1212 | */ | 654 | */ |
1213 | static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) | 655 | static void tomoyo_read_manager(struct tomoyo_io_buffer *head) |
1214 | { | 656 | { |
1215 | struct list_head *pos; | 657 | if (head->r.eof) |
1216 | bool done = true; | 658 | return; |
1217 | 659 | list_for_each_cookie(head->r.acl, | |
1218 | if (head->read_eof) | 660 | &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { |
1219 | return 0; | 661 | struct tomoyo_manager *ptr = |
1220 | list_for_each_cookie(pos, head->read_var2, | 662 | list_entry(head->r.acl, typeof(*ptr), head.list); |
1221 | &tomoyo_policy_manager_list) { | 663 | if (ptr->head.is_deleted) |
1222 | struct tomoyo_policy_manager_entry *ptr; | ||
1223 | ptr = list_entry(pos, struct tomoyo_policy_manager_entry, | ||
1224 | list); | ||
1225 | if (ptr->is_deleted) | ||
1226 | continue; | 664 | continue; |
1227 | done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); | 665 | if (!tomoyo_flush(head)) |
1228 | if (!done) | 666 | return; |
1229 | break; | 667 | tomoyo_set_string(head, ptr->manager->name); |
668 | tomoyo_set_lf(head); | ||
1230 | } | 669 | } |
1231 | head->read_eof = done; | 670 | head->r.eof = true; |
1232 | return 0; | ||
1233 | } | 671 | } |
1234 | 672 | ||
1235 | /** | 673 | /** |
1236 | * tomoyo_is_policy_manager - Check whether the current process is a policy manager. | 674 | * tomoyo_manager - Check whether the current process is a policy manager. |
1237 | * | 675 | * |
1238 | * Returns true if the current process is permitted to modify policy | 676 | * Returns true if the current process is permitted to modify policy |
1239 | * via /sys/kernel/security/tomoyo/ interface. | 677 | * via /sys/kernel/security/tomoyo/ interface. |
1240 | * | 678 | * |
1241 | * Caller holds tomoyo_read_lock(). | 679 | * Caller holds tomoyo_read_lock(). |
1242 | */ | 680 | */ |
1243 | static bool tomoyo_is_policy_manager(void) | 681 | static bool tomoyo_manager(void) |
1244 | { | 682 | { |
1245 | struct tomoyo_policy_manager_entry *ptr; | 683 | struct tomoyo_manager *ptr; |
1246 | const char *exe; | 684 | const char *exe; |
1247 | const struct task_struct *task = current; | 685 | const struct task_struct *task = current; |
1248 | const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; | 686 | const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; |
@@ -1252,8 +690,9 @@ static bool tomoyo_is_policy_manager(void) | |||
1252 | return true; | 690 | return true; |
1253 | if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) | 691 | if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) |
1254 | return false; | 692 | return false; |
1255 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 693 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
1256 | if (!ptr->is_deleted && ptr->is_domain | 694 | head.list) { |
695 | if (!ptr->head.is_deleted && ptr->is_domain | ||
1257 | && !tomoyo_pathcmp(domainname, ptr->manager)) { | 696 | && !tomoyo_pathcmp(domainname, ptr->manager)) { |
1258 | found = true; | 697 | found = true; |
1259 | break; | 698 | break; |
@@ -1264,8 +703,9 @@ static bool tomoyo_is_policy_manager(void) | |||
1264 | exe = tomoyo_get_exe(); | 703 | exe = tomoyo_get_exe(); |
1265 | if (!exe) | 704 | if (!exe) |
1266 | return false; | 705 | return false; |
1267 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { | 706 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], |
1268 | if (!ptr->is_deleted && !ptr->is_domain | 707 | head.list) { |
708 | if (!ptr->head.is_deleted && !ptr->is_domain | ||
1269 | && !strcmp(exe, ptr->manager->name)) { | 709 | && !strcmp(exe, ptr->manager->name)) { |
1270 | found = true; | 710 | found = true; |
1271 | break; | 711 | break; |
@@ -1285,7 +725,7 @@ static bool tomoyo_is_policy_manager(void) | |||
1285 | } | 725 | } |
1286 | 726 | ||
1287 | /** | 727 | /** |
1288 | * tomoyo_is_select_one - Parse select command. | 728 | * tomoyo_select_one - Parse select command. |
1289 | * | 729 | * |
1290 | * @head: Pointer to "struct tomoyo_io_buffer". | 730 | * @head: Pointer to "struct tomoyo_io_buffer". |
1291 | * @data: String to parse. | 731 | * @data: String to parse. |
@@ -1294,23 +734,31 @@ static bool tomoyo_is_policy_manager(void) | |||
1294 | * | 734 | * |
1295 | * Caller holds tomoyo_read_lock(). | 735 | * Caller holds tomoyo_read_lock(). |
1296 | */ | 736 | */ |
1297 | static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, | 737 | static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) |
1298 | const char *data) | ||
1299 | { | 738 | { |
1300 | unsigned int pid; | 739 | unsigned int pid; |
1301 | struct tomoyo_domain_info *domain = NULL; | 740 | struct tomoyo_domain_info *domain = NULL; |
741 | bool global_pid = false; | ||
1302 | 742 | ||
1303 | if (sscanf(data, "pid=%u", &pid) == 1) { | 743 | if (!strcmp(data, "allow_execute")) { |
744 | head->r.print_execute_only = true; | ||
745 | return true; | ||
746 | } | ||
747 | if (sscanf(data, "pid=%u", &pid) == 1 || | ||
748 | (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { | ||
1304 | struct task_struct *p; | 749 | struct task_struct *p; |
1305 | rcu_read_lock(); | 750 | rcu_read_lock(); |
1306 | read_lock(&tasklist_lock); | 751 | read_lock(&tasklist_lock); |
1307 | p = find_task_by_vpid(pid); | 752 | if (global_pid) |
753 | p = find_task_by_pid_ns(pid, &init_pid_ns); | ||
754 | else | ||
755 | p = find_task_by_vpid(pid); | ||
1308 | if (p) | 756 | if (p) |
1309 | domain = tomoyo_real_domain(p); | 757 | domain = tomoyo_real_domain(p); |
1310 | read_unlock(&tasklist_lock); | 758 | read_unlock(&tasklist_lock); |
1311 | rcu_read_unlock(); | 759 | rcu_read_unlock(); |
1312 | } else if (!strncmp(data, "domain=", 7)) { | 760 | } else if (!strncmp(data, "domain=", 7)) { |
1313 | if (tomoyo_is_domain_def(data + 7)) | 761 | if (tomoyo_domain_def(data + 7)) |
1314 | domain = tomoyo_find_domain(data + 7); | 762 | domain = tomoyo_find_domain(data + 7); |
1315 | } else | 763 | } else |
1316 | return false; | 764 | return false; |
@@ -1318,24 +766,13 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, | |||
1318 | /* Accessing read_buf is safe because head->io_sem is held. */ | 766 | /* Accessing read_buf is safe because head->io_sem is held. */ |
1319 | if (!head->read_buf) | 767 | if (!head->read_buf) |
1320 | return true; /* Do nothing if open(O_WRONLY). */ | 768 | return true; /* Do nothing if open(O_WRONLY). */ |
1321 | head->read_avail = 0; | 769 | memset(&head->r, 0, sizeof(head->r)); |
770 | head->r.print_this_domain_only = true; | ||
771 | head->r.eof = !domain; | ||
772 | head->r.domain = &domain->list; | ||
1322 | tomoyo_io_printf(head, "# select %s\n", data); | 773 | tomoyo_io_printf(head, "# select %s\n", data); |
1323 | head->read_single_domain = true; | 774 | if (domain && domain->is_deleted) |
1324 | head->read_eof = !domain; | 775 | tomoyo_io_printf(head, "# This is a deleted domain.\n"); |
1325 | if (domain) { | ||
1326 | struct tomoyo_domain_info *d; | ||
1327 | head->read_var1 = NULL; | ||
1328 | list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { | ||
1329 | if (d == domain) | ||
1330 | break; | ||
1331 | head->read_var1 = &d->list; | ||
1332 | } | ||
1333 | head->read_var2 = NULL; | ||
1334 | head->read_bit = 0; | ||
1335 | head->read_step = 0; | ||
1336 | if (domain->is_deleted) | ||
1337 | tomoyo_io_printf(head, "# This is a deleted domain.\n"); | ||
1338 | } | ||
1339 | return true; | 776 | return true; |
1340 | } | 777 | } |
1341 | 778 | ||
@@ -1373,7 +810,7 @@ static int tomoyo_delete_domain(char *domainname) | |||
1373 | } | 810 | } |
1374 | 811 | ||
1375 | /** | 812 | /** |
1376 | * tomoyo_write_domain_policy - Write domain policy. | 813 | * tomoyo_write_domain2 - Write domain policy. |
1377 | * | 814 | * |
1378 | * @head: Pointer to "struct tomoyo_io_buffer". | 815 | * @head: Pointer to "struct tomoyo_io_buffer". |
1379 | * | 816 | * |
@@ -1381,7 +818,24 @@ static int tomoyo_delete_domain(char *domainname) | |||
1381 | * | 818 | * |
1382 | * Caller holds tomoyo_read_lock(). | 819 | * Caller holds tomoyo_read_lock(). |
1383 | */ | 820 | */ |
1384 | static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | 821 | static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, |
822 | const bool is_delete) | ||
823 | { | ||
824 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) | ||
825 | return tomoyo_write_mount(data, domain, is_delete); | ||
826 | return tomoyo_write_file(data, domain, is_delete); | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * tomoyo_write_domain - Write domain policy. | ||
831 | * | ||
832 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
833 | * | ||
834 | * Returns 0 on success, negative value otherwise. | ||
835 | * | ||
836 | * Caller holds tomoyo_read_lock(). | ||
837 | */ | ||
838 | static int tomoyo_write_domain(struct tomoyo_io_buffer *head) | ||
1385 | { | 839 | { |
1386 | char *data = head->write_buf; | 840 | char *data = head->write_buf; |
1387 | struct tomoyo_domain_info *domain = head->write_var1; | 841 | struct tomoyo_domain_info *domain = head->write_var1; |
@@ -1393,19 +847,19 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | |||
1393 | is_delete = true; | 847 | is_delete = true; |
1394 | else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) | 848 | else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) |
1395 | is_select = true; | 849 | is_select = true; |
1396 | if (is_select && tomoyo_is_select_one(head, data)) | 850 | if (is_select && tomoyo_select_one(head, data)) |
1397 | return 0; | 851 | return 0; |
1398 | /* Don't allow updating policies by non manager programs. */ | 852 | /* Don't allow updating policies by non manager programs. */ |
1399 | if (!tomoyo_is_policy_manager()) | 853 | if (!tomoyo_manager()) |
1400 | return -EPERM; | 854 | return -EPERM; |
1401 | if (tomoyo_is_domain_def(data)) { | 855 | if (tomoyo_domain_def(data)) { |
1402 | domain = NULL; | 856 | domain = NULL; |
1403 | if (is_delete) | 857 | if (is_delete) |
1404 | tomoyo_delete_domain(data); | 858 | tomoyo_delete_domain(data); |
1405 | else if (is_select) | 859 | else if (is_select) |
1406 | domain = tomoyo_find_domain(data); | 860 | domain = tomoyo_find_domain(data); |
1407 | else | 861 | else |
1408 | domain = tomoyo_find_or_assign_new_domain(data, 0); | 862 | domain = tomoyo_assign_domain(data, 0); |
1409 | head->write_var1 = domain; | 863 | head->write_var1 = domain; |
1410 | return 0; | 864 | return 0; |
1411 | } | 865 | } |
@@ -1422,179 +876,198 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) | |||
1422 | domain->ignore_global_allow_read = !is_delete; | 876 | domain->ignore_global_allow_read = !is_delete; |
1423 | return 0; | 877 | return 0; |
1424 | } | 878 | } |
1425 | return tomoyo_write_file_policy(data, domain, is_delete); | 879 | if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { |
880 | domain->quota_warned = !is_delete; | ||
881 | return 0; | ||
882 | } | ||
883 | if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { | ||
884 | domain->transition_failed = !is_delete; | ||
885 | return 0; | ||
886 | } | ||
887 | return tomoyo_write_domain2(data, domain, is_delete); | ||
1426 | } | 888 | } |
1427 | 889 | ||
1428 | /** | 890 | /** |
1429 | * tomoyo_print_path_acl - Print a single path ACL entry. | 891 | * tomoyo_fns - Find next set bit. |
1430 | * | 892 | * |
1431 | * @head: Pointer to "struct tomoyo_io_buffer". | 893 | * @perm: 8 bits value. |
1432 | * @ptr: Pointer to "struct tomoyo_path_acl". | 894 | * @bit: First bit to find. |
1433 | * | 895 | * |
1434 | * Returns true on success, false otherwise. | 896 | * Returns next on-bit on success, 8 otherwise. |
1435 | */ | 897 | */ |
1436 | static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, | 898 | static u8 tomoyo_fns(const u8 perm, u8 bit) |
1437 | struct tomoyo_path_acl *ptr) | ||
1438 | { | 899 | { |
1439 | int pos; | 900 | for ( ; bit < 8; bit++) |
1440 | u8 bit; | 901 | if (perm & (1 << bit)) |
1441 | const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); | 902 | break; |
1442 | 903 | return bit; | |
1443 | for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { | ||
1444 | if (!(perm & (1 << bit))) | ||
1445 | continue; | ||
1446 | /* Print "read/write" instead of "read" and "write". */ | ||
1447 | if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) | ||
1448 | && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
1449 | continue; | ||
1450 | pos = head->read_avail; | ||
1451 | if (!tomoyo_io_printf(head, "allow_%s ", | ||
1452 | tomoyo_path2keyword(bit)) || | ||
1453 | !tomoyo_print_name_union(head, &ptr->name) || | ||
1454 | !tomoyo_io_printf(head, "\n")) | ||
1455 | goto out; | ||
1456 | } | ||
1457 | head->read_bit = 0; | ||
1458 | return true; | ||
1459 | out: | ||
1460 | head->read_bit = bit; | ||
1461 | head->read_avail = pos; | ||
1462 | return false; | ||
1463 | } | 904 | } |
1464 | 905 | ||
1465 | /** | 906 | /** |
1466 | * tomoyo_print_path2_acl - Print a double path ACL entry. | 907 | * tomoyo_print_entry - Print an ACL entry. |
1467 | * | 908 | * |
1468 | * @head: Pointer to "struct tomoyo_io_buffer". | 909 | * @head: Pointer to "struct tomoyo_io_buffer". |
1469 | * @ptr: Pointer to "struct tomoyo_path2_acl". | 910 | * @acl: Pointer to an ACL entry. |
1470 | * | 911 | * |
1471 | * Returns true on success, false otherwise. | 912 | * Returns true on success, false otherwise. |
1472 | */ | 913 | */ |
1473 | static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, | 914 | static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, |
1474 | struct tomoyo_path2_acl *ptr) | 915 | struct tomoyo_acl_info *acl) |
1475 | { | 916 | { |
1476 | int pos; | 917 | const u8 acl_type = acl->type; |
1477 | const u8 perm = ptr->perm; | ||
1478 | u8 bit; | 918 | u8 bit; |
1479 | 919 | ||
1480 | for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { | 920 | if (acl->is_deleted) |
1481 | if (!(perm & (1 << bit))) | 921 | return true; |
1482 | continue; | 922 | next: |
1483 | pos = head->read_avail; | 923 | bit = head->r.bit; |
1484 | if (!tomoyo_io_printf(head, "allow_%s ", | 924 | if (!tomoyo_flush(head)) |
1485 | tomoyo_path22keyword(bit)) || | 925 | return false; |
1486 | !tomoyo_print_name_union(head, &ptr->name1) || | 926 | else if (acl_type == TOMOYO_TYPE_PATH_ACL) { |
1487 | !tomoyo_print_name_union(head, &ptr->name2) || | 927 | struct tomoyo_path_acl *ptr = |
1488 | !tomoyo_io_printf(head, "\n")) | 928 | container_of(acl, typeof(*ptr), head); |
1489 | goto out; | 929 | const u16 perm = ptr->perm; |
930 | for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { | ||
931 | if (!(perm & (1 << bit))) | ||
932 | continue; | ||
933 | if (head->r.print_execute_only && | ||
934 | bit != TOMOYO_TYPE_EXECUTE) | ||
935 | continue; | ||
936 | /* Print "read/write" instead of "read" and "write". */ | ||
937 | if ((bit == TOMOYO_TYPE_READ || | ||
938 | bit == TOMOYO_TYPE_WRITE) | ||
939 | && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
940 | continue; | ||
941 | break; | ||
942 | } | ||
943 | if (bit >= TOMOYO_MAX_PATH_OPERATION) | ||
944 | goto done; | ||
945 | tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]); | ||
946 | tomoyo_print_name_union(head, &ptr->name); | ||
947 | } else if (head->r.print_execute_only) { | ||
948 | return true; | ||
949 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | ||
950 | struct tomoyo_path2_acl *ptr = | ||
951 | container_of(acl, typeof(*ptr), head); | ||
952 | bit = tomoyo_fns(ptr->perm, bit); | ||
953 | if (bit >= TOMOYO_MAX_PATH2_OPERATION) | ||
954 | goto done; | ||
955 | tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]); | ||
956 | tomoyo_print_name_union(head, &ptr->name1); | ||
957 | tomoyo_print_name_union(head, &ptr->name2); | ||
958 | } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { | ||
959 | struct tomoyo_path_number_acl *ptr = | ||
960 | container_of(acl, typeof(*ptr), head); | ||
961 | bit = tomoyo_fns(ptr->perm, bit); | ||
962 | if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) | ||
963 | goto done; | ||
964 | tomoyo_io_printf(head, "allow_%s", | ||
965 | tomoyo_path_number_keyword[bit]); | ||
966 | tomoyo_print_name_union(head, &ptr->name); | ||
967 | tomoyo_print_number_union(head, &ptr->number); | ||
968 | } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { | ||
969 | struct tomoyo_mkdev_acl *ptr = | ||
970 | container_of(acl, typeof(*ptr), head); | ||
971 | bit = tomoyo_fns(ptr->perm, bit); | ||
972 | if (bit >= TOMOYO_MAX_MKDEV_OPERATION) | ||
973 | goto done; | ||
974 | tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]); | ||
975 | tomoyo_print_name_union(head, &ptr->name); | ||
976 | tomoyo_print_number_union(head, &ptr->mode); | ||
977 | tomoyo_print_number_union(head, &ptr->major); | ||
978 | tomoyo_print_number_union(head, &ptr->minor); | ||
979 | } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { | ||
980 | struct tomoyo_mount_acl *ptr = | ||
981 | container_of(acl, typeof(*ptr), head); | ||
982 | tomoyo_io_printf(head, "allow_mount"); | ||
983 | tomoyo_print_name_union(head, &ptr->dev_name); | ||
984 | tomoyo_print_name_union(head, &ptr->dir_name); | ||
985 | tomoyo_print_name_union(head, &ptr->fs_type); | ||
986 | tomoyo_print_number_union(head, &ptr->flags); | ||
1490 | } | 987 | } |
1491 | head->read_bit = 0; | 988 | head->r.bit = bit + 1; |
989 | tomoyo_io_printf(head, "\n"); | ||
990 | if (acl_type != TOMOYO_TYPE_MOUNT_ACL) | ||
991 | goto next; | ||
992 | done: | ||
993 | head->r.bit = 0; | ||
1492 | return true; | 994 | return true; |
1493 | out: | ||
1494 | head->read_bit = bit; | ||
1495 | head->read_avail = pos; | ||
1496 | return false; | ||
1497 | } | 995 | } |
1498 | 996 | ||
1499 | /** | 997 | /** |
1500 | * tomoyo_print_entry - Print an ACL entry. | 998 | * tomoyo_read_domain2 - Read domain policy. |
1501 | * | 999 | * |
1502 | * @head: Pointer to "struct tomoyo_io_buffer". | 1000 | * @head: Pointer to "struct tomoyo_io_buffer". |
1503 | * @ptr: Pointer to an ACL entry. | 1001 | * @domain: Pointer to "struct tomoyo_domain_info". |
1002 | * | ||
1003 | * Caller holds tomoyo_read_lock(). | ||
1504 | * | 1004 | * |
1505 | * Returns true on success, false otherwise. | 1005 | * Returns true on success, false otherwise. |
1506 | */ | 1006 | */ |
1507 | static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | 1007 | static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, |
1508 | struct tomoyo_acl_info *ptr) | 1008 | struct tomoyo_domain_info *domain) |
1509 | { | 1009 | { |
1510 | const u8 acl_type = ptr->type; | 1010 | list_for_each_cookie(head->r.acl, &domain->acl_info_list) { |
1511 | 1011 | struct tomoyo_acl_info *ptr = | |
1512 | if (acl_type == TOMOYO_TYPE_PATH_ACL) { | 1012 | list_entry(head->r.acl, typeof(*ptr), list); |
1513 | struct tomoyo_path_acl *acl | 1013 | if (!tomoyo_print_entry(head, ptr)) |
1514 | = container_of(ptr, struct tomoyo_path_acl, head); | 1014 | return false; |
1515 | return tomoyo_print_path_acl(head, acl); | ||
1516 | } | ||
1517 | if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | ||
1518 | struct tomoyo_path2_acl *acl | ||
1519 | = container_of(ptr, struct tomoyo_path2_acl, head); | ||
1520 | return tomoyo_print_path2_acl(head, acl); | ||
1521 | } | 1015 | } |
1522 | BUG(); /* This must not happen. */ | 1016 | head->r.acl = NULL; |
1523 | return false; | 1017 | return true; |
1524 | } | 1018 | } |
1525 | 1019 | ||
1526 | /** | 1020 | /** |
1527 | * tomoyo_read_domain_policy - Read domain policy. | 1021 | * tomoyo_read_domain - Read domain policy. |
1528 | * | 1022 | * |
1529 | * @head: Pointer to "struct tomoyo_io_buffer". | 1023 | * @head: Pointer to "struct tomoyo_io_buffer". |
1530 | * | 1024 | * |
1531 | * Returns 0. | ||
1532 | * | ||
1533 | * Caller holds tomoyo_read_lock(). | 1025 | * Caller holds tomoyo_read_lock(). |
1534 | */ | 1026 | */ |
1535 | static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) | 1027 | static void tomoyo_read_domain(struct tomoyo_io_buffer *head) |
1536 | { | 1028 | { |
1537 | struct list_head *dpos; | 1029 | if (head->r.eof) |
1538 | struct list_head *apos; | 1030 | return; |
1539 | bool done = true; | 1031 | list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { |
1540 | 1032 | struct tomoyo_domain_info *domain = | |
1541 | if (head->read_eof) | 1033 | list_entry(head->r.domain, typeof(*domain), list); |
1542 | return 0; | 1034 | switch (head->r.step) { |
1543 | if (head->read_step == 0) | 1035 | case 0: |
1544 | head->read_step = 1; | 1036 | if (domain->is_deleted && |
1545 | list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { | 1037 | !head->r.print_this_domain_only) |
1546 | struct tomoyo_domain_info *domain; | 1038 | continue; |
1547 | const char *quota_exceeded = ""; | 1039 | /* Print domainname and flags. */ |
1548 | const char *transition_failed = ""; | 1040 | tomoyo_set_string(head, domain->domainname->name); |
1549 | const char *ignore_global_allow_read = ""; | 1041 | tomoyo_set_lf(head); |
1550 | domain = list_entry(dpos, struct tomoyo_domain_info, list); | 1042 | tomoyo_io_printf(head, |
1551 | if (head->read_step != 1) | 1043 | TOMOYO_KEYWORD_USE_PROFILE "%u\n", |
1552 | goto acl_loop; | 1044 | domain->profile); |
1553 | if (domain->is_deleted && !head->read_single_domain) | 1045 | if (domain->quota_warned) |
1554 | continue; | 1046 | tomoyo_set_string(head, "quota_exceeded\n"); |
1555 | /* Print domainname and flags. */ | 1047 | if (domain->transition_failed) |
1556 | if (domain->quota_warned) | 1048 | tomoyo_set_string(head, "transition_failed\n"); |
1557 | quota_exceeded = "quota_exceeded\n"; | 1049 | if (domain->ignore_global_allow_read) |
1558 | if (domain->transition_failed) | 1050 | tomoyo_set_string(head, |
1559 | transition_failed = "transition_failed\n"; | 1051 | TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ |
1560 | if (domain->ignore_global_allow_read) | 1052 | "\n"); |
1561 | ignore_global_allow_read | 1053 | head->r.step++; |
1562 | = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; | 1054 | tomoyo_set_lf(head); |
1563 | done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE | 1055 | /* fall through */ |
1564 | "%u\n%s%s%s\n", | 1056 | case 1: |
1565 | domain->domainname->name, | 1057 | if (!tomoyo_read_domain2(head, domain)) |
1566 | domain->profile, quota_exceeded, | 1058 | return; |
1567 | transition_failed, | 1059 | head->r.step++; |
1568 | ignore_global_allow_read); | 1060 | if (!tomoyo_set_lf(head)) |
1569 | if (!done) | 1061 | return; |
1570 | break; | 1062 | /* fall through */ |
1571 | head->read_step = 2; | 1063 | case 2: |
1572 | acl_loop: | 1064 | head->r.step = 0; |
1573 | if (head->read_step == 3) | 1065 | if (head->r.print_this_domain_only) |
1574 | goto tail_mark; | 1066 | goto done; |
1575 | /* Print ACL entries in the domain. */ | ||
1576 | list_for_each_cookie(apos, head->read_var2, | ||
1577 | &domain->acl_info_list) { | ||
1578 | struct tomoyo_acl_info *ptr | ||
1579 | = list_entry(apos, struct tomoyo_acl_info, | ||
1580 | list); | ||
1581 | done = tomoyo_print_entry(head, ptr); | ||
1582 | if (!done) | ||
1583 | break; | ||
1584 | } | 1067 | } |
1585 | if (!done) | ||
1586 | break; | ||
1587 | head->read_step = 3; | ||
1588 | tail_mark: | ||
1589 | done = tomoyo_io_printf(head, "\n"); | ||
1590 | if (!done) | ||
1591 | break; | ||
1592 | head->read_step = 1; | ||
1593 | if (head->read_single_domain) | ||
1594 | break; | ||
1595 | } | 1068 | } |
1596 | head->read_eof = done; | 1069 | done: |
1597 | return 0; | 1070 | head->r.eof = true; |
1598 | } | 1071 | } |
1599 | 1072 | ||
1600 | /** | 1073 | /** |
@@ -1607,7 +1080,7 @@ tail_mark: | |||
1607 | * This is equivalent to doing | 1080 | * This is equivalent to doing |
1608 | * | 1081 | * |
1609 | * ( echo "select " $domainname; echo "use_profile " $profile ) | | 1082 | * ( echo "select " $domainname; echo "use_profile " $profile ) | |
1610 | * /usr/lib/ccs/loadpolicy -d | 1083 | * /usr/sbin/tomoyo-loadpolicy -d |
1611 | * | 1084 | * |
1612 | * Caller holds tomoyo_read_lock(). | 1085 | * Caller holds tomoyo_read_lock(). |
1613 | */ | 1086 | */ |
@@ -1646,25 +1119,22 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) | |||
1646 | * | 1119 | * |
1647 | * Caller holds tomoyo_read_lock(). | 1120 | * Caller holds tomoyo_read_lock(). |
1648 | */ | 1121 | */ |
1649 | static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) | 1122 | static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) |
1650 | { | 1123 | { |
1651 | struct list_head *pos; | 1124 | if (head->r.eof) |
1652 | bool done = true; | 1125 | return; |
1653 | 1126 | list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { | |
1654 | if (head->read_eof) | 1127 | struct tomoyo_domain_info *domain = |
1655 | return 0; | 1128 | list_entry(head->r.domain, typeof(*domain), list); |
1656 | list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { | ||
1657 | struct tomoyo_domain_info *domain; | ||
1658 | domain = list_entry(pos, struct tomoyo_domain_info, list); | ||
1659 | if (domain->is_deleted) | 1129 | if (domain->is_deleted) |
1660 | continue; | 1130 | continue; |
1661 | done = tomoyo_io_printf(head, "%u %s\n", domain->profile, | 1131 | if (!tomoyo_flush(head)) |
1662 | domain->domainname->name); | 1132 | return; |
1663 | if (!done) | 1133 | tomoyo_io_printf(head, "%u ", domain->profile); |
1664 | break; | 1134 | tomoyo_set_string(head, domain->domainname->name); |
1135 | tomoyo_set_lf(head); | ||
1665 | } | 1136 | } |
1666 | head->read_eof = done; | 1137 | head->r.eof = true; |
1667 | return 0; | ||
1668 | } | 1138 | } |
1669 | 1139 | ||
1670 | /** | 1140 | /** |
@@ -1676,11 +1146,7 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) | |||
1676 | */ | 1146 | */ |
1677 | static int tomoyo_write_pid(struct tomoyo_io_buffer *head) | 1147 | static int tomoyo_write_pid(struct tomoyo_io_buffer *head) |
1678 | { | 1148 | { |
1679 | unsigned long pid; | 1149 | head->r.eof = false; |
1680 | /* No error check. */ | ||
1681 | strict_strtoul(head->write_buf, 10, &pid); | ||
1682 | head->read_step = (int) pid; | ||
1683 | head->read_eof = false; | ||
1684 | return 0; | 1150 | return 0; |
1685 | } | 1151 | } |
1686 | 1152 | ||
@@ -1694,29 +1160,57 @@ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) | |||
1694 | * The PID is specified by tomoyo_write_pid() so that the user can obtain | 1160 | * The PID is specified by tomoyo_write_pid() so that the user can obtain |
1695 | * using read()/write() interface rather than sysctl() interface. | 1161 | * using read()/write() interface rather than sysctl() interface. |
1696 | */ | 1162 | */ |
1697 | static int tomoyo_read_pid(struct tomoyo_io_buffer *head) | 1163 | static void tomoyo_read_pid(struct tomoyo_io_buffer *head) |
1698 | { | 1164 | { |
1699 | if (head->read_avail == 0 && !head->read_eof) { | 1165 | char *buf = head->write_buf; |
1700 | const int pid = head->read_step; | 1166 | bool global_pid = false; |
1701 | struct task_struct *p; | 1167 | unsigned int pid; |
1702 | struct tomoyo_domain_info *domain = NULL; | 1168 | struct task_struct *p; |
1703 | rcu_read_lock(); | 1169 | struct tomoyo_domain_info *domain = NULL; |
1704 | read_lock(&tasklist_lock); | 1170 | |
1705 | p = find_task_by_vpid(pid); | 1171 | /* Accessing write_buf is safe because head->io_sem is held. */ |
1706 | if (p) | 1172 | if (!buf) { |
1707 | domain = tomoyo_real_domain(p); | 1173 | head->r.eof = true; |
1708 | read_unlock(&tasklist_lock); | 1174 | return; /* Do nothing if open(O_RDONLY). */ |
1709 | rcu_read_unlock(); | ||
1710 | if (domain) | ||
1711 | tomoyo_io_printf(head, "%d %u %s", pid, domain->profile, | ||
1712 | domain->domainname->name); | ||
1713 | head->read_eof = true; | ||
1714 | } | 1175 | } |
1715 | return 0; | 1176 | if (head->r.w_pos || head->r.eof) |
1177 | return; | ||
1178 | head->r.eof = true; | ||
1179 | if (tomoyo_str_starts(&buf, "global-pid ")) | ||
1180 | global_pid = true; | ||
1181 | pid = (unsigned int) simple_strtoul(buf, NULL, 10); | ||
1182 | rcu_read_lock(); | ||
1183 | read_lock(&tasklist_lock); | ||
1184 | if (global_pid) | ||
1185 | p = find_task_by_pid_ns(pid, &init_pid_ns); | ||
1186 | else | ||
1187 | p = find_task_by_vpid(pid); | ||
1188 | if (p) | ||
1189 | domain = tomoyo_real_domain(p); | ||
1190 | read_unlock(&tasklist_lock); | ||
1191 | rcu_read_unlock(); | ||
1192 | if (!domain) | ||
1193 | return; | ||
1194 | tomoyo_io_printf(head, "%u %u ", pid, domain->profile); | ||
1195 | tomoyo_set_string(head, domain->domainname->name); | ||
1716 | } | 1196 | } |
1717 | 1197 | ||
1198 | static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { | ||
1199 | [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] | ||
1200 | = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, | ||
1201 | [TOMOYO_TRANSITION_CONTROL_INITIALIZE] | ||
1202 | = TOMOYO_KEYWORD_INITIALIZE_DOMAIN, | ||
1203 | [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN, | ||
1204 | [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN | ||
1205 | }; | ||
1206 | |||
1207 | static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { | ||
1208 | [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, | ||
1209 | [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP | ||
1210 | }; | ||
1211 | |||
1718 | /** | 1212 | /** |
1719 | * tomoyo_write_exception_policy - Write exception policy. | 1213 | * tomoyo_write_exception - Write exception policy. |
1720 | * | 1214 | * |
1721 | * @head: Pointer to "struct tomoyo_io_buffer". | 1215 | * @head: Pointer to "struct tomoyo_io_buffer". |
1722 | * | 1216 | * |
@@ -1724,186 +1218,523 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) | |||
1724 | * | 1218 | * |
1725 | * Caller holds tomoyo_read_lock(). | 1219 | * Caller holds tomoyo_read_lock(). |
1726 | */ | 1220 | */ |
1727 | static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) | 1221 | static int tomoyo_write_exception(struct tomoyo_io_buffer *head) |
1728 | { | 1222 | { |
1729 | char *data = head->write_buf; | 1223 | char *data = head->write_buf; |
1730 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); | 1224 | bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); |
1731 | 1225 | u8 i; | |
1732 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN)) | 1226 | static const struct { |
1733 | return tomoyo_write_domain_keeper_policy(data, false, | 1227 | const char *keyword; |
1734 | is_delete); | 1228 | int (*write) (char *, const bool); |
1735 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN)) | 1229 | } tomoyo_callback[4] = { |
1736 | return tomoyo_write_domain_keeper_policy(data, true, is_delete); | 1230 | { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, |
1737 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN)) | 1231 | { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, |
1738 | return tomoyo_write_domain_initializer_policy(data, false, | 1232 | { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, |
1739 | is_delete); | 1233 | { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, |
1740 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN)) | 1234 | }; |
1741 | return tomoyo_write_domain_initializer_policy(data, true, | 1235 | |
1742 | is_delete); | 1236 | for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) |
1743 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS)) | 1237 | if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) |
1744 | return tomoyo_write_alias_policy(data, is_delete); | 1238 | return tomoyo_write_transition_control(data, is_delete, |
1745 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) | 1239 | i); |
1746 | return tomoyo_write_globally_readable_policy(data, is_delete); | 1240 | for (i = 0; i < 4; i++) |
1747 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN)) | 1241 | if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword)) |
1748 | return tomoyo_write_pattern_policy(data, is_delete); | 1242 | return tomoyo_callback[i].write(data, is_delete); |
1749 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) | 1243 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) |
1750 | return tomoyo_write_no_rewrite_policy(data, is_delete); | 1244 | if (tomoyo_str_starts(&data, tomoyo_group_name[i])) |
1751 | if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) | 1245 | return tomoyo_write_group(data, is_delete, i); |
1752 | return tomoyo_write_path_group_policy(data, is_delete); | ||
1753 | return -EINVAL; | 1246 | return -EINVAL; |
1754 | } | 1247 | } |
1755 | 1248 | ||
1756 | /** | 1249 | /** |
1757 | * tomoyo_read_exception_policy - Read exception policy. | 1250 | * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. |
1758 | * | 1251 | * |
1759 | * @head: Pointer to "struct tomoyo_io_buffer". | 1252 | * @head: Pointer to "struct tomoyo_io_buffer". |
1253 | * @idx: Index number. | ||
1760 | * | 1254 | * |
1761 | * Returns 0 on success, -EINVAL otherwise. | 1255 | * Returns true on success, false otherwise. |
1762 | * | 1256 | * |
1763 | * Caller holds tomoyo_read_lock(). | 1257 | * Caller holds tomoyo_read_lock(). |
1764 | */ | 1258 | */ |
1765 | static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) | 1259 | static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) |
1766 | { | 1260 | { |
1767 | if (!head->read_eof) { | 1261 | list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { |
1768 | switch (head->read_step) { | 1262 | struct tomoyo_group *group = |
1769 | case 0: | 1263 | list_entry(head->r.group, typeof(*group), list); |
1770 | head->read_var2 = NULL; | 1264 | list_for_each_cookie(head->r.acl, &group->member_list) { |
1771 | head->read_step = 1; | 1265 | struct tomoyo_acl_head *ptr = |
1772 | case 1: | 1266 | list_entry(head->r.acl, typeof(*ptr), list); |
1773 | if (!tomoyo_read_domain_keeper_policy(head)) | 1267 | if (ptr->is_deleted) |
1774 | break; | 1268 | continue; |
1775 | head->read_var2 = NULL; | 1269 | if (!tomoyo_flush(head)) |
1776 | head->read_step = 2; | 1270 | return false; |
1777 | case 2: | 1271 | tomoyo_set_string(head, tomoyo_group_name[idx]); |
1778 | if (!tomoyo_read_globally_readable_policy(head)) | 1272 | tomoyo_set_string(head, group->group_name->name); |
1779 | break; | 1273 | if (idx == TOMOYO_PATH_GROUP) { |
1780 | head->read_var2 = NULL; | 1274 | tomoyo_set_space(head); |
1781 | head->read_step = 3; | 1275 | tomoyo_set_string(head, container_of |
1782 | case 3: | 1276 | (ptr, struct tomoyo_path_group, |
1783 | head->read_var2 = NULL; | 1277 | head)->member_name->name); |
1784 | head->read_step = 4; | 1278 | } else if (idx == TOMOYO_NUMBER_GROUP) { |
1785 | case 4: | 1279 | tomoyo_print_number_union(head, &container_of |
1786 | if (!tomoyo_read_domain_initializer_policy(head)) | 1280 | (ptr, |
1787 | break; | 1281 | struct tomoyo_number_group, |
1788 | head->read_var2 = NULL; | 1282 | head)->number); |
1789 | head->read_step = 5; | 1283 | } |
1790 | case 5: | 1284 | tomoyo_set_lf(head); |
1791 | if (!tomoyo_read_alias_policy(head)) | 1285 | } |
1792 | break; | 1286 | head->r.acl = NULL; |
1793 | head->read_var2 = NULL; | 1287 | } |
1794 | head->read_step = 6; | 1288 | head->r.group = NULL; |
1795 | case 6: | 1289 | return true; |
1796 | head->read_var2 = NULL; | 1290 | } |
1797 | head->read_step = 7; | 1291 | |
1798 | case 7: | 1292 | /** |
1799 | if (!tomoyo_read_file_pattern(head)) | 1293 | * tomoyo_read_policy - Read "struct tomoyo_..._entry" list. |
1800 | break; | 1294 | * |
1801 | head->read_var2 = NULL; | 1295 | * @head: Pointer to "struct tomoyo_io_buffer". |
1802 | head->read_step = 8; | 1296 | * @idx: Index number. |
1803 | case 8: | 1297 | * |
1804 | if (!tomoyo_read_no_rewrite_policy(head)) | 1298 | * Returns true on success, false otherwise. |
1805 | break; | 1299 | * |
1806 | head->read_var2 = NULL; | 1300 | * Caller holds tomoyo_read_lock(). |
1807 | head->read_step = 9; | 1301 | */ |
1808 | case 9: | 1302 | static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) |
1809 | if (!tomoyo_read_path_group_policy(head)) | 1303 | { |
1810 | break; | 1304 | list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { |
1811 | head->read_var1 = NULL; | 1305 | struct tomoyo_acl_head *acl = |
1812 | head->read_var2 = NULL; | 1306 | container_of(head->r.acl, typeof(*acl), list); |
1813 | head->read_step = 10; | 1307 | if (acl->is_deleted) |
1814 | case 10: | 1308 | continue; |
1815 | head->read_eof = true; | 1309 | if (!tomoyo_flush(head)) |
1310 | return false; | ||
1311 | switch (idx) { | ||
1312 | case TOMOYO_ID_TRANSITION_CONTROL: | ||
1313 | { | ||
1314 | struct tomoyo_transition_control *ptr = | ||
1315 | container_of(acl, typeof(*ptr), head); | ||
1316 | tomoyo_set_string(head, | ||
1317 | tomoyo_transition_type | ||
1318 | [ptr->type]); | ||
1319 | if (ptr->program) | ||
1320 | tomoyo_set_string(head, | ||
1321 | ptr->program->name); | ||
1322 | if (ptr->program && ptr->domainname) | ||
1323 | tomoyo_set_string(head, " from "); | ||
1324 | if (ptr->domainname) | ||
1325 | tomoyo_set_string(head, | ||
1326 | ptr->domainname-> | ||
1327 | name); | ||
1328 | } | ||
1329 | break; | ||
1330 | case TOMOYO_ID_GLOBALLY_READABLE: | ||
1331 | { | ||
1332 | struct tomoyo_readable_file *ptr = | ||
1333 | container_of(acl, typeof(*ptr), head); | ||
1334 | tomoyo_set_string(head, | ||
1335 | TOMOYO_KEYWORD_ALLOW_READ); | ||
1336 | tomoyo_set_string(head, ptr->filename->name); | ||
1337 | } | ||
1338 | break; | ||
1339 | case TOMOYO_ID_AGGREGATOR: | ||
1340 | { | ||
1341 | struct tomoyo_aggregator *ptr = | ||
1342 | container_of(acl, typeof(*ptr), head); | ||
1343 | tomoyo_set_string(head, | ||
1344 | TOMOYO_KEYWORD_AGGREGATOR); | ||
1345 | tomoyo_set_string(head, | ||
1346 | ptr->original_name->name); | ||
1347 | tomoyo_set_space(head); | ||
1348 | tomoyo_set_string(head, | ||
1349 | ptr->aggregated_name->name); | ||
1350 | } | ||
1351 | break; | ||
1352 | case TOMOYO_ID_PATTERN: | ||
1353 | { | ||
1354 | struct tomoyo_no_pattern *ptr = | ||
1355 | container_of(acl, typeof(*ptr), head); | ||
1356 | tomoyo_set_string(head, | ||
1357 | TOMOYO_KEYWORD_FILE_PATTERN); | ||
1358 | tomoyo_set_string(head, ptr->pattern->name); | ||
1359 | } | ||
1360 | break; | ||
1361 | case TOMOYO_ID_NO_REWRITE: | ||
1362 | { | ||
1363 | struct tomoyo_no_rewrite *ptr = | ||
1364 | container_of(acl, typeof(*ptr), head); | ||
1365 | tomoyo_set_string(head, | ||
1366 | TOMOYO_KEYWORD_DENY_REWRITE); | ||
1367 | tomoyo_set_string(head, ptr->pattern->name); | ||
1368 | } | ||
1816 | break; | 1369 | break; |
1817 | default: | 1370 | default: |
1818 | return -EINVAL; | 1371 | continue; |
1819 | } | 1372 | } |
1373 | tomoyo_set_lf(head); | ||
1820 | } | 1374 | } |
1821 | return 0; | 1375 | head->r.acl = NULL; |
1376 | return true; | ||
1822 | } | 1377 | } |
1823 | 1378 | ||
1824 | /* path to policy loader */ | 1379 | /** |
1825 | static const char *tomoyo_loader = "/sbin/tomoyo-init"; | 1380 | * tomoyo_read_exception - Read exception policy. |
1381 | * | ||
1382 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
1383 | * | ||
1384 | * Caller holds tomoyo_read_lock(). | ||
1385 | */ | ||
1386 | static void tomoyo_read_exception(struct tomoyo_io_buffer *head) | ||
1387 | { | ||
1388 | if (head->r.eof) | ||
1389 | return; | ||
1390 | while (head->r.step < TOMOYO_MAX_POLICY && | ||
1391 | tomoyo_read_policy(head, head->r.step)) | ||
1392 | head->r.step++; | ||
1393 | if (head->r.step < TOMOYO_MAX_POLICY) | ||
1394 | return; | ||
1395 | while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && | ||
1396 | tomoyo_read_group(head, head->r.step - TOMOYO_MAX_POLICY)) | ||
1397 | head->r.step++; | ||
1398 | if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) | ||
1399 | return; | ||
1400 | head->r.eof = true; | ||
1401 | } | ||
1826 | 1402 | ||
1827 | /** | 1403 | /** |
1828 | * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. | 1404 | * tomoyo_print_header - Get header line of audit log. |
1405 | * | ||
1406 | * @r: Pointer to "struct tomoyo_request_info". | ||
1829 | * | 1407 | * |
1830 | * Returns true if /sbin/tomoyo-init exists, false otherwise. | 1408 | * Returns string representation. |
1409 | * | ||
1410 | * This function uses kmalloc(), so caller must kfree() if this function | ||
1411 | * didn't return NULL. | ||
1831 | */ | 1412 | */ |
1832 | static bool tomoyo_policy_loader_exists(void) | 1413 | static char *tomoyo_print_header(struct tomoyo_request_info *r) |
1833 | { | 1414 | { |
1834 | /* | 1415 | struct timeval tv; |
1835 | * Don't activate MAC if the policy loader doesn't exist. | 1416 | const pid_t gpid = task_pid_nr(current); |
1836 | * If the initrd includes /sbin/init but real-root-dev has not | 1417 | static const int tomoyo_buffer_len = 4096; |
1837 | * mounted on / yet, activating MAC will block the system since | 1418 | char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); |
1838 | * policies are not loaded yet. | 1419 | if (!buffer) |
1839 | * Thus, let do_execve() call this function everytime. | 1420 | return NULL; |
1840 | */ | 1421 | do_gettimeofday(&tv); |
1841 | struct path path; | 1422 | snprintf(buffer, tomoyo_buffer_len - 1, |
1423 | "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" | ||
1424 | " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" | ||
1425 | " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", | ||
1426 | tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid, | ||
1427 | (pid_t) sys_getpid(), (pid_t) sys_getppid(), | ||
1428 | current_uid(), current_gid(), current_euid(), | ||
1429 | current_egid(), current_suid(), current_sgid(), | ||
1430 | current_fsuid(), current_fsgid()); | ||
1431 | return buffer; | ||
1432 | } | ||
1842 | 1433 | ||
1843 | if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { | 1434 | /** |
1844 | printk(KERN_INFO "Not activating Mandatory Access Control now " | 1435 | * tomoyo_init_audit_log - Allocate buffer for audit logs. |
1845 | "since %s doesn't exist.\n", tomoyo_loader); | 1436 | * |
1846 | return false; | 1437 | * @len: Required size. |
1438 | * @r: Pointer to "struct tomoyo_request_info". | ||
1439 | * | ||
1440 | * Returns pointer to allocated memory. | ||
1441 | * | ||
1442 | * The @len is updated to add the header lines' size on success. | ||
1443 | * | ||
1444 | * This function uses kzalloc(), so caller must kfree() if this function | ||
1445 | * didn't return NULL. | ||
1446 | */ | ||
1447 | static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) | ||
1448 | { | ||
1449 | char *buf = NULL; | ||
1450 | const char *header; | ||
1451 | const char *domainname; | ||
1452 | if (!r->domain) | ||
1453 | r->domain = tomoyo_domain(); | ||
1454 | domainname = r->domain->domainname->name; | ||
1455 | header = tomoyo_print_header(r); | ||
1456 | if (!header) | ||
1457 | return NULL; | ||
1458 | *len += strlen(domainname) + strlen(header) + 10; | ||
1459 | buf = kzalloc(*len, GFP_NOFS); | ||
1460 | if (buf) | ||
1461 | snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); | ||
1462 | kfree(header); | ||
1463 | return buf; | ||
1464 | } | ||
1465 | |||
1466 | /* Wait queue for tomoyo_query_list. */ | ||
1467 | static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); | ||
1468 | |||
1469 | /* Lock for manipulating tomoyo_query_list. */ | ||
1470 | static DEFINE_SPINLOCK(tomoyo_query_list_lock); | ||
1471 | |||
1472 | /* Structure for query. */ | ||
1473 | struct tomoyo_query { | ||
1474 | struct list_head list; | ||
1475 | char *query; | ||
1476 | int query_len; | ||
1477 | unsigned int serial; | ||
1478 | int timer; | ||
1479 | int answer; | ||
1480 | }; | ||
1481 | |||
1482 | /* The list for "struct tomoyo_query". */ | ||
1483 | static LIST_HEAD(tomoyo_query_list); | ||
1484 | |||
1485 | /* | ||
1486 | * Number of "struct file" referring /sys/kernel/security/tomoyo/query | ||
1487 | * interface. | ||
1488 | */ | ||
1489 | static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); | ||
1490 | |||
1491 | /** | ||
1492 | * tomoyo_supervisor - Ask for the supervisor's decision. | ||
1493 | * | ||
1494 | * @r: Pointer to "struct tomoyo_request_info". | ||
1495 | * @fmt: The printf()'s format string, followed by parameters. | ||
1496 | * | ||
1497 | * Returns 0 if the supervisor decided to permit the access request which | ||
1498 | * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the | ||
1499 | * supervisor decided to retry the access request which violated the policy in | ||
1500 | * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise. | ||
1501 | */ | ||
1502 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) | ||
1503 | { | ||
1504 | va_list args; | ||
1505 | int error = -EPERM; | ||
1506 | int pos; | ||
1507 | int len; | ||
1508 | static unsigned int tomoyo_serial; | ||
1509 | struct tomoyo_query *entry = NULL; | ||
1510 | bool quota_exceeded = false; | ||
1511 | char *header; | ||
1512 | switch (r->mode) { | ||
1513 | char *buffer; | ||
1514 | case TOMOYO_CONFIG_LEARNING: | ||
1515 | if (!tomoyo_domain_quota_is_ok(r)) | ||
1516 | return 0; | ||
1517 | va_start(args, fmt); | ||
1518 | len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; | ||
1519 | va_end(args); | ||
1520 | buffer = kmalloc(len, GFP_NOFS); | ||
1521 | if (!buffer) | ||
1522 | return 0; | ||
1523 | va_start(args, fmt); | ||
1524 | vsnprintf(buffer, len - 1, fmt, args); | ||
1525 | va_end(args); | ||
1526 | tomoyo_normalize_line(buffer); | ||
1527 | tomoyo_write_domain2(buffer, r->domain, false); | ||
1528 | kfree(buffer); | ||
1529 | /* fall through */ | ||
1530 | case TOMOYO_CONFIG_PERMISSIVE: | ||
1531 | return 0; | ||
1847 | } | 1532 | } |
1848 | path_put(&path); | 1533 | if (!r->domain) |
1849 | return true; | 1534 | r->domain = tomoyo_domain(); |
1535 | if (!atomic_read(&tomoyo_query_observers)) | ||
1536 | return -EPERM; | ||
1537 | va_start(args, fmt); | ||
1538 | len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; | ||
1539 | va_end(args); | ||
1540 | header = tomoyo_init_audit_log(&len, r); | ||
1541 | if (!header) | ||
1542 | goto out; | ||
1543 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
1544 | if (!entry) | ||
1545 | goto out; | ||
1546 | entry->query = kzalloc(len, GFP_NOFS); | ||
1547 | if (!entry->query) | ||
1548 | goto out; | ||
1549 | len = ksize(entry->query); | ||
1550 | spin_lock(&tomoyo_query_list_lock); | ||
1551 | if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + | ||
1552 | sizeof(*entry) >= tomoyo_quota_for_query) { | ||
1553 | quota_exceeded = true; | ||
1554 | } else { | ||
1555 | tomoyo_query_memory_size += len + sizeof(*entry); | ||
1556 | entry->serial = tomoyo_serial++; | ||
1557 | } | ||
1558 | spin_unlock(&tomoyo_query_list_lock); | ||
1559 | if (quota_exceeded) | ||
1560 | goto out; | ||
1561 | pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", | ||
1562 | entry->serial, r->retry, header); | ||
1563 | kfree(header); | ||
1564 | header = NULL; | ||
1565 | va_start(args, fmt); | ||
1566 | vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); | ||
1567 | entry->query_len = strlen(entry->query) + 1; | ||
1568 | va_end(args); | ||
1569 | spin_lock(&tomoyo_query_list_lock); | ||
1570 | list_add_tail(&entry->list, &tomoyo_query_list); | ||
1571 | spin_unlock(&tomoyo_query_list_lock); | ||
1572 | /* Give 10 seconds for supervisor's opinion. */ | ||
1573 | for (entry->timer = 0; | ||
1574 | atomic_read(&tomoyo_query_observers) && entry->timer < 100; | ||
1575 | entry->timer++) { | ||
1576 | wake_up(&tomoyo_query_wait); | ||
1577 | set_current_state(TASK_INTERRUPTIBLE); | ||
1578 | schedule_timeout(HZ / 10); | ||
1579 | if (entry->answer) | ||
1580 | break; | ||
1581 | } | ||
1582 | spin_lock(&tomoyo_query_list_lock); | ||
1583 | list_del(&entry->list); | ||
1584 | tomoyo_query_memory_size -= len + sizeof(*entry); | ||
1585 | spin_unlock(&tomoyo_query_list_lock); | ||
1586 | switch (entry->answer) { | ||
1587 | case 3: /* Asked to retry by administrator. */ | ||
1588 | error = TOMOYO_RETRY_REQUEST; | ||
1589 | r->retry++; | ||
1590 | break; | ||
1591 | case 1: | ||
1592 | /* Granted by administrator. */ | ||
1593 | error = 0; | ||
1594 | break; | ||
1595 | case 0: | ||
1596 | /* Timed out. */ | ||
1597 | break; | ||
1598 | default: | ||
1599 | /* Rejected by administrator. */ | ||
1600 | break; | ||
1601 | } | ||
1602 | out: | ||
1603 | if (entry) | ||
1604 | kfree(entry->query); | ||
1605 | kfree(entry); | ||
1606 | kfree(header); | ||
1607 | return error; | ||
1850 | } | 1608 | } |
1851 | 1609 | ||
1852 | /** | 1610 | /** |
1853 | * tomoyo_load_policy - Run external policy loader to load policy. | 1611 | * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query. |
1854 | * | 1612 | * |
1855 | * @filename: The program about to start. | 1613 | * @file: Pointer to "struct file". |
1614 | * @wait: Pointer to "poll_table". | ||
1856 | * | 1615 | * |
1857 | * This function checks whether @filename is /sbin/init , and if so | 1616 | * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise. |
1858 | * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init | ||
1859 | * and then continues invocation of /sbin/init. | ||
1860 | * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and | ||
1861 | * writes to /sys/kernel/security/tomoyo/ interfaces. | ||
1862 | * | 1617 | * |
1863 | * Returns nothing. | 1618 | * Waits for access requests which violated policy in enforcing mode. |
1864 | */ | 1619 | */ |
1865 | void tomoyo_load_policy(const char *filename) | 1620 | static int tomoyo_poll_query(struct file *file, poll_table *wait) |
1866 | { | 1621 | { |
1867 | char *argv[2]; | 1622 | struct list_head *tmp; |
1868 | char *envp[3]; | 1623 | bool found = false; |
1624 | u8 i; | ||
1625 | for (i = 0; i < 2; i++) { | ||
1626 | spin_lock(&tomoyo_query_list_lock); | ||
1627 | list_for_each(tmp, &tomoyo_query_list) { | ||
1628 | struct tomoyo_query *ptr = | ||
1629 | list_entry(tmp, typeof(*ptr), list); | ||
1630 | if (ptr->answer) | ||
1631 | continue; | ||
1632 | found = true; | ||
1633 | break; | ||
1634 | } | ||
1635 | spin_unlock(&tomoyo_query_list_lock); | ||
1636 | if (found) | ||
1637 | return POLLIN | POLLRDNORM; | ||
1638 | if (i) | ||
1639 | break; | ||
1640 | poll_wait(file, &tomoyo_query_wait, wait); | ||
1641 | } | ||
1642 | return 0; | ||
1643 | } | ||
1869 | 1644 | ||
1870 | if (tomoyo_policy_loaded) | 1645 | /** |
1646 | * tomoyo_read_query - Read access requests which violated policy in enforcing mode. | ||
1647 | * | ||
1648 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
1649 | */ | ||
1650 | static void tomoyo_read_query(struct tomoyo_io_buffer *head) | ||
1651 | { | ||
1652 | struct list_head *tmp; | ||
1653 | int pos = 0; | ||
1654 | int len = 0; | ||
1655 | char *buf; | ||
1656 | if (head->r.w_pos) | ||
1871 | return; | 1657 | return; |
1872 | /* | 1658 | if (head->read_buf) { |
1873 | * Check filename is /sbin/init or /sbin/tomoyo-start. | 1659 | kfree(head->read_buf); |
1874 | * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't | 1660 | head->read_buf = NULL; |
1875 | * be passed. | 1661 | } |
1876 | * You can create /sbin/tomoyo-start by | 1662 | spin_lock(&tomoyo_query_list_lock); |
1877 | * "ln -s /bin/true /sbin/tomoyo-start". | 1663 | list_for_each(tmp, &tomoyo_query_list) { |
1878 | */ | 1664 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); |
1879 | if (strcmp(filename, "/sbin/init") && | 1665 | if (ptr->answer) |
1880 | strcmp(filename, "/sbin/tomoyo-start")) | 1666 | continue; |
1667 | if (pos++ != head->r.query_index) | ||
1668 | continue; | ||
1669 | len = ptr->query_len; | ||
1670 | break; | ||
1671 | } | ||
1672 | spin_unlock(&tomoyo_query_list_lock); | ||
1673 | if (!len) { | ||
1674 | head->r.query_index = 0; | ||
1881 | return; | 1675 | return; |
1882 | if (!tomoyo_policy_loader_exists()) | 1676 | } |
1677 | buf = kzalloc(len, GFP_NOFS); | ||
1678 | if (!buf) | ||
1883 | return; | 1679 | return; |
1680 | pos = 0; | ||
1681 | spin_lock(&tomoyo_query_list_lock); | ||
1682 | list_for_each(tmp, &tomoyo_query_list) { | ||
1683 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
1684 | if (ptr->answer) | ||
1685 | continue; | ||
1686 | if (pos++ != head->r.query_index) | ||
1687 | continue; | ||
1688 | /* | ||
1689 | * Some query can be skipped because tomoyo_query_list | ||
1690 | * can change, but I don't care. | ||
1691 | */ | ||
1692 | if (len == ptr->query_len) | ||
1693 | memmove(buf, ptr->query, len); | ||
1694 | break; | ||
1695 | } | ||
1696 | spin_unlock(&tomoyo_query_list_lock); | ||
1697 | if (buf[0]) { | ||
1698 | head->read_buf = buf; | ||
1699 | head->r.w[head->r.w_pos++] = buf; | ||
1700 | head->r.query_index++; | ||
1701 | } else { | ||
1702 | kfree(buf); | ||
1703 | } | ||
1704 | } | ||
1884 | 1705 | ||
1885 | printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | 1706 | /** |
1886 | tomoyo_loader); | 1707 | * tomoyo_write_answer - Write the supervisor's decision. |
1887 | argv[0] = (char *) tomoyo_loader; | 1708 | * |
1888 | argv[1] = NULL; | 1709 | * @head: Pointer to "struct tomoyo_io_buffer". |
1889 | envp[0] = "HOME=/"; | 1710 | * |
1890 | envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | 1711 | * Returns 0 on success, -EINVAL otherwise. |
1891 | envp[2] = NULL; | 1712 | */ |
1892 | call_usermodehelper(argv[0], argv, envp, 1); | 1713 | static int tomoyo_write_answer(struct tomoyo_io_buffer *head) |
1893 | 1714 | { | |
1894 | printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); | 1715 | char *data = head->write_buf; |
1895 | printk(KERN_INFO "Mandatory Access Control activated.\n"); | 1716 | struct list_head *tmp; |
1896 | tomoyo_policy_loaded = true; | 1717 | unsigned int serial; |
1897 | { /* Check all profiles currently assigned to domains are defined. */ | 1718 | unsigned int answer; |
1898 | struct tomoyo_domain_info *domain; | 1719 | spin_lock(&tomoyo_query_list_lock); |
1899 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 1720 | list_for_each(tmp, &tomoyo_query_list) { |
1900 | const u8 profile = domain->profile; | 1721 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); |
1901 | if (tomoyo_profile_ptr[profile]) | 1722 | ptr->timer = 0; |
1902 | continue; | 1723 | } |
1903 | panic("Profile %u (used by '%s') not defined.\n", | 1724 | spin_unlock(&tomoyo_query_list_lock); |
1904 | profile, domain->domainname->name); | 1725 | if (sscanf(data, "A%u=%u", &serial, &answer) != 2) |
1905 | } | 1726 | return -EINVAL; |
1727 | spin_lock(&tomoyo_query_list_lock); | ||
1728 | list_for_each(tmp, &tomoyo_query_list) { | ||
1729 | struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
1730 | if (ptr->serial != serial) | ||
1731 | continue; | ||
1732 | if (!ptr->answer) | ||
1733 | ptr->answer = answer; | ||
1734 | break; | ||
1906 | } | 1735 | } |
1736 | spin_unlock(&tomoyo_query_list_lock); | ||
1737 | return 0; | ||
1907 | } | 1738 | } |
1908 | 1739 | ||
1909 | /** | 1740 | /** |
@@ -1913,13 +1744,12 @@ void tomoyo_load_policy(const char *filename) | |||
1913 | * | 1744 | * |
1914 | * Returns version information. | 1745 | * Returns version information. |
1915 | */ | 1746 | */ |
1916 | static int tomoyo_read_version(struct tomoyo_io_buffer *head) | 1747 | static void tomoyo_read_version(struct tomoyo_io_buffer *head) |
1917 | { | 1748 | { |
1918 | if (!head->read_eof) { | 1749 | if (!head->r.eof) { |
1919 | tomoyo_io_printf(head, "2.2.0"); | 1750 | tomoyo_io_printf(head, "2.3.0"); |
1920 | head->read_eof = true; | 1751 | head->r.eof = true; |
1921 | } | 1752 | } |
1922 | return 0; | ||
1923 | } | 1753 | } |
1924 | 1754 | ||
1925 | /** | 1755 | /** |
@@ -1929,18 +1759,17 @@ static int tomoyo_read_version(struct tomoyo_io_buffer *head) | |||
1929 | * | 1759 | * |
1930 | * Returns the current process's domainname. | 1760 | * Returns the current process's domainname. |
1931 | */ | 1761 | */ |
1932 | static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | 1762 | static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) |
1933 | { | 1763 | { |
1934 | if (!head->read_eof) { | 1764 | if (!head->r.eof) { |
1935 | /* | 1765 | /* |
1936 | * tomoyo_domain()->domainname != NULL | 1766 | * tomoyo_domain()->domainname != NULL |
1937 | * because every process belongs to a domain and | 1767 | * because every process belongs to a domain and |
1938 | * the domain's name cannot be NULL. | 1768 | * the domain's name cannot be NULL. |
1939 | */ | 1769 | */ |
1940 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); | 1770 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); |
1941 | head->read_eof = true; | 1771 | head->r.eof = true; |
1942 | } | 1772 | } |
1943 | return 0; | ||
1944 | } | 1773 | } |
1945 | 1774 | ||
1946 | /** | 1775 | /** |
@@ -1953,23 +1782,24 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | |||
1953 | * | 1782 | * |
1954 | * Caller acquires tomoyo_read_lock(). | 1783 | * Caller acquires tomoyo_read_lock(). |
1955 | */ | 1784 | */ |
1956 | static int tomoyo_open_control(const u8 type, struct file *file) | 1785 | int tomoyo_open_control(const u8 type, struct file *file) |
1957 | { | 1786 | { |
1958 | struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); | 1787 | struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); |
1959 | 1788 | ||
1960 | if (!head) | 1789 | if (!head) |
1961 | return -ENOMEM; | 1790 | return -ENOMEM; |
1962 | mutex_init(&head->io_sem); | 1791 | mutex_init(&head->io_sem); |
1792 | head->type = type; | ||
1963 | switch (type) { | 1793 | switch (type) { |
1964 | case TOMOYO_DOMAINPOLICY: | 1794 | case TOMOYO_DOMAINPOLICY: |
1965 | /* /sys/kernel/security/tomoyo/domain_policy */ | 1795 | /* /sys/kernel/security/tomoyo/domain_policy */ |
1966 | head->write = tomoyo_write_domain_policy; | 1796 | head->write = tomoyo_write_domain; |
1967 | head->read = tomoyo_read_domain_policy; | 1797 | head->read = tomoyo_read_domain; |
1968 | break; | 1798 | break; |
1969 | case TOMOYO_EXCEPTIONPOLICY: | 1799 | case TOMOYO_EXCEPTIONPOLICY: |
1970 | /* /sys/kernel/security/tomoyo/exception_policy */ | 1800 | /* /sys/kernel/security/tomoyo/exception_policy */ |
1971 | head->write = tomoyo_write_exception_policy; | 1801 | head->write = tomoyo_write_exception; |
1972 | head->read = tomoyo_read_exception_policy; | 1802 | head->read = tomoyo_read_exception; |
1973 | break; | 1803 | break; |
1974 | case TOMOYO_SELFDOMAIN: | 1804 | case TOMOYO_SELFDOMAIN: |
1975 | /* /sys/kernel/security/tomoyo/self_domain */ | 1805 | /* /sys/kernel/security/tomoyo/self_domain */ |
@@ -2001,10 +1831,15 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
2001 | head->write = tomoyo_write_profile; | 1831 | head->write = tomoyo_write_profile; |
2002 | head->read = tomoyo_read_profile; | 1832 | head->read = tomoyo_read_profile; |
2003 | break; | 1833 | break; |
1834 | case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ | ||
1835 | head->poll = tomoyo_poll_query; | ||
1836 | head->write = tomoyo_write_answer; | ||
1837 | head->read = tomoyo_read_query; | ||
1838 | break; | ||
2004 | case TOMOYO_MANAGER: | 1839 | case TOMOYO_MANAGER: |
2005 | /* /sys/kernel/security/tomoyo/manager */ | 1840 | /* /sys/kernel/security/tomoyo/manager */ |
2006 | head->write = tomoyo_write_manager_policy; | 1841 | head->write = tomoyo_write_manager; |
2007 | head->read = tomoyo_read_manager_policy; | 1842 | head->read = tomoyo_read_manager; |
2008 | break; | 1843 | break; |
2009 | } | 1844 | } |
2010 | if (!(file->f_mode & FMODE_READ)) { | 1845 | if (!(file->f_mode & FMODE_READ)) { |
@@ -2013,7 +1848,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
2013 | * for reading. | 1848 | * for reading. |
2014 | */ | 1849 | */ |
2015 | head->read = NULL; | 1850 | head->read = NULL; |
2016 | } else { | 1851 | head->poll = NULL; |
1852 | } else if (!head->poll) { | ||
1853 | /* Don't allocate read_buf for poll() access. */ | ||
2017 | if (!head->readbuf_size) | 1854 | if (!head->readbuf_size) |
2018 | head->readbuf_size = 4096 * 2; | 1855 | head->readbuf_size = 4096 * 2; |
2019 | head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); | 1856 | head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); |
@@ -2037,7 +1874,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
2037 | return -ENOMEM; | 1874 | return -ENOMEM; |
2038 | } | 1875 | } |
2039 | } | 1876 | } |
2040 | head->reader_idx = tomoyo_read_lock(); | 1877 | if (type != TOMOYO_QUERY) |
1878 | head->reader_idx = tomoyo_read_lock(); | ||
2041 | file->private_data = head; | 1879 | file->private_data = head; |
2042 | /* | 1880 | /* |
2043 | * Call the handler now if the file is | 1881 | * Call the handler now if the file is |
@@ -2048,10 +1886,35 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
2048 | */ | 1886 | */ |
2049 | if (type == TOMOYO_SELFDOMAIN) | 1887 | if (type == TOMOYO_SELFDOMAIN) |
2050 | tomoyo_read_control(file, NULL, 0); | 1888 | tomoyo_read_control(file, NULL, 0); |
1889 | /* | ||
1890 | * If the file is /sys/kernel/security/tomoyo/query , increment the | ||
1891 | * observer counter. | ||
1892 | * The obserber counter is used by tomoyo_supervisor() to see if | ||
1893 | * there is some process monitoring /sys/kernel/security/tomoyo/query. | ||
1894 | */ | ||
1895 | else if (type == TOMOYO_QUERY) | ||
1896 | atomic_inc(&tomoyo_query_observers); | ||
2051 | return 0; | 1897 | return 0; |
2052 | } | 1898 | } |
2053 | 1899 | ||
2054 | /** | 1900 | /** |
1901 | * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. | ||
1902 | * | ||
1903 | * @file: Pointer to "struct file". | ||
1904 | * @wait: Pointer to "poll_table". | ||
1905 | * | ||
1906 | * Waits for read readiness. | ||
1907 | * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . | ||
1908 | */ | ||
1909 | int tomoyo_poll_control(struct file *file, poll_table *wait) | ||
1910 | { | ||
1911 | struct tomoyo_io_buffer *head = file->private_data; | ||
1912 | if (!head->poll) | ||
1913 | return -ENOSYS; | ||
1914 | return head->poll(file, wait); | ||
1915 | } | ||
1916 | |||
1917 | /** | ||
2055 | * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. | 1918 | * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. |
2056 | * | 1919 | * |
2057 | * @file: Pointer to "struct file". | 1920 | * @file: Pointer to "struct file". |
@@ -2062,36 +1925,23 @@ static int tomoyo_open_control(const u8 type, struct file *file) | |||
2062 | * | 1925 | * |
2063 | * Caller holds tomoyo_read_lock(). | 1926 | * Caller holds tomoyo_read_lock(). |
2064 | */ | 1927 | */ |
2065 | static int tomoyo_read_control(struct file *file, char __user *buffer, | 1928 | int tomoyo_read_control(struct file *file, char __user *buffer, |
2066 | const int buffer_len) | 1929 | const int buffer_len) |
2067 | { | 1930 | { |
2068 | int len = 0; | 1931 | int len; |
2069 | struct tomoyo_io_buffer *head = file->private_data; | 1932 | struct tomoyo_io_buffer *head = file->private_data; |
2070 | char *cp; | ||
2071 | 1933 | ||
2072 | if (!head->read) | 1934 | if (!head->read) |
2073 | return -ENOSYS; | 1935 | return -ENOSYS; |
2074 | if (mutex_lock_interruptible(&head->io_sem)) | 1936 | if (mutex_lock_interruptible(&head->io_sem)) |
2075 | return -EINTR; | 1937 | return -EINTR; |
2076 | /* Call the policy handler. */ | 1938 | head->read_user_buf = buffer; |
2077 | len = head->read(head); | 1939 | head->read_user_buf_avail = buffer_len; |
2078 | if (len < 0) | 1940 | if (tomoyo_flush(head)) |
2079 | goto out; | 1941 | /* Call the policy handler. */ |
2080 | /* Write to buffer. */ | 1942 | head->read(head); |
2081 | len = head->read_avail; | 1943 | tomoyo_flush(head); |
2082 | if (len > buffer_len) | 1944 | len = head->read_user_buf - buffer; |
2083 | len = buffer_len; | ||
2084 | if (!len) | ||
2085 | goto out; | ||
2086 | /* head->read_buf changes by some functions. */ | ||
2087 | cp = head->read_buf; | ||
2088 | if (copy_to_user(buffer, cp, len)) { | ||
2089 | len = -EFAULT; | ||
2090 | goto out; | ||
2091 | } | ||
2092 | head->read_avail -= len; | ||
2093 | memmove(cp, cp + len, head->read_avail); | ||
2094 | out: | ||
2095 | mutex_unlock(&head->io_sem); | 1945 | mutex_unlock(&head->io_sem); |
2096 | return len; | 1946 | return len; |
2097 | } | 1947 | } |
@@ -2107,8 +1957,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, | |||
2107 | * | 1957 | * |
2108 | * Caller holds tomoyo_read_lock(). | 1958 | * Caller holds tomoyo_read_lock(). |
2109 | */ | 1959 | */ |
2110 | static int tomoyo_write_control(struct file *file, const char __user *buffer, | 1960 | int tomoyo_write_control(struct file *file, const char __user *buffer, |
2111 | const int buffer_len) | 1961 | const int buffer_len) |
2112 | { | 1962 | { |
2113 | struct tomoyo_io_buffer *head = file->private_data; | 1963 | struct tomoyo_io_buffer *head = file->private_data; |
2114 | int error = buffer_len; | 1964 | int error = buffer_len; |
@@ -2121,8 +1971,7 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, | |||
2121 | return -EFAULT; | 1971 | return -EFAULT; |
2122 | /* Don't allow updating policies by non manager programs. */ | 1972 | /* Don't allow updating policies by non manager programs. */ |
2123 | if (head->write != tomoyo_write_pid && | 1973 | if (head->write != tomoyo_write_pid && |
2124 | head->write != tomoyo_write_domain_policy && | 1974 | head->write != tomoyo_write_domain && !tomoyo_manager()) |
2125 | !tomoyo_is_policy_manager()) | ||
2126 | return -EPERM; | 1975 | return -EPERM; |
2127 | if (mutex_lock_interruptible(&head->io_sem)) | 1976 | if (mutex_lock_interruptible(&head->io_sem)) |
2128 | return -EINTR; | 1977 | return -EINTR; |
@@ -2159,12 +2008,19 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, | |||
2159 | * | 2008 | * |
2160 | * Caller looses tomoyo_read_lock(). | 2009 | * Caller looses tomoyo_read_lock(). |
2161 | */ | 2010 | */ |
2162 | static int tomoyo_close_control(struct file *file) | 2011 | int tomoyo_close_control(struct file *file) |
2163 | { | 2012 | { |
2164 | struct tomoyo_io_buffer *head = file->private_data; | 2013 | struct tomoyo_io_buffer *head = file->private_data; |
2165 | const bool is_write = !!head->write_buf; | 2014 | const bool is_write = !!head->write_buf; |
2166 | 2015 | ||
2167 | tomoyo_read_unlock(head->reader_idx); | 2016 | /* |
2017 | * If the file is /sys/kernel/security/tomoyo/query , decrement the | ||
2018 | * observer counter. | ||
2019 | */ | ||
2020 | if (head->type == TOMOYO_QUERY) | ||
2021 | atomic_dec(&tomoyo_query_observers); | ||
2022 | else | ||
2023 | tomoyo_read_unlock(head->reader_idx); | ||
2168 | /* Release memory used for policy I/O. */ | 2024 | /* Release memory used for policy I/O. */ |
2169 | kfree(head->read_buf); | 2025 | kfree(head->read_buf); |
2170 | head->read_buf = NULL; | 2026 | head->read_buf = NULL; |
@@ -2179,129 +2035,25 @@ static int tomoyo_close_control(struct file *file) | |||
2179 | } | 2035 | } |
2180 | 2036 | ||
2181 | /** | 2037 | /** |
2182 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | 2038 | * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. |
2183 | * | ||
2184 | * @inode: Pointer to "struct inode". | ||
2185 | * @file: Pointer to "struct file". | ||
2186 | * | ||
2187 | * Returns 0 on success, negative value otherwise. | ||
2188 | */ | ||
2189 | static int tomoyo_open(struct inode *inode, struct file *file) | ||
2190 | { | ||
2191 | const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) | ||
2192 | - ((u8 *) NULL); | ||
2193 | return tomoyo_open_control(key, file); | ||
2194 | } | ||
2195 | |||
2196 | /** | ||
2197 | * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. | ||
2198 | * | ||
2199 | * @inode: Pointer to "struct inode". | ||
2200 | * @file: Pointer to "struct file". | ||
2201 | * | ||
2202 | * Returns 0 on success, negative value otherwise. | ||
2203 | */ | ||
2204 | static int tomoyo_release(struct inode *inode, struct file *file) | ||
2205 | { | ||
2206 | return tomoyo_close_control(file); | ||
2207 | } | ||
2208 | |||
2209 | /** | ||
2210 | * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. | ||
2211 | * | ||
2212 | * @file: Pointer to "struct file". | ||
2213 | * @buf: Pointer to buffer. | ||
2214 | * @count: Size of @buf. | ||
2215 | * @ppos: Unused. | ||
2216 | * | ||
2217 | * Returns bytes read on success, negative value otherwise. | ||
2218 | */ | 2039 | */ |
2219 | static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, | 2040 | void tomoyo_check_profile(void) |
2220 | loff_t *ppos) | ||
2221 | { | 2041 | { |
2222 | return tomoyo_read_control(file, buf, count); | 2042 | struct tomoyo_domain_info *domain; |
2223 | } | 2043 | const int idx = tomoyo_read_lock(); |
2224 | 2044 | tomoyo_policy_loaded = true; | |
2225 | /** | 2045 | /* Check all profiles currently assigned to domains are defined. */ |
2226 | * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. | 2046 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
2227 | * | 2047 | const u8 profile = domain->profile; |
2228 | * @file: Pointer to "struct file". | 2048 | if (tomoyo_profile_ptr[profile]) |
2229 | * @buf: Pointer to buffer. | 2049 | continue; |
2230 | * @count: Size of @buf. | 2050 | panic("Profile %u (used by '%s') not defined.\n", |
2231 | * @ppos: Unused. | 2051 | profile, domain->domainname->name); |
2232 | * | 2052 | } |
2233 | * Returns @count on success, negative value otherwise. | 2053 | tomoyo_read_unlock(idx); |
2234 | */ | 2054 | if (tomoyo_profile_version != 20090903) |
2235 | static ssize_t tomoyo_write(struct file *file, const char __user *buf, | 2055 | panic("Profile version %u is not supported.\n", |
2236 | size_t count, loff_t *ppos) | 2056 | tomoyo_profile_version); |
2237 | { | 2057 | printk(KERN_INFO "TOMOYO: 2.3.0\n"); |
2238 | return tomoyo_write_control(file, buf, count); | 2058 | printk(KERN_INFO "Mandatory Access Control activated.\n"); |
2239 | } | ||
2240 | |||
2241 | /* | ||
2242 | * tomoyo_operations is a "struct file_operations" which is used for handling | ||
2243 | * /sys/kernel/security/tomoyo/ interface. | ||
2244 | * | ||
2245 | * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). | ||
2246 | * See tomoyo_io_buffer for internals. | ||
2247 | */ | ||
2248 | static const struct file_operations tomoyo_operations = { | ||
2249 | .open = tomoyo_open, | ||
2250 | .release = tomoyo_release, | ||
2251 | .read = tomoyo_read, | ||
2252 | .write = tomoyo_write, | ||
2253 | }; | ||
2254 | |||
2255 | /** | ||
2256 | * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. | ||
2257 | * | ||
2258 | * @name: The name of the interface file. | ||
2259 | * @mode: The permission of the interface file. | ||
2260 | * @parent: The parent directory. | ||
2261 | * @key: Type of interface. | ||
2262 | * | ||
2263 | * Returns nothing. | ||
2264 | */ | ||
2265 | static void __init tomoyo_create_entry(const char *name, const mode_t mode, | ||
2266 | struct dentry *parent, const u8 key) | ||
2267 | { | ||
2268 | securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | ||
2269 | &tomoyo_operations); | ||
2270 | } | ||
2271 | |||
2272 | /** | ||
2273 | * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. | ||
2274 | * | ||
2275 | * Returns 0. | ||
2276 | */ | ||
2277 | static int __init tomoyo_initerface_init(void) | ||
2278 | { | ||
2279 | struct dentry *tomoyo_dir; | ||
2280 | |||
2281 | /* Don't create securityfs entries unless registered. */ | ||
2282 | if (current_cred()->security != &tomoyo_kernel_domain) | ||
2283 | return 0; | ||
2284 | |||
2285 | tomoyo_dir = securityfs_create_dir("tomoyo", NULL); | ||
2286 | tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, | ||
2287 | TOMOYO_DOMAINPOLICY); | ||
2288 | tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, | ||
2289 | TOMOYO_EXCEPTIONPOLICY); | ||
2290 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
2291 | TOMOYO_SELFDOMAIN); | ||
2292 | tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, | ||
2293 | TOMOYO_DOMAIN_STATUS); | ||
2294 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | ||
2295 | TOMOYO_PROCESS_STATUS); | ||
2296 | tomoyo_create_entry("meminfo", 0600, tomoyo_dir, | ||
2297 | TOMOYO_MEMINFO); | ||
2298 | tomoyo_create_entry("profile", 0600, tomoyo_dir, | ||
2299 | TOMOYO_PROFILE); | ||
2300 | tomoyo_create_entry("manager", 0600, tomoyo_dir, | ||
2301 | TOMOYO_MANAGER); | ||
2302 | tomoyo_create_entry("version", 0400, tomoyo_dir, | ||
2303 | TOMOYO_VERSION); | ||
2304 | return 0; | ||
2305 | } | 2059 | } |
2306 | |||
2307 | fs_initcall(tomoyo_initerface_init); | ||
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9f1ae5e3ba5..04454cb7b24 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/mount.h> | 20 | #include <linux/mount.h> |
21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
22 | #include <linux/cred.h> | 22 | #include <linux/cred.h> |
23 | #include <linux/poll.h> | ||
23 | struct linux_binprm; | 24 | struct linux_binprm; |
24 | 25 | ||
25 | /********** Constants definitions. **********/ | 26 | /********** Constants definitions. **********/ |
@@ -32,20 +33,44 @@ struct linux_binprm; | |||
32 | #define TOMOYO_HASH_BITS 8 | 33 | #define TOMOYO_HASH_BITS 8 |
33 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) | 34 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) |
34 | 35 | ||
35 | /* | 36 | #define TOMOYO_EXEC_TMPSIZE 4096 |
36 | * This is the max length of a token. | ||
37 | * | ||
38 | * A token consists of only ASCII printable characters. | ||
39 | * Non printable characters in a token is represented in \ooo style | ||
40 | * octal string. Thus, \ itself is represented as \\. | ||
41 | */ | ||
42 | #define TOMOYO_MAX_PATHNAME_LEN 4000 | ||
43 | 37 | ||
44 | /* Profile number is an integer between 0 and 255. */ | 38 | /* Profile number is an integer between 0 and 255. */ |
45 | #define TOMOYO_MAX_PROFILES 256 | 39 | #define TOMOYO_MAX_PROFILES 256 |
46 | 40 | ||
41 | enum tomoyo_mode_index { | ||
42 | TOMOYO_CONFIG_DISABLED, | ||
43 | TOMOYO_CONFIG_LEARNING, | ||
44 | TOMOYO_CONFIG_PERMISSIVE, | ||
45 | TOMOYO_CONFIG_ENFORCING, | ||
46 | TOMOYO_CONFIG_USE_DEFAULT = 255 | ||
47 | }; | ||
48 | |||
49 | enum tomoyo_policy_id { | ||
50 | TOMOYO_ID_GROUP, | ||
51 | TOMOYO_ID_PATH_GROUP, | ||
52 | TOMOYO_ID_NUMBER_GROUP, | ||
53 | TOMOYO_ID_TRANSITION_CONTROL, | ||
54 | TOMOYO_ID_AGGREGATOR, | ||
55 | TOMOYO_ID_GLOBALLY_READABLE, | ||
56 | TOMOYO_ID_PATTERN, | ||
57 | TOMOYO_ID_NO_REWRITE, | ||
58 | TOMOYO_ID_MANAGER, | ||
59 | TOMOYO_ID_NAME, | ||
60 | TOMOYO_ID_ACL, | ||
61 | TOMOYO_ID_DOMAIN, | ||
62 | TOMOYO_MAX_POLICY | ||
63 | }; | ||
64 | |||
65 | enum tomoyo_group_id { | ||
66 | TOMOYO_PATH_GROUP, | ||
67 | TOMOYO_NUMBER_GROUP, | ||
68 | TOMOYO_MAX_GROUP | ||
69 | }; | ||
70 | |||
47 | /* Keywords for ACLs. */ | 71 | /* Keywords for ACLs. */ |
48 | #define TOMOYO_KEYWORD_ALIAS "alias " | 72 | #define TOMOYO_KEYWORD_AGGREGATOR "aggregator " |
73 | #define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " | ||
49 | #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " | 74 | #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " |
50 | #define TOMOYO_KEYWORD_DELETE "delete " | 75 | #define TOMOYO_KEYWORD_DELETE "delete " |
51 | #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " | 76 | #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " |
@@ -55,36 +80,51 @@ struct linux_binprm; | |||
55 | #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " | 80 | #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " |
56 | #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " | 81 | #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " |
57 | #define TOMOYO_KEYWORD_PATH_GROUP "path_group " | 82 | #define TOMOYO_KEYWORD_PATH_GROUP "path_group " |
83 | #define TOMOYO_KEYWORD_NUMBER_GROUP "number_group " | ||
58 | #define TOMOYO_KEYWORD_SELECT "select " | 84 | #define TOMOYO_KEYWORD_SELECT "select " |
59 | #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " | 85 | #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " |
60 | #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" | 86 | #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" |
87 | #define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded" | ||
88 | #define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed" | ||
61 | /* A domain definition starts with <kernel>. */ | 89 | /* A domain definition starts with <kernel>. */ |
62 | #define TOMOYO_ROOT_NAME "<kernel>" | 90 | #define TOMOYO_ROOT_NAME "<kernel>" |
63 | #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) | 91 | #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) |
64 | 92 | ||
65 | /* Index numbers for Access Controls. */ | 93 | /* Value type definition. */ |
66 | enum tomoyo_mac_index { | 94 | #define TOMOYO_VALUE_TYPE_INVALID 0 |
67 | TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ | 95 | #define TOMOYO_VALUE_TYPE_DECIMAL 1 |
68 | TOMOYO_MAX_ACCEPT_ENTRY, | 96 | #define TOMOYO_VALUE_TYPE_OCTAL 2 |
69 | TOMOYO_VERBOSE, | 97 | #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 |
70 | TOMOYO_MAX_CONTROL_INDEX | 98 | |
99 | enum tomoyo_transition_type { | ||
100 | /* Do not change this order, */ | ||
101 | TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE, | ||
102 | TOMOYO_TRANSITION_CONTROL_INITIALIZE, | ||
103 | TOMOYO_TRANSITION_CONTROL_NO_KEEP, | ||
104 | TOMOYO_TRANSITION_CONTROL_KEEP, | ||
105 | TOMOYO_MAX_TRANSITION_TYPE | ||
71 | }; | 106 | }; |
72 | 107 | ||
73 | /* Index numbers for Access Controls. */ | 108 | /* Index numbers for Access Controls. */ |
74 | enum tomoyo_acl_entry_type_index { | 109 | enum tomoyo_acl_entry_type_index { |
75 | TOMOYO_TYPE_PATH_ACL, | 110 | TOMOYO_TYPE_PATH_ACL, |
76 | TOMOYO_TYPE_PATH2_ACL, | 111 | TOMOYO_TYPE_PATH2_ACL, |
112 | TOMOYO_TYPE_PATH_NUMBER_ACL, | ||
113 | TOMOYO_TYPE_MKDEV_ACL, | ||
114 | TOMOYO_TYPE_MOUNT_ACL, | ||
77 | }; | 115 | }; |
78 | 116 | ||
79 | /* Index numbers for File Controls. */ | 117 | /* Index numbers for File Controls. */ |
80 | 118 | ||
81 | /* | 119 | /* |
82 | * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set | 120 | * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically |
83 | * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and | 121 | * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. |
84 | * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. | 122 | * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if |
85 | * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or | 123 | * TOMOYO_TYPE_READ_WRITE is set. |
86 | * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are | 124 | * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ |
87 | * automatically cleared if TYPE_READ_WRITE_ACL is cleared. | 125 | * or TOMOYO_TYPE_WRITE is cleared. |
126 | * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if | ||
127 | * TOMOYO_TYPE_READ_WRITE is cleared. | ||
88 | */ | 128 | */ |
89 | 129 | ||
90 | enum tomoyo_path_acl_index { | 130 | enum tomoyo_path_acl_index { |
@@ -92,27 +132,24 @@ enum tomoyo_path_acl_index { | |||
92 | TOMOYO_TYPE_EXECUTE, | 132 | TOMOYO_TYPE_EXECUTE, |
93 | TOMOYO_TYPE_READ, | 133 | TOMOYO_TYPE_READ, |
94 | TOMOYO_TYPE_WRITE, | 134 | TOMOYO_TYPE_WRITE, |
95 | TOMOYO_TYPE_CREATE, | ||
96 | TOMOYO_TYPE_UNLINK, | 135 | TOMOYO_TYPE_UNLINK, |
97 | TOMOYO_TYPE_MKDIR, | ||
98 | TOMOYO_TYPE_RMDIR, | 136 | TOMOYO_TYPE_RMDIR, |
99 | TOMOYO_TYPE_MKFIFO, | ||
100 | TOMOYO_TYPE_MKSOCK, | ||
101 | TOMOYO_TYPE_MKBLOCK, | ||
102 | TOMOYO_TYPE_MKCHAR, | ||
103 | TOMOYO_TYPE_TRUNCATE, | 137 | TOMOYO_TYPE_TRUNCATE, |
104 | TOMOYO_TYPE_SYMLINK, | 138 | TOMOYO_TYPE_SYMLINK, |
105 | TOMOYO_TYPE_REWRITE, | 139 | TOMOYO_TYPE_REWRITE, |
106 | TOMOYO_TYPE_IOCTL, | ||
107 | TOMOYO_TYPE_CHMOD, | ||
108 | TOMOYO_TYPE_CHOWN, | ||
109 | TOMOYO_TYPE_CHGRP, | ||
110 | TOMOYO_TYPE_CHROOT, | 140 | TOMOYO_TYPE_CHROOT, |
111 | TOMOYO_TYPE_MOUNT, | ||
112 | TOMOYO_TYPE_UMOUNT, | 141 | TOMOYO_TYPE_UMOUNT, |
113 | TOMOYO_MAX_PATH_OPERATION | 142 | TOMOYO_MAX_PATH_OPERATION |
114 | }; | 143 | }; |
115 | 144 | ||
145 | #define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) | ||
146 | |||
147 | enum tomoyo_mkdev_acl_index { | ||
148 | TOMOYO_TYPE_MKBLOCK, | ||
149 | TOMOYO_TYPE_MKCHAR, | ||
150 | TOMOYO_MAX_MKDEV_OPERATION | ||
151 | }; | ||
152 | |||
116 | enum tomoyo_path2_acl_index { | 153 | enum tomoyo_path2_acl_index { |
117 | TOMOYO_TYPE_LINK, | 154 | TOMOYO_TYPE_LINK, |
118 | TOMOYO_TYPE_RENAME, | 155 | TOMOYO_TYPE_RENAME, |
@@ -120,6 +157,18 @@ enum tomoyo_path2_acl_index { | |||
120 | TOMOYO_MAX_PATH2_OPERATION | 157 | TOMOYO_MAX_PATH2_OPERATION |
121 | }; | 158 | }; |
122 | 159 | ||
160 | enum tomoyo_path_number_acl_index { | ||
161 | TOMOYO_TYPE_CREATE, | ||
162 | TOMOYO_TYPE_MKDIR, | ||
163 | TOMOYO_TYPE_MKFIFO, | ||
164 | TOMOYO_TYPE_MKSOCK, | ||
165 | TOMOYO_TYPE_IOCTL, | ||
166 | TOMOYO_TYPE_CHMOD, | ||
167 | TOMOYO_TYPE_CHOWN, | ||
168 | TOMOYO_TYPE_CHGRP, | ||
169 | TOMOYO_MAX_PATH_NUMBER_OPERATION | ||
170 | }; | ||
171 | |||
123 | enum tomoyo_securityfs_interface_index { | 172 | enum tomoyo_securityfs_interface_index { |
124 | TOMOYO_DOMAINPOLICY, | 173 | TOMOYO_DOMAINPOLICY, |
125 | TOMOYO_EXCEPTIONPOLICY, | 174 | TOMOYO_EXCEPTIONPOLICY, |
@@ -129,20 +178,109 @@ enum tomoyo_securityfs_interface_index { | |||
129 | TOMOYO_SELFDOMAIN, | 178 | TOMOYO_SELFDOMAIN, |
130 | TOMOYO_VERSION, | 179 | TOMOYO_VERSION, |
131 | TOMOYO_PROFILE, | 180 | TOMOYO_PROFILE, |
181 | TOMOYO_QUERY, | ||
132 | TOMOYO_MANAGER | 182 | TOMOYO_MANAGER |
133 | }; | 183 | }; |
134 | 184 | ||
185 | enum tomoyo_mac_index { | ||
186 | TOMOYO_MAC_FILE_EXECUTE, | ||
187 | TOMOYO_MAC_FILE_OPEN, | ||
188 | TOMOYO_MAC_FILE_CREATE, | ||
189 | TOMOYO_MAC_FILE_UNLINK, | ||
190 | TOMOYO_MAC_FILE_MKDIR, | ||
191 | TOMOYO_MAC_FILE_RMDIR, | ||
192 | TOMOYO_MAC_FILE_MKFIFO, | ||
193 | TOMOYO_MAC_FILE_MKSOCK, | ||
194 | TOMOYO_MAC_FILE_TRUNCATE, | ||
195 | TOMOYO_MAC_FILE_SYMLINK, | ||
196 | TOMOYO_MAC_FILE_REWRITE, | ||
197 | TOMOYO_MAC_FILE_MKBLOCK, | ||
198 | TOMOYO_MAC_FILE_MKCHAR, | ||
199 | TOMOYO_MAC_FILE_LINK, | ||
200 | TOMOYO_MAC_FILE_RENAME, | ||
201 | TOMOYO_MAC_FILE_CHMOD, | ||
202 | TOMOYO_MAC_FILE_CHOWN, | ||
203 | TOMOYO_MAC_FILE_CHGRP, | ||
204 | TOMOYO_MAC_FILE_IOCTL, | ||
205 | TOMOYO_MAC_FILE_CHROOT, | ||
206 | TOMOYO_MAC_FILE_MOUNT, | ||
207 | TOMOYO_MAC_FILE_UMOUNT, | ||
208 | TOMOYO_MAC_FILE_PIVOT_ROOT, | ||
209 | TOMOYO_MAX_MAC_INDEX | ||
210 | }; | ||
211 | |||
212 | enum tomoyo_mac_category_index { | ||
213 | TOMOYO_MAC_CATEGORY_FILE, | ||
214 | TOMOYO_MAX_MAC_CATEGORY_INDEX | ||
215 | }; | ||
216 | |||
217 | #define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ | ||
218 | |||
135 | /********** Structure definitions. **********/ | 219 | /********** Structure definitions. **********/ |
136 | 220 | ||
137 | /* | 221 | /* |
138 | * tomoyo_page_buffer is a structure which is used for holding a pathname | 222 | * tomoyo_acl_head is a structure which is used for holding elements not in |
139 | * obtained from "struct dentry" and "struct vfsmount" pair. | 223 | * domain policy. |
140 | * As of now, it is 4096 bytes. If users complain that 4096 bytes is too small | 224 | * It has following fields. |
141 | * (because TOMOYO escapes non ASCII printable characters using \ooo format), | 225 | * |
142 | * we will make the buffer larger. | 226 | * (1) "list" which is linked to tomoyo_policy_list[] . |
227 | * (2) "is_deleted" is a bool which is true if marked as deleted, false | ||
228 | * otherwise. | ||
143 | */ | 229 | */ |
144 | struct tomoyo_page_buffer { | 230 | struct tomoyo_acl_head { |
145 | char buffer[4096]; | 231 | struct list_head list; |
232 | bool is_deleted; | ||
233 | } __packed; | ||
234 | |||
235 | /* | ||
236 | * tomoyo_request_info is a structure which is used for holding | ||
237 | * | ||
238 | * (1) Domain information of current process. | ||
239 | * (2) How many retries are made for this request. | ||
240 | * (3) Profile number used for this request. | ||
241 | * (4) Access control mode of the profile. | ||
242 | */ | ||
243 | struct tomoyo_request_info { | ||
244 | struct tomoyo_domain_info *domain; | ||
245 | /* For holding parameters. */ | ||
246 | union { | ||
247 | struct { | ||
248 | const struct tomoyo_path_info *filename; | ||
249 | /* For using wildcards at tomoyo_find_next_domain(). */ | ||
250 | const struct tomoyo_path_info *matched_path; | ||
251 | u8 operation; | ||
252 | } path; | ||
253 | struct { | ||
254 | const struct tomoyo_path_info *filename1; | ||
255 | const struct tomoyo_path_info *filename2; | ||
256 | u8 operation; | ||
257 | } path2; | ||
258 | struct { | ||
259 | const struct tomoyo_path_info *filename; | ||
260 | unsigned int mode; | ||
261 | unsigned int major; | ||
262 | unsigned int minor; | ||
263 | u8 operation; | ||
264 | } mkdev; | ||
265 | struct { | ||
266 | const struct tomoyo_path_info *filename; | ||
267 | unsigned long number; | ||
268 | u8 operation; | ||
269 | } path_number; | ||
270 | struct { | ||
271 | const struct tomoyo_path_info *type; | ||
272 | const struct tomoyo_path_info *dir; | ||
273 | const struct tomoyo_path_info *dev; | ||
274 | unsigned long flags; | ||
275 | int need_dev; | ||
276 | } mount; | ||
277 | } param; | ||
278 | u8 param_type; | ||
279 | bool granted; | ||
280 | u8 retry; | ||
281 | u8 profile; | ||
282 | u8 mode; /* One of tomoyo_mode_index . */ | ||
283 | u8 type; | ||
146 | }; | 284 | }; |
147 | 285 | ||
148 | /* | 286 | /* |
@@ -174,45 +312,31 @@ struct tomoyo_path_info { | |||
174 | }; | 312 | }; |
175 | 313 | ||
176 | /* | 314 | /* |
177 | * tomoyo_name_entry is a structure which is used for linking | 315 | * tomoyo_name is a structure which is used for linking |
178 | * "struct tomoyo_path_info" into tomoyo_name_list . | 316 | * "struct tomoyo_path_info" into tomoyo_name_list . |
179 | */ | 317 | */ |
180 | struct tomoyo_name_entry { | 318 | struct tomoyo_name { |
181 | struct list_head list; | 319 | struct list_head list; |
182 | atomic_t users; | 320 | atomic_t users; |
183 | struct tomoyo_path_info entry; | 321 | struct tomoyo_path_info entry; |
184 | }; | 322 | }; |
185 | 323 | ||
186 | /* | ||
187 | * tomoyo_path_info_with_data is a structure which is used for holding a | ||
188 | * pathname obtained from "struct dentry" and "struct vfsmount" pair. | ||
189 | * | ||
190 | * "struct tomoyo_path_info_with_data" consists of "struct tomoyo_path_info" | ||
191 | * and buffer for the pathname, while "struct tomoyo_page_buffer" consists of | ||
192 | * buffer for the pathname only. | ||
193 | * | ||
194 | * "struct tomoyo_path_info_with_data" is intended to allow TOMOYO to release | ||
195 | * both "struct tomoyo_path_info" and buffer for the pathname by single kfree() | ||
196 | * so that we don't need to return two pointers to the caller. If the caller | ||
197 | * puts "struct tomoyo_path_info" on stack memory, we will be able to remove | ||
198 | * "struct tomoyo_path_info_with_data". | ||
199 | */ | ||
200 | struct tomoyo_path_info_with_data { | ||
201 | /* Keep "head" first, for this pointer is passed to kfree(). */ | ||
202 | struct tomoyo_path_info head; | ||
203 | char barrier1[16]; /* Safeguard for overrun. */ | ||
204 | char body[TOMOYO_MAX_PATHNAME_LEN]; | ||
205 | char barrier2[16]; /* Safeguard for overrun. */ | ||
206 | }; | ||
207 | |||
208 | struct tomoyo_name_union { | 324 | struct tomoyo_name_union { |
209 | const struct tomoyo_path_info *filename; | 325 | const struct tomoyo_path_info *filename; |
210 | struct tomoyo_path_group *group; | 326 | struct tomoyo_group *group; |
211 | u8 is_group; | 327 | u8 is_group; |
212 | }; | 328 | }; |
213 | 329 | ||
214 | /* Structure for "path_group" directive. */ | 330 | struct tomoyo_number_union { |
215 | struct tomoyo_path_group { | 331 | unsigned long values[2]; |
332 | struct tomoyo_group *group; | ||
333 | u8 min_type; | ||
334 | u8 max_type; | ||
335 | u8 is_group; | ||
336 | }; | ||
337 | |||
338 | /* Structure for "path_group"/"number_group" directive. */ | ||
339 | struct tomoyo_group { | ||
216 | struct list_head list; | 340 | struct list_head list; |
217 | const struct tomoyo_path_info *group_name; | 341 | const struct tomoyo_path_info *group_name; |
218 | struct list_head member_list; | 342 | struct list_head member_list; |
@@ -220,28 +344,35 @@ struct tomoyo_path_group { | |||
220 | }; | 344 | }; |
221 | 345 | ||
222 | /* Structure for "path_group" directive. */ | 346 | /* Structure for "path_group" directive. */ |
223 | struct tomoyo_path_group_member { | 347 | struct tomoyo_path_group { |
224 | struct list_head list; | 348 | struct tomoyo_acl_head head; |
225 | bool is_deleted; | ||
226 | const struct tomoyo_path_info *member_name; | 349 | const struct tomoyo_path_info *member_name; |
227 | }; | 350 | }; |
228 | 351 | ||
352 | /* Structure for "number_group" directive. */ | ||
353 | struct tomoyo_number_group { | ||
354 | struct tomoyo_acl_head head; | ||
355 | struct tomoyo_number_union number; | ||
356 | }; | ||
357 | |||
229 | /* | 358 | /* |
230 | * tomoyo_acl_info is a structure which is used for holding | 359 | * tomoyo_acl_info is a structure which is used for holding |
231 | * | 360 | * |
232 | * (1) "list" which is linked to the ->acl_info_list of | 361 | * (1) "list" which is linked to the ->acl_info_list of |
233 | * "struct tomoyo_domain_info" | 362 | * "struct tomoyo_domain_info" |
234 | * (2) "type" which tells type of the entry (either | 363 | * (2) "is_deleted" is a bool which is true if this domain is marked as |
235 | * "struct tomoyo_path_acl" or "struct tomoyo_path2_acl"). | 364 | * "deleted", false otherwise. |
365 | * (3) "type" which tells type of the entry. | ||
236 | * | 366 | * |
237 | * Packing "struct tomoyo_acl_info" allows | 367 | * Packing "struct tomoyo_acl_info" allows |
238 | * "struct tomoyo_path_acl" to embed "u8" + "u16" and | 368 | * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" |
239 | * "struct tomoyo_path2_acl" to embed "u8" | 369 | * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed |
240 | * without enlarging their structure size. | 370 | * "u8" without enlarging their structure size. |
241 | */ | 371 | */ |
242 | struct tomoyo_acl_info { | 372 | struct tomoyo_acl_info { |
243 | struct list_head list; | 373 | struct list_head list; |
244 | u8 type; | 374 | bool is_deleted; |
375 | u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */ | ||
245 | } __packed; | 376 | } __packed; |
246 | 377 | ||
247 | /* | 378 | /* |
@@ -299,20 +430,62 @@ struct tomoyo_domain_info { | |||
299 | * (3) "name" is the pathname. | 430 | * (3) "name" is the pathname. |
300 | * | 431 | * |
301 | * Directives held by this structure are "allow_read/write", "allow_execute", | 432 | * Directives held by this structure are "allow_read/write", "allow_execute", |
302 | * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", | 433 | * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", |
303 | * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", | 434 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and |
304 | * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", | 435 | * "allow_unmount". |
305 | * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount" | ||
306 | * and "allow_unmount". | ||
307 | */ | 436 | */ |
308 | struct tomoyo_path_acl { | 437 | struct tomoyo_path_acl { |
309 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ | 438 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ |
310 | u8 perm_high; | ||
311 | u16 perm; | 439 | u16 perm; |
312 | struct tomoyo_name_union name; | 440 | struct tomoyo_name_union name; |
313 | }; | 441 | }; |
314 | 442 | ||
315 | /* | 443 | /* |
444 | * tomoyo_path_number_acl is a structure which is used for holding an | ||
445 | * entry with one pathname and one number operation. | ||
446 | * It has following fields. | ||
447 | * | ||
448 | * (1) "head" which is a "struct tomoyo_acl_info". | ||
449 | * (2) "perm" which is a bitmask of permitted operations. | ||
450 | * (3) "name" is the pathname. | ||
451 | * (4) "number" is the numeric value. | ||
452 | * | ||
453 | * Directives held by this structure are "allow_create", "allow_mkdir", | ||
454 | * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown" | ||
455 | * and "allow_chgrp". | ||
456 | * | ||
457 | */ | ||
458 | struct tomoyo_path_number_acl { | ||
459 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */ | ||
460 | u8 perm; | ||
461 | struct tomoyo_name_union name; | ||
462 | struct tomoyo_number_union number; | ||
463 | }; | ||
464 | |||
465 | /* | ||
466 | * tomoyo_mkdev_acl is a structure which is used for holding an | ||
467 | * entry with one pathname and three numbers operation. | ||
468 | * It has following fields. | ||
469 | * | ||
470 | * (1) "head" which is a "struct tomoyo_acl_info". | ||
471 | * (2) "perm" which is a bitmask of permitted operations. | ||
472 | * (3) "mode" is the create mode. | ||
473 | * (4) "major" is the major number of device node. | ||
474 | * (5) "minor" is the minor number of device node. | ||
475 | * | ||
476 | * Directives held by this structure are "allow_mkchar", "allow_mkblock". | ||
477 | * | ||
478 | */ | ||
479 | struct tomoyo_mkdev_acl { | ||
480 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */ | ||
481 | u8 perm; | ||
482 | struct tomoyo_name_union name; | ||
483 | struct tomoyo_number_union mode; | ||
484 | struct tomoyo_number_union major; | ||
485 | struct tomoyo_number_union minor; | ||
486 | }; | ||
487 | |||
488 | /* | ||
316 | * tomoyo_path2_acl is a structure which is used for holding an | 489 | * tomoyo_path2_acl is a structure which is used for holding an |
317 | * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). | 490 | * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). |
318 | * It has following fields. | 491 | * It has following fields. |
@@ -333,53 +506,61 @@ struct tomoyo_path2_acl { | |||
333 | }; | 506 | }; |
334 | 507 | ||
335 | /* | 508 | /* |
336 | * tomoyo_io_buffer is a structure which is used for reading and modifying | 509 | * tomoyo_mount_acl is a structure which is used for holding an |
337 | * configuration via /sys/kernel/security/tomoyo/ interface. | 510 | * entry for mount operation. |
338 | * It has many fields. ->read_var1 , ->read_var2 , ->write_var1 are used as | 511 | * It has following fields. |
339 | * cursors. | ||
340 | * | 512 | * |
341 | * Since the content of /sys/kernel/security/tomoyo/domain_policy is a list of | 513 | * (1) "head" which is a "struct tomoyo_acl_info". |
342 | * "struct tomoyo_domain_info" entries and each "struct tomoyo_domain_info" | 514 | * (2) "dev_name" is the device name. |
343 | * entry has a list of "struct tomoyo_acl_info", we need two cursors when | 515 | * (3) "dir_name" is the mount point. |
344 | * reading (one is for traversing tomoyo_domain_list and the other is for | 516 | * (4) "fs_type" is the filesystem type. |
345 | * traversing "struct tomoyo_acl_info"->acl_info_list ). | 517 | * (5) "flags" is the mount flags. |
346 | * | 518 | * |
347 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 519 | * Directive held by this structure is "allow_mount". |
348 | * "select ", TOMOYO seeks the cursor ->read_var1 and ->write_var1 to the | 520 | */ |
349 | * domain with the domainname specified by the rest of that line (NULL is set | 521 | struct tomoyo_mount_acl { |
350 | * if seek failed). | 522 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */ |
351 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 523 | struct tomoyo_name_union dev_name; |
352 | * "delete ", TOMOYO deletes an entry or a domain specified by the rest of that | 524 | struct tomoyo_name_union dir_name; |
353 | * line (->write_var1 is set to NULL if a domain was deleted). | 525 | struct tomoyo_name_union fs_type; |
354 | * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with | 526 | struct tomoyo_number_union flags; |
355 | * neither "select " nor "delete ", an entry or a domain specified by that line | 527 | }; |
356 | * is appended. | 528 | |
529 | #define TOMOYO_MAX_IO_READ_QUEUE 32 | ||
530 | |||
531 | /* | ||
532 | * Structure for reading/writing policy via /sys/kernel/security/tomoyo | ||
533 | * interfaces. | ||
357 | */ | 534 | */ |
358 | struct tomoyo_io_buffer { | 535 | struct tomoyo_io_buffer { |
359 | int (*read) (struct tomoyo_io_buffer *); | 536 | void (*read) (struct tomoyo_io_buffer *); |
360 | int (*write) (struct tomoyo_io_buffer *); | 537 | int (*write) (struct tomoyo_io_buffer *); |
538 | int (*poll) (struct file *file, poll_table *wait); | ||
361 | /* Exclusive lock for this structure. */ | 539 | /* Exclusive lock for this structure. */ |
362 | struct mutex io_sem; | 540 | struct mutex io_sem; |
363 | /* Index returned by tomoyo_read_lock(). */ | 541 | /* Index returned by tomoyo_read_lock(). */ |
364 | int reader_idx; | 542 | int reader_idx; |
365 | /* The position currently reading from. */ | 543 | char __user *read_user_buf; |
366 | struct list_head *read_var1; | 544 | int read_user_buf_avail; |
367 | /* Extra variables for reading. */ | 545 | struct { |
368 | struct list_head *read_var2; | 546 | struct list_head *domain; |
547 | struct list_head *group; | ||
548 | struct list_head *acl; | ||
549 | int avail; | ||
550 | int step; | ||
551 | int query_index; | ||
552 | u16 index; | ||
553 | u8 bit; | ||
554 | u8 w_pos; | ||
555 | bool eof; | ||
556 | bool print_this_domain_only; | ||
557 | bool print_execute_only; | ||
558 | const char *w[TOMOYO_MAX_IO_READ_QUEUE]; | ||
559 | } r; | ||
369 | /* The position currently writing to. */ | 560 | /* The position currently writing to. */ |
370 | struct tomoyo_domain_info *write_var1; | 561 | struct tomoyo_domain_info *write_var1; |
371 | /* The step for reading. */ | ||
372 | int read_step; | ||
373 | /* Buffer for reading. */ | 562 | /* Buffer for reading. */ |
374 | char *read_buf; | 563 | char *read_buf; |
375 | /* EOF flag for reading. */ | ||
376 | bool read_eof; | ||
377 | /* Read domain ACL of specified PID? */ | ||
378 | bool read_single_domain; | ||
379 | /* Extra variable for reading. */ | ||
380 | u8 read_bit; | ||
381 | /* Bytes available for reading. */ | ||
382 | int read_avail; | ||
383 | /* Size of read buffer. */ | 564 | /* Size of read buffer. */ |
384 | int readbuf_size; | 565 | int readbuf_size; |
385 | /* Buffer for writing. */ | 566 | /* Buffer for writing. */ |
@@ -388,215 +569,203 @@ struct tomoyo_io_buffer { | |||
388 | int write_avail; | 569 | int write_avail; |
389 | /* Size of write buffer. */ | 570 | /* Size of write buffer. */ |
390 | int writebuf_size; | 571 | int writebuf_size; |
572 | /* Type of this interface. */ | ||
573 | u8 type; | ||
391 | }; | 574 | }; |
392 | 575 | ||
393 | /* | 576 | /* |
394 | * tomoyo_globally_readable_file_entry is a structure which is used for holding | 577 | * tomoyo_readable_file is a structure which is used for holding |
395 | * "allow_read" entries. | 578 | * "allow_read" entries. |
396 | * It has following fields. | 579 | * It has following fields. |
397 | * | 580 | * |
398 | * (1) "list" which is linked to tomoyo_globally_readable_list . | 581 | * (1) "head" is "struct tomoyo_acl_head". |
399 | * (2) "filename" is a pathname which is allowed to open(O_RDONLY). | 582 | * (2) "filename" is a pathname which is allowed to open(O_RDONLY). |
400 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
401 | * otherwise. | ||
402 | */ | 583 | */ |
403 | struct tomoyo_globally_readable_file_entry { | 584 | struct tomoyo_readable_file { |
404 | struct list_head list; | 585 | struct tomoyo_acl_head head; |
405 | const struct tomoyo_path_info *filename; | 586 | const struct tomoyo_path_info *filename; |
406 | bool is_deleted; | ||
407 | }; | 587 | }; |
408 | 588 | ||
409 | /* | 589 | /* |
410 | * tomoyo_pattern_entry is a structure which is used for holding | 590 | * tomoyo_no_pattern is a structure which is used for holding |
411 | * "tomoyo_pattern_list" entries. | 591 | * "file_pattern" entries. |
412 | * It has following fields. | 592 | * It has following fields. |
413 | * | 593 | * |
414 | * (1) "list" which is linked to tomoyo_pattern_list . | 594 | * (1) "head" is "struct tomoyo_acl_head". |
415 | * (2) "pattern" is a pathname pattern which is used for converting pathnames | 595 | * (2) "pattern" is a pathname pattern which is used for converting pathnames |
416 | * to pathname patterns during learning mode. | 596 | * to pathname patterns during learning mode. |
417 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
418 | * otherwise. | ||
419 | */ | 597 | */ |
420 | struct tomoyo_pattern_entry { | 598 | struct tomoyo_no_pattern { |
421 | struct list_head list; | 599 | struct tomoyo_acl_head head; |
422 | const struct tomoyo_path_info *pattern; | 600 | const struct tomoyo_path_info *pattern; |
423 | bool is_deleted; | ||
424 | }; | 601 | }; |
425 | 602 | ||
426 | /* | 603 | /* |
427 | * tomoyo_no_rewrite_entry is a structure which is used for holding | 604 | * tomoyo_no_rewrite is a structure which is used for holding |
428 | * "deny_rewrite" entries. | 605 | * "deny_rewrite" entries. |
429 | * It has following fields. | 606 | * It has following fields. |
430 | * | 607 | * |
431 | * (1) "list" which is linked to tomoyo_no_rewrite_list . | 608 | * (1) "head" is "struct tomoyo_acl_head". |
432 | * (2) "pattern" is a pathname which is by default not permitted to modify | 609 | * (2) "pattern" is a pathname which is by default not permitted to modify |
433 | * already existing content. | 610 | * already existing content. |
434 | * (3) "is_deleted" is a bool which is true if marked as deleted, false | ||
435 | * otherwise. | ||
436 | */ | 611 | */ |
437 | struct tomoyo_no_rewrite_entry { | 612 | struct tomoyo_no_rewrite { |
438 | struct list_head list; | 613 | struct tomoyo_acl_head head; |
439 | const struct tomoyo_path_info *pattern; | 614 | const struct tomoyo_path_info *pattern; |
440 | bool is_deleted; | ||
441 | }; | 615 | }; |
442 | 616 | ||
443 | /* | 617 | /* |
444 | * tomoyo_domain_initializer_entry is a structure which is used for holding | 618 | * tomoyo_transition_control is a structure which is used for holding |
445 | * "initialize_domain" and "no_initialize_domain" entries. | 619 | * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" |
620 | * entries. | ||
446 | * It has following fields. | 621 | * It has following fields. |
447 | * | 622 | * |
448 | * (1) "list" which is linked to tomoyo_domain_initializer_list . | 623 | * (1) "head" is "struct tomoyo_acl_head". |
449 | * (2) "domainname" which is "a domainname" or "the last component of a | 624 | * (2) "type" is type of this entry. |
450 | * domainname". This field is NULL if "from" clause is not specified. | 625 | * (3) "is_last_name" is a bool which is true if "domainname" is "the last |
451 | * (3) "program" which is a program's pathname. | ||
452 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
453 | * otherwise. | ||
454 | * (5) "is_not" is a bool which is true if "no_initialize_domain", false | ||
455 | * otherwise. | ||
456 | * (6) "is_last_name" is a bool which is true if "domainname" is "the last | ||
457 | * component of a domainname", false otherwise. | 626 | * component of a domainname", false otherwise. |
458 | */ | 627 | * (4) "domainname" which is "a domainname" or "the last component of a |
459 | struct tomoyo_domain_initializer_entry { | ||
460 | struct list_head list; | ||
461 | const struct tomoyo_path_info *domainname; /* This may be NULL */ | ||
462 | const struct tomoyo_path_info *program; | ||
463 | bool is_deleted; | ||
464 | bool is_not; /* True if this entry is "no_initialize_domain". */ | ||
465 | /* True if the domainname is tomoyo_get_last_name(). */ | ||
466 | bool is_last_name; | ||
467 | }; | ||
468 | |||
469 | /* | ||
470 | * tomoyo_domain_keeper_entry is a structure which is used for holding | ||
471 | * "keep_domain" and "no_keep_domain" entries. | ||
472 | * It has following fields. | ||
473 | * | ||
474 | * (1) "list" which is linked to tomoyo_domain_keeper_list . | ||
475 | * (2) "domainname" which is "a domainname" or "the last component of a | ||
476 | * domainname". | 628 | * domainname". |
477 | * (3) "program" which is a program's pathname. | 629 | * (5) "program" which is a program's pathname. |
478 | * This field is NULL if "from" clause is not specified. | ||
479 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
480 | * otherwise. | ||
481 | * (5) "is_not" is a bool which is true if "no_initialize_domain", false | ||
482 | * otherwise. | ||
483 | * (6) "is_last_name" is a bool which is true if "domainname" is "the last | ||
484 | * component of a domainname", false otherwise. | ||
485 | */ | 630 | */ |
486 | struct tomoyo_domain_keeper_entry { | 631 | struct tomoyo_transition_control { |
487 | struct list_head list; | 632 | struct tomoyo_acl_head head; |
488 | const struct tomoyo_path_info *domainname; | 633 | u8 type; /* One of values in "enum tomoyo_transition_type". */ |
489 | const struct tomoyo_path_info *program; /* This may be NULL */ | ||
490 | bool is_deleted; | ||
491 | bool is_not; /* True if this entry is "no_keep_domain". */ | ||
492 | /* True if the domainname is tomoyo_get_last_name(). */ | 634 | /* True if the domainname is tomoyo_get_last_name(). */ |
493 | bool is_last_name; | 635 | bool is_last_name; |
636 | const struct tomoyo_path_info *domainname; /* Maybe NULL */ | ||
637 | const struct tomoyo_path_info *program; /* Maybe NULL */ | ||
494 | }; | 638 | }; |
495 | 639 | ||
496 | /* | 640 | /* |
497 | * tomoyo_alias_entry is a structure which is used for holding "alias" entries. | 641 | * tomoyo_aggregator is a structure which is used for holding |
642 | * "aggregator" entries. | ||
498 | * It has following fields. | 643 | * It has following fields. |
499 | * | 644 | * |
500 | * (1) "list" which is linked to tomoyo_alias_list . | 645 | * (1) "head" is "struct tomoyo_acl_head". |
501 | * (2) "original_name" which is a dereferenced pathname. | 646 | * (2) "original_name" which is originally requested name. |
502 | * (3) "aliased_name" which is a symlink's pathname. | 647 | * (3) "aggregated_name" which is name to rewrite. |
503 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
504 | * otherwise. | ||
505 | */ | 648 | */ |
506 | struct tomoyo_alias_entry { | 649 | struct tomoyo_aggregator { |
507 | struct list_head list; | 650 | struct tomoyo_acl_head head; |
508 | const struct tomoyo_path_info *original_name; | 651 | const struct tomoyo_path_info *original_name; |
509 | const struct tomoyo_path_info *aliased_name; | 652 | const struct tomoyo_path_info *aggregated_name; |
510 | bool is_deleted; | ||
511 | }; | 653 | }; |
512 | 654 | ||
513 | /* | 655 | /* |
514 | * tomoyo_policy_manager_entry is a structure which is used for holding list of | 656 | * tomoyo_manager is a structure which is used for holding list of |
515 | * domainnames or programs which are permitted to modify configuration via | 657 | * domainnames or programs which are permitted to modify configuration via |
516 | * /sys/kernel/security/tomoyo/ interface. | 658 | * /sys/kernel/security/tomoyo/ interface. |
517 | * It has following fields. | 659 | * It has following fields. |
518 | * | 660 | * |
519 | * (1) "list" which is linked to tomoyo_policy_manager_list . | 661 | * (1) "head" is "struct tomoyo_acl_head". |
520 | * (2) "manager" is a domainname or a program's pathname. | 662 | * (2) "is_domain" is a bool which is true if "manager" is a domainname, false |
521 | * (3) "is_domain" is a bool which is true if "manager" is a domainname, false | ||
522 | * otherwise. | ||
523 | * (4) "is_deleted" is a bool which is true if marked as deleted, false | ||
524 | * otherwise. | 663 | * otherwise. |
664 | * (3) "manager" is a domainname or a program's pathname. | ||
525 | */ | 665 | */ |
526 | struct tomoyo_policy_manager_entry { | 666 | struct tomoyo_manager { |
527 | struct list_head list; | 667 | struct tomoyo_acl_head head; |
668 | bool is_domain; /* True if manager is a domainname. */ | ||
528 | /* A path to program or a domainname. */ | 669 | /* A path to program or a domainname. */ |
529 | const struct tomoyo_path_info *manager; | 670 | const struct tomoyo_path_info *manager; |
530 | bool is_domain; /* True if manager is a domainname. */ | 671 | }; |
531 | bool is_deleted; /* True if this entry is deleted. */ | 672 | |
673 | struct tomoyo_preference { | ||
674 | unsigned int learning_max_entry; | ||
675 | bool enforcing_verbose; | ||
676 | bool learning_verbose; | ||
677 | bool permissive_verbose; | ||
678 | }; | ||
679 | |||
680 | struct tomoyo_profile { | ||
681 | const struct tomoyo_path_info *comment; | ||
682 | struct tomoyo_preference *learning; | ||
683 | struct tomoyo_preference *permissive; | ||
684 | struct tomoyo_preference *enforcing; | ||
685 | struct tomoyo_preference preference; | ||
686 | u8 default_config; | ||
687 | u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; | ||
532 | }; | 688 | }; |
533 | 689 | ||
534 | /********** Function prototypes. **********/ | 690 | /********** Function prototypes. **********/ |
535 | 691 | ||
536 | /* Check whether the given name matches the given name_union. */ | 692 | extern asmlinkage long sys_getpid(void); |
537 | bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, | 693 | extern asmlinkage long sys_getppid(void); |
538 | const struct tomoyo_name_union *ptr); | 694 | |
695 | /* Check whether the given string starts with the given keyword. */ | ||
696 | bool tomoyo_str_starts(char **src, const char *find); | ||
697 | /* Get tomoyo_realpath() of current process. */ | ||
698 | const char *tomoyo_get_exe(void); | ||
699 | /* Format string. */ | ||
700 | void tomoyo_normalize_line(unsigned char *buffer); | ||
701 | /* Print warning or error message on console. */ | ||
702 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
703 | __attribute__ ((format(printf, 2, 3))); | ||
704 | /* Check all profiles currently assigned to domains are defined. */ | ||
705 | void tomoyo_check_profile(void); | ||
706 | /* Open operation for /sys/kernel/security/tomoyo/ interface. */ | ||
707 | int tomoyo_open_control(const u8 type, struct file *file); | ||
708 | /* Close /sys/kernel/security/tomoyo/ interface. */ | ||
709 | int tomoyo_close_control(struct file *file); | ||
710 | /* Poll operation for /sys/kernel/security/tomoyo/ interface. */ | ||
711 | int tomoyo_poll_control(struct file *file, poll_table *wait); | ||
712 | /* Read operation for /sys/kernel/security/tomoyo/ interface. */ | ||
713 | int tomoyo_read_control(struct file *file, char __user *buffer, | ||
714 | const int buffer_len); | ||
715 | /* Write operation for /sys/kernel/security/tomoyo/ interface. */ | ||
716 | int tomoyo_write_control(struct file *file, const char __user *buffer, | ||
717 | const int buffer_len); | ||
539 | /* Check whether the domain has too many ACL entries to hold. */ | 718 | /* Check whether the domain has too many ACL entries to hold. */ |
540 | bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain); | 719 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); |
541 | /* Transactional sprintf() for policy dump. */ | 720 | /* Print out of memory warning message. */ |
542 | bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | 721 | void tomoyo_warn_oom(const char *function); |
722 | /* Check whether the given name matches the given name_union. */ | ||
723 | const struct tomoyo_path_info * | ||
724 | tomoyo_compare_name_union(const struct tomoyo_path_info *name, | ||
725 | const struct tomoyo_name_union *ptr); | ||
726 | /* Check whether the given number matches the given number_union. */ | ||
727 | bool tomoyo_compare_number_union(const unsigned long value, | ||
728 | const struct tomoyo_number_union *ptr); | ||
729 | int tomoyo_get_mode(const u8 profile, const u8 index); | ||
730 | void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) | ||
543 | __attribute__ ((format(printf, 2, 3))); | 731 | __attribute__ ((format(printf, 2, 3))); |
544 | /* Check whether the domainname is correct. */ | 732 | /* Check whether the domainname is correct. */ |
545 | bool tomoyo_is_correct_domain(const unsigned char *domainname); | 733 | bool tomoyo_correct_domain(const unsigned char *domainname); |
546 | /* Check whether the token is correct. */ | 734 | /* Check whether the token is correct. */ |
547 | bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | 735 | bool tomoyo_correct_path(const char *filename); |
548 | const s8 pattern_type, const s8 end_type); | 736 | bool tomoyo_correct_word(const char *string); |
549 | /* Check whether the token can be a domainname. */ | 737 | /* Check whether the token can be a domainname. */ |
550 | bool tomoyo_is_domain_def(const unsigned char *buffer); | 738 | bool tomoyo_domain_def(const unsigned char *buffer); |
551 | bool tomoyo_parse_name_union(const char *filename, | 739 | bool tomoyo_parse_name_union(const char *filename, |
552 | struct tomoyo_name_union *ptr); | 740 | struct tomoyo_name_union *ptr); |
553 | /* Check whether the given filename matches the given path_group. */ | 741 | /* Check whether the given filename matches the given path_group. */ |
554 | bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | 742 | const struct tomoyo_path_info * |
555 | const struct tomoyo_path_group *group, | 743 | tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, |
556 | const bool may_use_pattern); | 744 | const struct tomoyo_group *group); |
745 | /* Check whether the given value matches the given number_group. */ | ||
746 | bool tomoyo_number_matches_group(const unsigned long min, | ||
747 | const unsigned long max, | ||
748 | const struct tomoyo_group *group); | ||
557 | /* Check whether the given filename matches the given pattern. */ | 749 | /* Check whether the given filename matches the given pattern. */ |
558 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | 750 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, |
559 | const struct tomoyo_path_info *pattern); | 751 | const struct tomoyo_path_info *pattern); |
560 | /* Read "alias" entry in exception policy. */ | 752 | |
561 | bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); | 753 | bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); |
562 | /* | ||
563 | * Read "initialize_domain" and "no_initialize_domain" entry | ||
564 | * in exception policy. | ||
565 | */ | ||
566 | bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head); | ||
567 | /* Read "keep_domain" and "no_keep_domain" entry in exception policy. */ | ||
568 | bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head); | ||
569 | /* Read "file_pattern" entry in exception policy. */ | ||
570 | bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head); | ||
571 | /* Read "path_group" entry in exception policy. */ | ||
572 | bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head); | ||
573 | /* Read "allow_read" entry in exception policy. */ | ||
574 | bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head); | ||
575 | /* Read "deny_rewrite" entry in exception policy. */ | ||
576 | bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); | ||
577 | /* Tokenize a line. */ | 754 | /* Tokenize a line. */ |
578 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size); | 755 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size); |
579 | /* Write domain policy violation warning message to console? */ | 756 | /* Write domain policy violation warning message to console? */ |
580 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); | 757 | bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); |
581 | /* Convert double path operation to operation name. */ | 758 | /* Fill "struct tomoyo_request_info". */ |
582 | const char *tomoyo_path22keyword(const u8 operation); | 759 | int tomoyo_init_request_info(struct tomoyo_request_info *r, |
583 | /* Get the last component of the given domainname. */ | 760 | struct tomoyo_domain_info *domain, |
584 | const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); | 761 | const u8 index); |
585 | /* Get warning message. */ | 762 | /* Check permission for mount operation. */ |
586 | const char *tomoyo_get_msg(const bool is_enforce); | 763 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, |
587 | /* Convert single path operation to operation name. */ | 764 | unsigned long flags, void *data_page); |
588 | const char *tomoyo_path2keyword(const u8 operation); | 765 | /* Create "aggregator" entry in exception policy. */ |
589 | /* Create "alias" entry in exception policy. */ | 766 | int tomoyo_write_aggregator(char *data, const bool is_delete); |
590 | int tomoyo_write_alias_policy(char *data, const bool is_delete); | 767 | int tomoyo_write_transition_control(char *data, const bool is_delete, |
591 | /* | 768 | const u8 type); |
592 | * Create "initialize_domain" and "no_initialize_domain" entry | ||
593 | * in exception policy. | ||
594 | */ | ||
595 | int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, | ||
596 | const bool is_delete); | ||
597 | /* Create "keep_domain" and "no_keep_domain" entry in exception policy. */ | ||
598 | int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | ||
599 | const bool is_delete); | ||
600 | /* | 769 | /* |
601 | * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", | 770 | * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", |
602 | * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", | 771 | * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", |
@@ -604,25 +773,31 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | |||
604 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and | 773 | * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and |
605 | * "allow_link" entry in domain policy. | 774 | * "allow_link" entry in domain policy. |
606 | */ | 775 | */ |
607 | int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | 776 | int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, |
608 | const bool is_delete); | 777 | const bool is_delete); |
609 | /* Create "allow_read" entry in exception policy. */ | 778 | /* Create "allow_read" entry in exception policy. */ |
610 | int tomoyo_write_globally_readable_policy(char *data, const bool is_delete); | 779 | int tomoyo_write_globally_readable(char *data, const bool is_delete); |
780 | /* Create "allow_mount" entry in domain policy. */ | ||
781 | int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, | ||
782 | const bool is_delete); | ||
611 | /* Create "deny_rewrite" entry in exception policy. */ | 783 | /* Create "deny_rewrite" entry in exception policy. */ |
612 | int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); | 784 | int tomoyo_write_no_rewrite(char *data, const bool is_delete); |
613 | /* Create "file_pattern" entry in exception policy. */ | 785 | /* Create "file_pattern" entry in exception policy. */ |
614 | int tomoyo_write_pattern_policy(char *data, const bool is_delete); | 786 | int tomoyo_write_pattern(char *data, const bool is_delete); |
615 | /* Create "path_group" entry in exception policy. */ | 787 | /* Create "path_group"/"number_group" entry in exception policy. */ |
616 | int tomoyo_write_path_group_policy(char *data, const bool is_delete); | 788 | int tomoyo_write_group(char *data, const bool is_delete, const u8 type); |
789 | int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) | ||
790 | __attribute__ ((format(printf, 2, 3))); | ||
617 | /* Find a domain by the given name. */ | 791 | /* Find a domain by the given name. */ |
618 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); | 792 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); |
619 | /* Find or create a domain by the given name. */ | 793 | /* Find or create a domain by the given name. */ |
620 | struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | 794 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, |
621 | domainname, | 795 | const u8 profile); |
622 | const u8 profile); | 796 | struct tomoyo_profile *tomoyo_profile(const u8 profile); |
623 | 797 | /* | |
624 | /* Allocate memory for "struct tomoyo_path_group". */ | 798 | * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". |
625 | struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); | 799 | */ |
800 | struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type); | ||
626 | 801 | ||
627 | /* Check mode for specified functionality. */ | 802 | /* Check mode for specified functionality. */ |
628 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, | 803 | unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, |
@@ -632,25 +807,23 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); | |||
632 | /* Run policy loader when /sbin/init starts. */ | 807 | /* Run policy loader when /sbin/init starts. */ |
633 | void tomoyo_load_policy(const char *filename); | 808 | void tomoyo_load_policy(const char *filename); |
634 | 809 | ||
635 | /* Convert binary string to ascii string. */ | 810 | void tomoyo_put_number_union(struct tomoyo_number_union *ptr); |
636 | int tomoyo_encode(char *buffer, int buflen, const char *str); | ||
637 | 811 | ||
638 | /* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ | 812 | /* Convert binary string to ascii string. */ |
639 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | 813 | char *tomoyo_encode(const char *str); |
640 | int newname_len); | ||
641 | 814 | ||
642 | /* | 815 | /* |
643 | * Returns realpath(3) of the given pathname but ignores chroot'ed root. | 816 | * Returns realpath(3) of the given pathname except that |
644 | * These functions use kzalloc(), so the caller must call kfree() | 817 | * ignores chroot'ed root and does not follow the final symlink. |
645 | * if these functions didn't return NULL. | ||
646 | */ | 818 | */ |
647 | char *tomoyo_realpath(const char *pathname); | 819 | char *tomoyo_realpath_nofollow(const char *pathname); |
648 | /* | 820 | /* |
649 | * Same with tomoyo_realpath() except that it doesn't follow the final symlink. | 821 | * Returns realpath(3) of the given pathname except that |
822 | * ignores chroot'ed root and the pathname is already solved. | ||
650 | */ | 823 | */ |
651 | char *tomoyo_realpath_nofollow(const char *pathname); | ||
652 | /* Same with tomoyo_realpath() except that the pathname is already solved. */ | ||
653 | char *tomoyo_realpath_from_path(struct path *path); | 824 | char *tomoyo_realpath_from_path(struct path *path); |
825 | /* Get patterned pathname. */ | ||
826 | const char *tomoyo_pattern(const struct tomoyo_path_info *filename); | ||
654 | 827 | ||
655 | /* Check memory quota. */ | 828 | /* Check memory quota. */ |
656 | bool tomoyo_memory_ok(void *ptr); | 829 | bool tomoyo_memory_ok(void *ptr); |
@@ -663,23 +836,29 @@ void *tomoyo_commit_ok(void *data, const unsigned int size); | |||
663 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); | 836 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); |
664 | 837 | ||
665 | /* Check for memory usage. */ | 838 | /* Check for memory usage. */ |
666 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); | 839 | void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); |
667 | 840 | ||
668 | /* Set memory quota. */ | 841 | /* Set memory quota. */ |
669 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); | 842 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); |
670 | 843 | ||
671 | /* Initialize realpath related code. */ | 844 | /* Initialize mm related code. */ |
672 | void __init tomoyo_realpath_init(void); | 845 | void __init tomoyo_mm_init(void); |
673 | int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, | 846 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
674 | const struct tomoyo_path_info *filename); | 847 | const struct tomoyo_path_info *filename); |
675 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | 848 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, |
676 | struct path *path, const int flag); | 849 | struct path *path, const int flag); |
850 | int tomoyo_path_number_perm(const u8 operation, struct path *path, | ||
851 | unsigned long number); | ||
852 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, | ||
853 | const unsigned int mode, unsigned int dev); | ||
677 | int tomoyo_path_perm(const u8 operation, struct path *path); | 854 | int tomoyo_path_perm(const u8 operation, struct path *path); |
678 | int tomoyo_path2_perm(const u8 operation, struct path *path1, | 855 | int tomoyo_path2_perm(const u8 operation, struct path *path1, |
679 | struct path *path2); | 856 | struct path *path2); |
680 | int tomoyo_check_rewrite_permission(struct file *filp); | ||
681 | int tomoyo_find_next_domain(struct linux_binprm *bprm); | 857 | int tomoyo_find_next_domain(struct linux_binprm *bprm); |
682 | 858 | ||
859 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | ||
860 | const unsigned long value, const u8 type); | ||
861 | |||
683 | /* Drop refcount on tomoyo_name_union. */ | 862 | /* Drop refcount on tomoyo_name_union. */ |
684 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); | 863 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr); |
685 | 864 | ||
@@ -688,6 +867,25 @@ void tomoyo_run_gc(void); | |||
688 | 867 | ||
689 | void tomoyo_memory_free(void *ptr); | 868 | void tomoyo_memory_free(void *ptr); |
690 | 869 | ||
870 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | ||
871 | bool is_delete, struct tomoyo_domain_info *domain, | ||
872 | bool (*check_duplicate) (const struct tomoyo_acl_info | ||
873 | *, | ||
874 | const struct tomoyo_acl_info | ||
875 | *), | ||
876 | bool (*merge_duplicate) (struct tomoyo_acl_info *, | ||
877 | struct tomoyo_acl_info *, | ||
878 | const bool)); | ||
879 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | ||
880 | bool is_delete, struct list_head *list, | ||
881 | bool (*check_duplicate) (const struct tomoyo_acl_head | ||
882 | *, | ||
883 | const struct tomoyo_acl_head | ||
884 | *)); | ||
885 | void tomoyo_check_acl(struct tomoyo_request_info *r, | ||
886 | bool (*check_entry) (struct tomoyo_request_info *, | ||
887 | const struct tomoyo_acl_info *)); | ||
888 | |||
691 | /********** External variable definitions. **********/ | 889 | /********** External variable definitions. **********/ |
692 | 890 | ||
693 | /* Lock for GC. */ | 891 | /* Lock for GC. */ |
@@ -696,14 +894,8 @@ extern struct srcu_struct tomoyo_ss; | |||
696 | /* The list for "struct tomoyo_domain_info". */ | 894 | /* The list for "struct tomoyo_domain_info". */ |
697 | extern struct list_head tomoyo_domain_list; | 895 | extern struct list_head tomoyo_domain_list; |
698 | 896 | ||
699 | extern struct list_head tomoyo_path_group_list; | 897 | extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; |
700 | extern struct list_head tomoyo_domain_initializer_list; | 898 | extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; |
701 | extern struct list_head tomoyo_domain_keeper_list; | ||
702 | extern struct list_head tomoyo_alias_list; | ||
703 | extern struct list_head tomoyo_globally_readable_list; | ||
704 | extern struct list_head tomoyo_pattern_list; | ||
705 | extern struct list_head tomoyo_no_rewrite_list; | ||
706 | extern struct list_head tomoyo_policy_manager_list; | ||
707 | extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | 899 | extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; |
708 | 900 | ||
709 | /* Lock for protecting policy. */ | 901 | /* Lock for protecting policy. */ |
@@ -715,6 +907,14 @@ extern bool tomoyo_policy_loaded; | |||
715 | /* The kernel's domain. */ | 907 | /* The kernel's domain. */ |
716 | extern struct tomoyo_domain_info tomoyo_kernel_domain; | 908 | extern struct tomoyo_domain_info tomoyo_kernel_domain; |
717 | 909 | ||
910 | extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; | ||
911 | extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; | ||
912 | extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION]; | ||
913 | extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION]; | ||
914 | |||
915 | extern unsigned int tomoyo_quota_for_query; | ||
916 | extern unsigned int tomoyo_query_memory_size; | ||
917 | |||
718 | /********** Inlined functions. **********/ | 918 | /********** Inlined functions. **********/ |
719 | 919 | ||
720 | static inline int tomoyo_read_lock(void) | 920 | static inline int tomoyo_read_lock(void) |
@@ -735,25 +935,25 @@ static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, | |||
735 | } | 935 | } |
736 | 936 | ||
737 | /** | 937 | /** |
738 | * tomoyo_is_valid - Check whether the character is a valid char. | 938 | * tomoyo_valid - Check whether the character is a valid char. |
739 | * | 939 | * |
740 | * @c: The character to check. | 940 | * @c: The character to check. |
741 | * | 941 | * |
742 | * Returns true if @c is a valid character, false otherwise. | 942 | * Returns true if @c is a valid character, false otherwise. |
743 | */ | 943 | */ |
744 | static inline bool tomoyo_is_valid(const unsigned char c) | 944 | static inline bool tomoyo_valid(const unsigned char c) |
745 | { | 945 | { |
746 | return c > ' ' && c < 127; | 946 | return c > ' ' && c < 127; |
747 | } | 947 | } |
748 | 948 | ||
749 | /** | 949 | /** |
750 | * tomoyo_is_invalid - Check whether the character is an invalid char. | 950 | * tomoyo_invalid - Check whether the character is an invalid char. |
751 | * | 951 | * |
752 | * @c: The character to check. | 952 | * @c: The character to check. |
753 | * | 953 | * |
754 | * Returns true if @c is an invalid character, false otherwise. | 954 | * Returns true if @c is an invalid character, false otherwise. |
755 | */ | 955 | */ |
756 | static inline bool tomoyo_is_invalid(const unsigned char c) | 956 | static inline bool tomoyo_invalid(const unsigned char c) |
757 | { | 957 | { |
758 | return c && (c <= ' ' || c >= 127); | 958 | return c && (c <= ' ' || c >= 127); |
759 | } | 959 | } |
@@ -761,13 +961,13 @@ static inline bool tomoyo_is_invalid(const unsigned char c) | |||
761 | static inline void tomoyo_put_name(const struct tomoyo_path_info *name) | 961 | static inline void tomoyo_put_name(const struct tomoyo_path_info *name) |
762 | { | 962 | { |
763 | if (name) { | 963 | if (name) { |
764 | struct tomoyo_name_entry *ptr = | 964 | struct tomoyo_name *ptr = |
765 | container_of(name, struct tomoyo_name_entry, entry); | 965 | container_of(name, typeof(*ptr), entry); |
766 | atomic_dec(&ptr->users); | 966 | atomic_dec(&ptr->users); |
767 | } | 967 | } |
768 | } | 968 | } |
769 | 969 | ||
770 | static inline void tomoyo_put_path_group(struct tomoyo_path_group *group) | 970 | static inline void tomoyo_put_group(struct tomoyo_group *group) |
771 | { | 971 | { |
772 | if (group) | 972 | if (group) |
773 | atomic_dec(&group->users); | 973 | atomic_dec(&group->users); |
@@ -784,75 +984,35 @@ static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct | |||
784 | return task_cred_xxx(task, security); | 984 | return task_cred_xxx(task, security); |
785 | } | 985 | } |
786 | 986 | ||
787 | static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1, | 987 | static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1, |
788 | const struct tomoyo_acl_info *p2) | 988 | const struct tomoyo_acl_info *p2) |
789 | { | 989 | { |
790 | return p1->type == p2->type; | 990 | return p1->type == p2->type; |
791 | } | 991 | } |
792 | 992 | ||
793 | static inline bool tomoyo_is_same_name_union | 993 | static inline bool tomoyo_same_name_union |
794 | (const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) | 994 | (const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) |
795 | { | 995 | { |
796 | return p1->filename == p2->filename && p1->group == p2->group && | 996 | return p1->filename == p2->filename && p1->group == p2->group && |
797 | p1->is_group == p2->is_group; | 997 | p1->is_group == p2->is_group; |
798 | } | 998 | } |
799 | 999 | ||
800 | static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, | 1000 | static inline bool tomoyo_same_number_union |
801 | const struct tomoyo_path_acl *p2) | 1001 | (const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) |
802 | { | ||
803 | return tomoyo_is_same_acl_head(&p1->head, &p2->head) && | ||
804 | tomoyo_is_same_name_union(&p1->name, &p2->name); | ||
805 | } | ||
806 | |||
807 | static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, | ||
808 | const struct tomoyo_path2_acl *p2) | ||
809 | { | 1002 | { |
810 | return tomoyo_is_same_acl_head(&p1->head, &p2->head) && | 1003 | return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] |
811 | tomoyo_is_same_name_union(&p1->name1, &p2->name1) && | 1004 | && p1->group == p2->group && p1->min_type == p2->min_type && |
812 | tomoyo_is_same_name_union(&p1->name2, &p2->name2); | 1005 | p1->max_type == p2->max_type && p1->is_group == p2->is_group; |
813 | } | ||
814 | |||
815 | static inline bool tomoyo_is_same_domain_initializer_entry | ||
816 | (const struct tomoyo_domain_initializer_entry *p1, | ||
817 | const struct tomoyo_domain_initializer_entry *p2) | ||
818 | { | ||
819 | return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name | ||
820 | && p1->domainname == p2->domainname | ||
821 | && p1->program == p2->program; | ||
822 | } | ||
823 | |||
824 | static inline bool tomoyo_is_same_domain_keeper_entry | ||
825 | (const struct tomoyo_domain_keeper_entry *p1, | ||
826 | const struct tomoyo_domain_keeper_entry *p2) | ||
827 | { | ||
828 | return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name | ||
829 | && p1->domainname == p2->domainname | ||
830 | && p1->program == p2->program; | ||
831 | } | ||
832 | |||
833 | static inline bool tomoyo_is_same_alias_entry | ||
834 | (const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2) | ||
835 | { | ||
836 | return p1->original_name == p2->original_name && | ||
837 | p1->aliased_name == p2->aliased_name; | ||
838 | } | 1006 | } |
839 | 1007 | ||
840 | /** | 1008 | /** |
841 | * list_for_each_cookie - iterate over a list with cookie. | 1009 | * list_for_each_cookie - iterate over a list with cookie. |
842 | * @pos: the &struct list_head to use as a loop cursor. | 1010 | * @pos: the &struct list_head to use as a loop cursor. |
843 | * @cookie: the &struct list_head to use as a cookie. | ||
844 | * @head: the head for your list. | 1011 | * @head: the head for your list. |
845 | * | ||
846 | * Same with list_for_each_rcu() except that this primitive uses @cookie | ||
847 | * so that we can continue iteration. | ||
848 | * @cookie must be NULL when iteration starts, and @cookie will become | ||
849 | * NULL when iteration finishes. | ||
850 | */ | 1012 | */ |
851 | #define list_for_each_cookie(pos, cookie, head) \ | 1013 | #define list_for_each_cookie(pos, head) \ |
852 | for (({ if (!cookie) \ | 1014 | if (!pos) \ |
853 | cookie = head; }), \ | 1015 | pos = srcu_dereference((head)->next, &tomoyo_ss); \ |
854 | pos = rcu_dereference((cookie)->next); \ | 1016 | for ( ; pos != (head); pos = srcu_dereference(pos->next, &tomoyo_ss)) |
855 | prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ | ||
856 | (cookie) = pos, pos = rcu_dereference(pos->next)) | ||
857 | 1017 | ||
858 | #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ | 1018 | #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ |
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd8ba444676..35388408e47 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c | |||
@@ -1,12 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * security/tomoyo/domain.c | 2 | * security/tomoyo/domain.c |
3 | * | 3 | * |
4 | * Implementation of the Domain-Based Mandatory Access Control. | 4 | * Domain transition functions for TOMOYO. |
5 | * | ||
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
7 | * | ||
8 | * Version: 2.2.0 2009/04/01 | ||
9 | * | 5 | * |
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
10 | */ | 7 | */ |
11 | 8 | ||
12 | #include "common.h" | 9 | #include "common.h" |
@@ -18,366 +15,191 @@ | |||
18 | /* The initial domain. */ | 15 | /* The initial domain. */ |
19 | struct tomoyo_domain_info tomoyo_kernel_domain; | 16 | struct tomoyo_domain_info tomoyo_kernel_domain; |
20 | 17 | ||
21 | /* | ||
22 | * tomoyo_domain_list is used for holding list of domains. | ||
23 | * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding | ||
24 | * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain. | ||
25 | * | ||
26 | * An entry is added by | ||
27 | * | ||
28 | * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \ | ||
29 | * /sys/kernel/security/tomoyo/domain_policy | ||
30 | * | ||
31 | * and is deleted by | ||
32 | * | ||
33 | * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \ | ||
34 | * /sys/kernel/security/tomoyo/domain_policy | ||
35 | * | ||
36 | * and all entries are retrieved by | ||
37 | * | ||
38 | * # cat /sys/kernel/security/tomoyo/domain_policy | ||
39 | * | ||
40 | * A domain is added by | ||
41 | * | ||
42 | * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy | ||
43 | * | ||
44 | * and is deleted by | ||
45 | * | ||
46 | * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy | ||
47 | * | ||
48 | * and all domains are retrieved by | ||
49 | * | ||
50 | * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | ||
51 | * | ||
52 | * Normally, a domainname is monotonically getting longer because a domainname | ||
53 | * which the process will belong to if an execve() operation succeeds is | ||
54 | * defined as a concatenation of "current domainname" + "pathname passed to | ||
55 | * execve()". | ||
56 | * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for | ||
57 | * exceptions. | ||
58 | */ | ||
59 | LIST_HEAD(tomoyo_domain_list); | ||
60 | |||
61 | /** | 18 | /** |
62 | * tomoyo_get_last_name - Get last component of a domainname. | 19 | * tomoyo_update_policy - Update an entry for exception policy. |
63 | * | ||
64 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
65 | * | ||
66 | * Returns the last component of the domainname. | ||
67 | */ | ||
68 | const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) | ||
69 | { | ||
70 | const char *cp0 = domain->domainname->name; | ||
71 | const char *cp1 = strrchr(cp0, ' '); | ||
72 | |||
73 | if (cp1) | ||
74 | return cp1 + 1; | ||
75 | return cp0; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * tomoyo_domain_initializer_list is used for holding list of programs which | ||
80 | * triggers reinitialization of domainname. Normally, a domainname is | ||
81 | * monotonically getting longer. But sometimes, we restart daemon programs. | ||
82 | * It would be convenient for us that "a daemon started upon system boot" and | ||
83 | * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO | ||
84 | * provides a way to shorten domainnames. | ||
85 | * | 20 | * |
86 | * An entry is added by | 21 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
87 | * | 22 | * @size: Size of @new_entry in bytes. |
88 | * # echo 'initialize_domain /usr/sbin/httpd' > \ | 23 | * @is_delete: True if it is a delete request. |
89 | * /sys/kernel/security/tomoyo/exception_policy | 24 | * @list: Pointer to "struct list_head". |
90 | * | 25 | * @check_duplicate: Callback function to find duplicated entry. |
91 | * and is deleted by | ||
92 | * | ||
93 | * # echo 'delete initialize_domain /usr/sbin/httpd' > \ | ||
94 | * /sys/kernel/security/tomoyo/exception_policy | ||
95 | * | ||
96 | * and all entries are retrieved by | ||
97 | * | ||
98 | * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy | ||
99 | * | ||
100 | * In the example above, /usr/sbin/httpd will belong to | ||
101 | * "<kernel> /usr/sbin/httpd" domain. | ||
102 | * | ||
103 | * You may specify a domainname using "from" keyword. | ||
104 | * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | ||
105 | * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd" | ||
106 | * domain to belong to "<kernel> /usr/sbin/httpd" domain. | ||
107 | * | ||
108 | * You may add "no_" prefix to "initialize_domain". | ||
109 | * "initialize_domain /usr/sbin/httpd" and | ||
110 | * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" | ||
111 | * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain | ||
112 | * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. | ||
113 | */ | ||
114 | LIST_HEAD(tomoyo_domain_initializer_list); | ||
115 | |||
116 | /** | ||
117 | * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. | ||
118 | * | ||
119 | * @domainname: The name of domain. May be NULL. | ||
120 | * @program: The name of program. | ||
121 | * @is_not: True if it is "no_initialize_domain" entry. | ||
122 | * @is_delete: True if it is a delete request. | ||
123 | * | 26 | * |
124 | * Returns 0 on success, negative value otherwise. | 27 | * Returns 0 on success, negative value otherwise. |
125 | * | 28 | * |
126 | * Caller holds tomoyo_read_lock(). | 29 | * Caller holds tomoyo_read_lock(). |
127 | */ | 30 | */ |
128 | static int tomoyo_update_domain_initializer_entry(const char *domainname, | 31 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, |
129 | const char *program, | 32 | bool is_delete, struct list_head *list, |
130 | const bool is_not, | 33 | bool (*check_duplicate) (const struct tomoyo_acl_head |
131 | const bool is_delete) | 34 | *, |
35 | const struct tomoyo_acl_head | ||
36 | *)) | ||
132 | { | 37 | { |
133 | struct tomoyo_domain_initializer_entry *ptr; | ||
134 | struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; | ||
135 | int error = is_delete ? -ENOENT : -ENOMEM; | 38 | int error = is_delete ? -ENOENT : -ENOMEM; |
39 | struct tomoyo_acl_head *entry; | ||
136 | 40 | ||
137 | if (!tomoyo_is_correct_path(program, 1, -1, -1)) | ||
138 | return -EINVAL; /* No patterns allowed. */ | ||
139 | if (domainname) { | ||
140 | if (!tomoyo_is_domain_def(domainname) && | ||
141 | tomoyo_is_correct_path(domainname, 1, -1, -1)) | ||
142 | e.is_last_name = true; | ||
143 | else if (!tomoyo_is_correct_domain(domainname)) | ||
144 | return -EINVAL; | ||
145 | e.domainname = tomoyo_get_name(domainname); | ||
146 | if (!e.domainname) | ||
147 | goto out; | ||
148 | } | ||
149 | e.program = tomoyo_get_name(program); | ||
150 | if (!e.program) | ||
151 | goto out; | ||
152 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 41 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
153 | goto out; | 42 | return -ENOMEM; |
154 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { | 43 | list_for_each_entry_rcu(entry, list, list) { |
155 | if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) | 44 | if (!check_duplicate(entry, new_entry)) |
156 | continue; | 45 | continue; |
157 | ptr->is_deleted = is_delete; | 46 | entry->is_deleted = is_delete; |
158 | error = 0; | 47 | error = 0; |
159 | break; | 48 | break; |
160 | } | 49 | } |
161 | if (!is_delete && error) { | 50 | if (error && !is_delete) { |
162 | struct tomoyo_domain_initializer_entry *entry = | 51 | entry = tomoyo_commit_ok(new_entry, size); |
163 | tomoyo_commit_ok(&e, sizeof(e)); | ||
164 | if (entry) { | 52 | if (entry) { |
165 | list_add_tail_rcu(&entry->list, | 53 | list_add_tail_rcu(&entry->list, list); |
166 | &tomoyo_domain_initializer_list); | ||
167 | error = 0; | 54 | error = 0; |
168 | } | 55 | } |
169 | } | 56 | } |
170 | mutex_unlock(&tomoyo_policy_lock); | 57 | mutex_unlock(&tomoyo_policy_lock); |
171 | out: | ||
172 | tomoyo_put_name(e.domainname); | ||
173 | tomoyo_put_name(e.program); | ||
174 | return error; | 58 | return error; |
175 | } | 59 | } |
176 | 60 | ||
177 | /** | 61 | /** |
178 | * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. | 62 | * tomoyo_update_domain - Update an entry for domain policy. |
179 | * | 63 | * |
180 | * @head: Pointer to "struct tomoyo_io_buffer". | 64 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
65 | * @size: Size of @new_entry in bytes. | ||
66 | * @is_delete: True if it is a delete request. | ||
67 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
68 | * @check_duplicate: Callback function to find duplicated entry. | ||
69 | * @merge_duplicate: Callback function to merge duplicated entry. | ||
181 | * | 70 | * |
182 | * Returns true on success, false otherwise. | 71 | * Returns 0 on success, negative value otherwise. |
183 | * | 72 | * |
184 | * Caller holds tomoyo_read_lock(). | 73 | * Caller holds tomoyo_read_lock(). |
185 | */ | 74 | */ |
186 | bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) | 75 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, |
76 | bool is_delete, struct tomoyo_domain_info *domain, | ||
77 | bool (*check_duplicate) (const struct tomoyo_acl_info | ||
78 | *, | ||
79 | const struct tomoyo_acl_info | ||
80 | *), | ||
81 | bool (*merge_duplicate) (struct tomoyo_acl_info *, | ||
82 | struct tomoyo_acl_info *, | ||
83 | const bool)) | ||
187 | { | 84 | { |
188 | struct list_head *pos; | 85 | int error = is_delete ? -ENOENT : -ENOMEM; |
189 | bool done = true; | 86 | struct tomoyo_acl_info *entry; |
190 | 87 | ||
191 | list_for_each_cookie(pos, head->read_var2, | 88 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
192 | &tomoyo_domain_initializer_list) { | 89 | return error; |
193 | const char *no; | 90 | list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { |
194 | const char *from = ""; | 91 | if (!check_duplicate(entry, new_entry)) |
195 | const char *domain = ""; | ||
196 | struct tomoyo_domain_initializer_entry *ptr; | ||
197 | ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, | ||
198 | list); | ||
199 | if (ptr->is_deleted) | ||
200 | continue; | 92 | continue; |
201 | no = ptr->is_not ? "no_" : ""; | 93 | if (merge_duplicate) |
202 | if (ptr->domainname) { | 94 | entry->is_deleted = merge_duplicate(entry, new_entry, |
203 | from = " from "; | 95 | is_delete); |
204 | domain = ptr->domainname->name; | 96 | else |
97 | entry->is_deleted = is_delete; | ||
98 | error = 0; | ||
99 | break; | ||
100 | } | ||
101 | if (error && !is_delete) { | ||
102 | entry = tomoyo_commit_ok(new_entry, size); | ||
103 | if (entry) { | ||
104 | list_add_tail_rcu(&entry->list, &domain->acl_info_list); | ||
105 | error = 0; | ||
205 | } | 106 | } |
206 | done = tomoyo_io_printf(head, | ||
207 | "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN | ||
208 | "%s%s%s\n", no, ptr->program->name, | ||
209 | from, domain); | ||
210 | if (!done) | ||
211 | break; | ||
212 | } | 107 | } |
213 | return done; | 108 | mutex_unlock(&tomoyo_policy_lock); |
109 | return error; | ||
214 | } | 110 | } |
215 | 111 | ||
216 | /** | 112 | void tomoyo_check_acl(struct tomoyo_request_info *r, |
217 | * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. | 113 | bool (*check_entry) (struct tomoyo_request_info *, |
218 | * | 114 | const struct tomoyo_acl_info *)) |
219 | * @data: String to parse. | ||
220 | * @is_not: True if it is "no_initialize_domain" entry. | ||
221 | * @is_delete: True if it is a delete request. | ||
222 | * | ||
223 | * Returns 0 on success, negative value otherwise. | ||
224 | * | ||
225 | * Caller holds tomoyo_read_lock(). | ||
226 | */ | ||
227 | int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, | ||
228 | const bool is_delete) | ||
229 | { | 115 | { |
230 | char *cp = strstr(data, " from "); | 116 | const struct tomoyo_domain_info *domain = r->domain; |
117 | struct tomoyo_acl_info *ptr; | ||
231 | 118 | ||
232 | if (cp) { | 119 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
233 | *cp = '\0'; | 120 | if (ptr->is_deleted || ptr->type != r->param_type) |
234 | return tomoyo_update_domain_initializer_entry(cp + 6, data, | 121 | continue; |
235 | is_not, | 122 | if (check_entry(r, ptr)) { |
236 | is_delete); | 123 | r->granted = true; |
124 | return; | ||
125 | } | ||
237 | } | 126 | } |
238 | return tomoyo_update_domain_initializer_entry(NULL, data, is_not, | 127 | r->granted = false; |
239 | is_delete); | ||
240 | } | 128 | } |
241 | 129 | ||
130 | /* The list for "struct tomoyo_domain_info". */ | ||
131 | LIST_HEAD(tomoyo_domain_list); | ||
132 | |||
133 | struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; | ||
134 | struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; | ||
135 | |||
242 | /** | 136 | /** |
243 | * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization. | 137 | * tomoyo_last_word - Get last component of a domainname. |
244 | * | ||
245 | * @domainname: The name of domain. | ||
246 | * @program: The name of program. | ||
247 | * @last_name: The last component of @domainname. | ||
248 | * | 138 | * |
249 | * Returns true if executing @program reinitializes domain transition, | 139 | * @domainname: Domainname to check. |
250 | * false otherwise. | ||
251 | * | 140 | * |
252 | * Caller holds tomoyo_read_lock(). | 141 | * Returns the last word of @domainname. |
253 | */ | 142 | */ |
254 | static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * | 143 | static const char *tomoyo_last_word(const char *name) |
255 | domainname, | ||
256 | const struct tomoyo_path_info *program, | ||
257 | const struct tomoyo_path_info * | ||
258 | last_name) | ||
259 | { | 144 | { |
260 | struct tomoyo_domain_initializer_entry *ptr; | 145 | const char *cp = strrchr(name, ' '); |
261 | bool flag = false; | 146 | if (cp) |
262 | 147 | return cp + 1; | |
263 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { | 148 | return name; |
264 | if (ptr->is_deleted) | ||
265 | continue; | ||
266 | if (ptr->domainname) { | ||
267 | if (!ptr->is_last_name) { | ||
268 | if (ptr->domainname != domainname) | ||
269 | continue; | ||
270 | } else { | ||
271 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | ||
272 | continue; | ||
273 | } | ||
274 | } | ||
275 | if (tomoyo_pathcmp(ptr->program, program)) | ||
276 | continue; | ||
277 | if (ptr->is_not) { | ||
278 | flag = false; | ||
279 | break; | ||
280 | } | ||
281 | flag = true; | ||
282 | } | ||
283 | return flag; | ||
284 | } | 149 | } |
285 | 150 | ||
286 | /* | 151 | static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, |
287 | * tomoyo_domain_keeper_list is used for holding list of domainnames which | 152 | const struct tomoyo_acl_head *b) |
288 | * suppresses domain transition. Normally, a domainname is monotonically | 153 | { |
289 | * getting longer. But sometimes, we want to suppress domain transition. | 154 | const struct tomoyo_transition_control *p1 = container_of(a, |
290 | * It would be convenient for us that programs executed from a login session | 155 | typeof(*p1), |
291 | * belong to the same domain. Thus, TOMOYO provides a way to suppress domain | 156 | head); |
292 | * transition. | 157 | const struct tomoyo_transition_control *p2 = container_of(b, |
293 | * | 158 | typeof(*p2), |
294 | * An entry is added by | 159 | head); |
295 | * | 160 | return p1->type == p2->type && p1->is_last_name == p2->is_last_name |
296 | * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | 161 | && p1->domainname == p2->domainname |
297 | * /sys/kernel/security/tomoyo/exception_policy | 162 | && p1->program == p2->program; |
298 | * | 163 | } |
299 | * and is deleted by | ||
300 | * | ||
301 | * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \ | ||
302 | * /sys/kernel/security/tomoyo/exception_policy | ||
303 | * | ||
304 | * and all entries are retrieved by | ||
305 | * | ||
306 | * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy | ||
307 | * | ||
308 | * In the example above, any process which belongs to | ||
309 | * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain, | ||
310 | * unless explicitly specified by "initialize_domain" or "no_keep_domain". | ||
311 | * | ||
312 | * You may specify a program using "from" keyword. | ||
313 | * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash" | ||
314 | * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash" | ||
315 | * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain. | ||
316 | * | ||
317 | * You may add "no_" prefix to "keep_domain". | ||
318 | * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and | ||
319 | * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will | ||
320 | * cause "/usr/bin/passwd" to belong to | ||
321 | * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless | ||
322 | * explicitly specified by "initialize_domain". | ||
323 | */ | ||
324 | LIST_HEAD(tomoyo_domain_keeper_list); | ||
325 | 164 | ||
326 | /** | 165 | /** |
327 | * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. | 166 | * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. |
328 | * | 167 | * |
329 | * @domainname: The name of domain. | 168 | * @domainname: The name of domain. Maybe NULL. |
330 | * @program: The name of program. May be NULL. | 169 | * @program: The name of program. Maybe NULL. |
331 | * @is_not: True if it is "no_keep_domain" entry. | 170 | * @type: Type of transition. |
332 | * @is_delete: True if it is a delete request. | 171 | * @is_delete: True if it is a delete request. |
333 | * | 172 | * |
334 | * Returns 0 on success, negative value otherwise. | 173 | * Returns 0 on success, negative value otherwise. |
335 | * | ||
336 | * Caller holds tomoyo_read_lock(). | ||
337 | */ | 174 | */ |
338 | static int tomoyo_update_domain_keeper_entry(const char *domainname, | 175 | static int tomoyo_update_transition_control_entry(const char *domainname, |
339 | const char *program, | 176 | const char *program, |
340 | const bool is_not, | 177 | const u8 type, |
341 | const bool is_delete) | 178 | const bool is_delete) |
342 | { | 179 | { |
343 | struct tomoyo_domain_keeper_entry *ptr; | 180 | struct tomoyo_transition_control e = { .type = type }; |
344 | struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; | ||
345 | int error = is_delete ? -ENOENT : -ENOMEM; | 181 | int error = is_delete ? -ENOENT : -ENOMEM; |
346 | |||
347 | if (!tomoyo_is_domain_def(domainname) && | ||
348 | tomoyo_is_correct_path(domainname, 1, -1, -1)) | ||
349 | e.is_last_name = true; | ||
350 | else if (!tomoyo_is_correct_domain(domainname)) | ||
351 | return -EINVAL; | ||
352 | if (program) { | 182 | if (program) { |
353 | if (!tomoyo_is_correct_path(program, 1, -1, -1)) | 183 | if (!tomoyo_correct_path(program)) |
354 | return -EINVAL; | 184 | return -EINVAL; |
355 | e.program = tomoyo_get_name(program); | 185 | e.program = tomoyo_get_name(program); |
356 | if (!e.program) | 186 | if (!e.program) |
357 | goto out; | 187 | goto out; |
358 | } | 188 | } |
359 | e.domainname = tomoyo_get_name(domainname); | 189 | if (domainname) { |
360 | if (!e.domainname) | 190 | if (!tomoyo_correct_domain(domainname)) { |
361 | goto out; | 191 | if (!tomoyo_correct_path(domainname)) |
362 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 192 | goto out; |
363 | goto out; | 193 | e.is_last_name = true; |
364 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | ||
365 | if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) | ||
366 | continue; | ||
367 | ptr->is_deleted = is_delete; | ||
368 | error = 0; | ||
369 | break; | ||
370 | } | ||
371 | if (!is_delete && error) { | ||
372 | struct tomoyo_domain_keeper_entry *entry = | ||
373 | tomoyo_commit_ok(&e, sizeof(e)); | ||
374 | if (entry) { | ||
375 | list_add_tail_rcu(&entry->list, | ||
376 | &tomoyo_domain_keeper_list); | ||
377 | error = 0; | ||
378 | } | 194 | } |
195 | e.domainname = tomoyo_get_name(domainname); | ||
196 | if (!e.domainname) | ||
197 | goto out; | ||
379 | } | 198 | } |
380 | mutex_unlock(&tomoyo_policy_lock); | 199 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
200 | &tomoyo_policy_list | ||
201 | [TOMOYO_ID_TRANSITION_CONTROL], | ||
202 | tomoyo_same_transition_control); | ||
381 | out: | 203 | out: |
382 | tomoyo_put_name(e.domainname); | 204 | tomoyo_put_name(e.domainname); |
383 | tomoyo_put_name(e.program); | 205 | tomoyo_put_name(e.program); |
@@ -385,219 +207,133 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, | |||
385 | } | 207 | } |
386 | 208 | ||
387 | /** | 209 | /** |
388 | * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. | 210 | * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. |
389 | * | 211 | * |
390 | * @data: String to parse. | 212 | * @data: String to parse. |
391 | * @is_not: True if it is "no_keep_domain" entry. | ||
392 | * @is_delete: True if it is a delete request. | 213 | * @is_delete: True if it is a delete request. |
214 | * @type: Type of this entry. | ||
393 | * | 215 | * |
394 | * Caller holds tomoyo_read_lock(). | 216 | * Returns 0 on success, negative value otherwise. |
395 | */ | ||
396 | int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, | ||
397 | const bool is_delete) | ||
398 | { | ||
399 | char *cp = strstr(data, " from "); | ||
400 | |||
401 | if (cp) { | ||
402 | *cp = '\0'; | ||
403 | return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not, | ||
404 | is_delete); | ||
405 | } | ||
406 | return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list. | ||
411 | * | ||
412 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
413 | * | ||
414 | * Returns true on success, false otherwise. | ||
415 | * | ||
416 | * Caller holds tomoyo_read_lock(). | ||
417 | */ | 217 | */ |
418 | bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) | 218 | int tomoyo_write_transition_control(char *data, const bool is_delete, |
219 | const u8 type) | ||
419 | { | 220 | { |
420 | struct list_head *pos; | 221 | char *domainname = strstr(data, " from "); |
421 | bool done = true; | 222 | if (domainname) { |
422 | 223 | *domainname = '\0'; | |
423 | list_for_each_cookie(pos, head->read_var2, | 224 | domainname += 6; |
424 | &tomoyo_domain_keeper_list) { | 225 | } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || |
425 | struct tomoyo_domain_keeper_entry *ptr; | 226 | type == TOMOYO_TRANSITION_CONTROL_KEEP) { |
426 | const char *no; | 227 | domainname = data; |
427 | const char *from = ""; | 228 | data = NULL; |
428 | const char *program = ""; | ||
429 | |||
430 | ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list); | ||
431 | if (ptr->is_deleted) | ||
432 | continue; | ||
433 | no = ptr->is_not ? "no_" : ""; | ||
434 | if (ptr->program) { | ||
435 | from = " from "; | ||
436 | program = ptr->program->name; | ||
437 | } | ||
438 | done = tomoyo_io_printf(head, | ||
439 | "%s" TOMOYO_KEYWORD_KEEP_DOMAIN | ||
440 | "%s%s%s\n", no, program, from, | ||
441 | ptr->domainname->name); | ||
442 | if (!done) | ||
443 | break; | ||
444 | } | 229 | } |
445 | return done; | 230 | return tomoyo_update_transition_control_entry(domainname, data, type, |
231 | is_delete); | ||
446 | } | 232 | } |
447 | 233 | ||
448 | /** | 234 | /** |
449 | * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. | 235 | * tomoyo_transition_type - Get domain transition type. |
450 | * | 236 | * |
451 | * @domainname: The name of domain. | 237 | * @domainname: The name of domain. |
452 | * @program: The name of program. | 238 | * @program: The name of program. |
453 | * @last_name: The last component of @domainname. | ||
454 | * | 239 | * |
455 | * Returns true if executing @program supresses domain transition, | 240 | * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program |
456 | * false otherwise. | 241 | * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing |
242 | * @program suppresses domain transition, others otherwise. | ||
457 | * | 243 | * |
458 | * Caller holds tomoyo_read_lock(). | 244 | * Caller holds tomoyo_read_lock(). |
459 | */ | 245 | */ |
460 | static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, | 246 | static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, |
461 | const struct tomoyo_path_info *program, | 247 | const struct tomoyo_path_info *program) |
462 | const struct tomoyo_path_info *last_name) | ||
463 | { | 248 | { |
464 | struct tomoyo_domain_keeper_entry *ptr; | 249 | const struct tomoyo_transition_control *ptr; |
465 | bool flag = false; | 250 | const char *last_name = tomoyo_last_word(domainname->name); |
466 | 251 | u8 type; | |
467 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | 252 | for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { |
468 | if (ptr->is_deleted) | 253 | next: |
469 | continue; | 254 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
470 | if (!ptr->is_last_name) { | 255 | [TOMOYO_ID_TRANSITION_CONTROL], |
471 | if (ptr->domainname != domainname) | 256 | head.list) { |
257 | if (ptr->head.is_deleted || ptr->type != type) | ||
472 | continue; | 258 | continue; |
473 | } else { | 259 | if (ptr->domainname) { |
474 | if (tomoyo_pathcmp(ptr->domainname, last_name)) | 260 | if (!ptr->is_last_name) { |
261 | if (ptr->domainname != domainname) | ||
262 | continue; | ||
263 | } else { | ||
264 | /* | ||
265 | * Use direct strcmp() since this is | ||
266 | * unlikely used. | ||
267 | */ | ||
268 | if (strcmp(ptr->domainname->name, | ||
269 | last_name)) | ||
270 | continue; | ||
271 | } | ||
272 | } | ||
273 | if (ptr->program && | ||
274 | tomoyo_pathcmp(ptr->program, program)) | ||
475 | continue; | 275 | continue; |
276 | if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { | ||
277 | /* | ||
278 | * Do not check for initialize_domain if | ||
279 | * no_initialize_domain matched. | ||
280 | */ | ||
281 | type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; | ||
282 | goto next; | ||
283 | } | ||
284 | goto done; | ||
476 | } | 285 | } |
477 | if (ptr->program && tomoyo_pathcmp(ptr->program, program)) | ||
478 | continue; | ||
479 | if (ptr->is_not) { | ||
480 | flag = false; | ||
481 | break; | ||
482 | } | ||
483 | flag = true; | ||
484 | } | 286 | } |
485 | return flag; | 287 | done: |
288 | return type; | ||
486 | } | 289 | } |
487 | 290 | ||
488 | /* | 291 | static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, |
489 | * tomoyo_alias_list is used for holding list of symlink's pathnames which are | 292 | const struct tomoyo_acl_head *b) |
490 | * allowed to be passed to an execve() request. Normally, the domainname which | 293 | { |
491 | * the current process will belong to after execve() succeeds is calculated | 294 | const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); |
492 | * using dereferenced pathnames. But some programs behave differently depending | 295 | const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); |
493 | * on the name passed to argv[0]. For busybox, calculating domainname using | 296 | return p1->original_name == p2->original_name && |
494 | * dereferenced pathnames will cause all programs in the busybox to belong to | 297 | p1->aggregated_name == p2->aggregated_name; |
495 | * the same domain. Thus, TOMOYO provides a way to allow use of symlink's | 298 | } |
496 | * pathname for checking execve()'s permission and calculating domainname which | ||
497 | * the current process will belong to after execve() succeeds. | ||
498 | * | ||
499 | * An entry is added by | ||
500 | * | ||
501 | * # echo 'alias /bin/busybox /bin/cat' > \ | ||
502 | * /sys/kernel/security/tomoyo/exception_policy | ||
503 | * | ||
504 | * and is deleted by | ||
505 | * | ||
506 | * # echo 'delete alias /bin/busybox /bin/cat' > \ | ||
507 | * /sys/kernel/security/tomoyo/exception_policy | ||
508 | * | ||
509 | * and all entries are retrieved by | ||
510 | * | ||
511 | * # grep ^alias /sys/kernel/security/tomoyo/exception_policy | ||
512 | * | ||
513 | * In the example above, if /bin/cat is a symlink to /bin/busybox and execution | ||
514 | * of /bin/cat is requested, permission is checked for /bin/cat rather than | ||
515 | * /bin/busybox and domainname which the current process will belong to after | ||
516 | * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . | ||
517 | */ | ||
518 | LIST_HEAD(tomoyo_alias_list); | ||
519 | 299 | ||
520 | /** | 300 | /** |
521 | * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. | 301 | * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. |
522 | * | 302 | * |
523 | * @original_name: The original program's real name. | 303 | * @original_name: The original program's name. |
524 | * @aliased_name: The symbolic program's symbolic link's name. | 304 | * @aggregated_name: The program name to use. |
525 | * @is_delete: True if it is a delete request. | 305 | * @is_delete: True if it is a delete request. |
526 | * | 306 | * |
527 | * Returns 0 on success, negative value otherwise. | 307 | * Returns 0 on success, negative value otherwise. |
528 | * | 308 | * |
529 | * Caller holds tomoyo_read_lock(). | 309 | * Caller holds tomoyo_read_lock(). |
530 | */ | 310 | */ |
531 | static int tomoyo_update_alias_entry(const char *original_name, | 311 | static int tomoyo_update_aggregator_entry(const char *original_name, |
532 | const char *aliased_name, | 312 | const char *aggregated_name, |
533 | const bool is_delete) | 313 | const bool is_delete) |
534 | { | 314 | { |
535 | struct tomoyo_alias_entry *ptr; | 315 | struct tomoyo_aggregator e = { }; |
536 | struct tomoyo_alias_entry e = { }; | ||
537 | int error = is_delete ? -ENOENT : -ENOMEM; | 316 | int error = is_delete ? -ENOENT : -ENOMEM; |
538 | 317 | ||
539 | if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || | 318 | if (!tomoyo_correct_path(original_name) || |
540 | !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) | 319 | !tomoyo_correct_path(aggregated_name)) |
541 | return -EINVAL; /* No patterns allowed. */ | 320 | return -EINVAL; |
542 | e.original_name = tomoyo_get_name(original_name); | 321 | e.original_name = tomoyo_get_name(original_name); |
543 | e.aliased_name = tomoyo_get_name(aliased_name); | 322 | e.aggregated_name = tomoyo_get_name(aggregated_name); |
544 | if (!e.original_name || !e.aliased_name) | 323 | if (!e.original_name || !e.aggregated_name || |
324 | e.aggregated_name->is_patterned) /* No patterns allowed. */ | ||
545 | goto out; | 325 | goto out; |
546 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 326 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
547 | goto out; | 327 | &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], |
548 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | 328 | tomoyo_same_aggregator); |
549 | if (!tomoyo_is_same_alias_entry(ptr, &e)) | ||
550 | continue; | ||
551 | ptr->is_deleted = is_delete; | ||
552 | error = 0; | ||
553 | break; | ||
554 | } | ||
555 | if (!is_delete && error) { | ||
556 | struct tomoyo_alias_entry *entry = | ||
557 | tomoyo_commit_ok(&e, sizeof(e)); | ||
558 | if (entry) { | ||
559 | list_add_tail_rcu(&entry->list, &tomoyo_alias_list); | ||
560 | error = 0; | ||
561 | } | ||
562 | } | ||
563 | mutex_unlock(&tomoyo_policy_lock); | ||
564 | out: | 329 | out: |
565 | tomoyo_put_name(e.original_name); | 330 | tomoyo_put_name(e.original_name); |
566 | tomoyo_put_name(e.aliased_name); | 331 | tomoyo_put_name(e.aggregated_name); |
567 | return error; | 332 | return error; |
568 | } | 333 | } |
569 | 334 | ||
570 | /** | 335 | /** |
571 | * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list. | 336 | * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. |
572 | * | ||
573 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
574 | * | ||
575 | * Returns true on success, false otherwise. | ||
576 | * | ||
577 | * Caller holds tomoyo_read_lock(). | ||
578 | */ | ||
579 | bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) | ||
580 | { | ||
581 | struct list_head *pos; | ||
582 | bool done = true; | ||
583 | |||
584 | list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { | ||
585 | struct tomoyo_alias_entry *ptr; | ||
586 | |||
587 | ptr = list_entry(pos, struct tomoyo_alias_entry, list); | ||
588 | if (ptr->is_deleted) | ||
589 | continue; | ||
590 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", | ||
591 | ptr->original_name->name, | ||
592 | ptr->aliased_name->name); | ||
593 | if (!done) | ||
594 | break; | ||
595 | } | ||
596 | return done; | ||
597 | } | ||
598 | |||
599 | /** | ||
600 | * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. | ||
601 | * | 337 | * |
602 | * @data: String to parse. | 338 | * @data: String to parse. |
603 | * @is_delete: True if it is a delete request. | 339 | * @is_delete: True if it is a delete request. |
@@ -606,18 +342,18 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) | |||
606 | * | 342 | * |
607 | * Caller holds tomoyo_read_lock(). | 343 | * Caller holds tomoyo_read_lock(). |
608 | */ | 344 | */ |
609 | int tomoyo_write_alias_policy(char *data, const bool is_delete) | 345 | int tomoyo_write_aggregator(char *data, const bool is_delete) |
610 | { | 346 | { |
611 | char *cp = strchr(data, ' '); | 347 | char *cp = strchr(data, ' '); |
612 | 348 | ||
613 | if (!cp) | 349 | if (!cp) |
614 | return -EINVAL; | 350 | return -EINVAL; |
615 | *cp++ = '\0'; | 351 | *cp++ = '\0'; |
616 | return tomoyo_update_alias_entry(data, cp, is_delete); | 352 | return tomoyo_update_aggregator_entry(data, cp, is_delete); |
617 | } | 353 | } |
618 | 354 | ||
619 | /** | 355 | /** |
620 | * tomoyo_find_or_assign_new_domain - Create a domain. | 356 | * tomoyo_assign_domain - Create a domain. |
621 | * | 357 | * |
622 | * @domainname: The name of domain. | 358 | * @domainname: The name of domain. |
623 | * @profile: Profile number to assign if the domain was newly created. | 359 | * @profile: Profile number to assign if the domain was newly created. |
@@ -626,16 +362,15 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete) | |||
626 | * | 362 | * |
627 | * Caller holds tomoyo_read_lock(). | 363 | * Caller holds tomoyo_read_lock(). |
628 | */ | 364 | */ |
629 | struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | 365 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, |
630 | domainname, | 366 | const u8 profile) |
631 | const u8 profile) | ||
632 | { | 367 | { |
633 | struct tomoyo_domain_info *entry; | 368 | struct tomoyo_domain_info *entry; |
634 | struct tomoyo_domain_info *domain = NULL; | 369 | struct tomoyo_domain_info *domain = NULL; |
635 | const struct tomoyo_path_info *saved_domainname; | 370 | const struct tomoyo_path_info *saved_domainname; |
636 | bool found = false; | 371 | bool found = false; |
637 | 372 | ||
638 | if (!tomoyo_is_correct_domain(domainname)) | 373 | if (!tomoyo_correct_domain(domainname)) |
639 | return NULL; | 374 | return NULL; |
640 | saved_domainname = tomoyo_get_name(domainname); | 375 | saved_domainname = tomoyo_get_name(domainname); |
641 | if (!saved_domainname) | 376 | if (!saved_domainname) |
@@ -678,116 +413,118 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * | |||
678 | */ | 413 | */ |
679 | int tomoyo_find_next_domain(struct linux_binprm *bprm) | 414 | int tomoyo_find_next_domain(struct linux_binprm *bprm) |
680 | { | 415 | { |
681 | /* | 416 | struct tomoyo_request_info r; |
682 | * This function assumes that the size of buffer returned by | 417 | char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
683 | * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. | ||
684 | */ | ||
685 | struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS); | ||
686 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); | 418 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); |
687 | struct tomoyo_domain_info *domain = NULL; | 419 | struct tomoyo_domain_info *domain = NULL; |
688 | const char *old_domain_name = old_domain->domainname->name; | ||
689 | const char *original_name = bprm->filename; | 420 | const char *original_name = bprm->filename; |
690 | char *new_domain_name = NULL; | 421 | u8 mode; |
691 | char *real_program_name = NULL; | 422 | bool is_enforce; |
692 | char *symlink_program_name = NULL; | ||
693 | const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); | ||
694 | const bool is_enforce = (mode == 3); | ||
695 | int retval = -ENOMEM; | 423 | int retval = -ENOMEM; |
696 | struct tomoyo_path_info r; /* real name */ | 424 | bool need_kfree = false; |
697 | struct tomoyo_path_info s; /* symlink name */ | 425 | struct tomoyo_path_info rn = { }; /* real name */ |
698 | struct tomoyo_path_info l; /* last name */ | ||
699 | static bool initialized; | ||
700 | 426 | ||
427 | mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); | ||
428 | is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); | ||
701 | if (!tmp) | 429 | if (!tmp) |
702 | goto out; | 430 | goto out; |
703 | 431 | ||
704 | if (!initialized) { | 432 | retry: |
705 | /* | 433 | if (need_kfree) { |
706 | * Built-in initializers. This is needed because policies are | 434 | kfree(rn.name); |
707 | * not loaded until starting /sbin/init. | 435 | need_kfree = false; |
708 | */ | ||
709 | tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug", | ||
710 | false, false); | ||
711 | tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe", | ||
712 | false, false); | ||
713 | initialized = true; | ||
714 | } | 436 | } |
715 | 437 | /* Get symlink's pathname of program. */ | |
716 | /* Get tomoyo_realpath of program. */ | ||
717 | retval = -ENOENT; | 438 | retval = -ENOENT; |
718 | /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ | 439 | rn.name = tomoyo_realpath_nofollow(original_name); |
719 | real_program_name = tomoyo_realpath(original_name); | 440 | if (!rn.name) |
720 | if (!real_program_name) | ||
721 | goto out; | ||
722 | /* Get tomoyo_realpath of symbolic link. */ | ||
723 | symlink_program_name = tomoyo_realpath_nofollow(original_name); | ||
724 | if (!symlink_program_name) | ||
725 | goto out; | 441 | goto out; |
726 | 442 | tomoyo_fill_path_info(&rn); | |
727 | r.name = real_program_name; | 443 | need_kfree = true; |
728 | tomoyo_fill_path_info(&r); | 444 | |
729 | s.name = symlink_program_name; | 445 | /* Check 'aggregator' directive. */ |
730 | tomoyo_fill_path_info(&s); | 446 | { |
731 | l.name = tomoyo_get_last_name(old_domain); | 447 | struct tomoyo_aggregator *ptr; |
732 | tomoyo_fill_path_info(&l); | 448 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
733 | 449 | [TOMOYO_ID_AGGREGATOR], head.list) { | |
734 | /* Check 'alias' directive. */ | 450 | if (ptr->head.is_deleted || |
735 | if (tomoyo_pathcmp(&r, &s)) { | 451 | !tomoyo_path_matches_pattern(&rn, |
736 | struct tomoyo_alias_entry *ptr; | 452 | ptr->original_name)) |
737 | /* Is this program allowed to be called via symbolic links? */ | ||
738 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | ||
739 | if (ptr->is_deleted || | ||
740 | tomoyo_pathcmp(&r, ptr->original_name) || | ||
741 | tomoyo_pathcmp(&s, ptr->aliased_name)) | ||
742 | continue; | 453 | continue; |
743 | memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); | 454 | kfree(rn.name); |
744 | strncpy(real_program_name, ptr->aliased_name->name, | 455 | need_kfree = false; |
745 | TOMOYO_MAX_PATHNAME_LEN - 1); | 456 | /* This is OK because it is read only. */ |
746 | tomoyo_fill_path_info(&r); | 457 | rn = *ptr->aggregated_name; |
747 | break; | 458 | break; |
748 | } | 459 | } |
749 | } | 460 | } |
750 | 461 | ||
751 | /* Check execute permission. */ | 462 | /* Check execute permission. */ |
752 | retval = tomoyo_check_exec_perm(old_domain, &r); | 463 | retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); |
464 | if (retval == TOMOYO_RETRY_REQUEST) | ||
465 | goto retry; | ||
753 | if (retval < 0) | 466 | if (retval < 0) |
754 | goto out; | 467 | goto out; |
468 | /* | ||
469 | * To be able to specify domainnames with wildcards, use the | ||
470 | * pathname specified in the policy (which may contain | ||
471 | * wildcard) rather than the pathname passed to execve() | ||
472 | * (which never contains wildcard). | ||
473 | */ | ||
474 | if (r.param.path.matched_path) { | ||
475 | if (need_kfree) | ||
476 | kfree(rn.name); | ||
477 | need_kfree = false; | ||
478 | /* This is OK because it is read only. */ | ||
479 | rn = *r.param.path.matched_path; | ||
480 | } | ||
755 | 481 | ||
756 | new_domain_name = tmp->buffer; | 482 | /* Calculate domain to transit to. */ |
757 | if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { | 483 | switch (tomoyo_transition_type(old_domain->domainname, &rn)) { |
484 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: | ||
758 | /* Transit to the child of tomoyo_kernel_domain domain. */ | 485 | /* Transit to the child of tomoyo_kernel_domain domain. */ |
759 | snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, | 486 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " |
760 | TOMOYO_ROOT_NAME " " "%s", real_program_name); | 487 | "%s", rn.name); |
761 | } else if (old_domain == &tomoyo_kernel_domain && | 488 | break; |
762 | !tomoyo_policy_loaded) { | 489 | case TOMOYO_TRANSITION_CONTROL_KEEP: |
763 | /* | ||
764 | * Needn't to transit from kernel domain before starting | ||
765 | * /sbin/init. But transit from kernel domain if executing | ||
766 | * initializers because they might start before /sbin/init. | ||
767 | */ | ||
768 | domain = old_domain; | ||
769 | } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) { | ||
770 | /* Keep current domain. */ | 490 | /* Keep current domain. */ |
771 | domain = old_domain; | 491 | domain = old_domain; |
772 | } else { | 492 | break; |
773 | /* Normal domain transition. */ | 493 | default: |
774 | snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, | 494 | if (old_domain == &tomoyo_kernel_domain && |
775 | "%s %s", old_domain_name, real_program_name); | 495 | !tomoyo_policy_loaded) { |
496 | /* | ||
497 | * Needn't to transit from kernel domain before | ||
498 | * starting /sbin/init. But transit from kernel domain | ||
499 | * if executing initializers because they might start | ||
500 | * before /sbin/init. | ||
501 | */ | ||
502 | domain = old_domain; | ||
503 | } else { | ||
504 | /* Normal domain transition. */ | ||
505 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | ||
506 | old_domain->domainname->name, rn.name); | ||
507 | } | ||
508 | break; | ||
776 | } | 509 | } |
777 | if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) | 510 | if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) |
778 | goto done; | 511 | goto done; |
779 | domain = tomoyo_find_domain(new_domain_name); | 512 | domain = tomoyo_find_domain(tmp); |
780 | if (domain) | 513 | if (domain) |
781 | goto done; | 514 | goto done; |
782 | if (is_enforce) | 515 | if (is_enforce) { |
783 | goto done; | 516 | int error = tomoyo_supervisor(&r, "# wants to create domain\n" |
784 | domain = tomoyo_find_or_assign_new_domain(new_domain_name, | 517 | "%s\n", tmp); |
785 | old_domain->profile); | 518 | if (error == TOMOYO_RETRY_REQUEST) |
519 | goto retry; | ||
520 | if (error < 0) | ||
521 | goto done; | ||
522 | } | ||
523 | domain = tomoyo_assign_domain(tmp, old_domain->profile); | ||
786 | done: | 524 | done: |
787 | if (domain) | 525 | if (domain) |
788 | goto out; | 526 | goto out; |
789 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", | 527 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); |
790 | new_domain_name); | ||
791 | if (is_enforce) | 528 | if (is_enforce) |
792 | retval = -EPERM; | 529 | retval = -EPERM; |
793 | else | 530 | else |
@@ -798,8 +535,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
798 | /* Update reference count on "struct tomoyo_domain_info". */ | 535 | /* Update reference count on "struct tomoyo_domain_info". */ |
799 | atomic_inc(&domain->users); | 536 | atomic_inc(&domain->users); |
800 | bprm->cred->security = domain; | 537 | bprm->cred->security = domain; |
801 | kfree(real_program_name); | 538 | if (need_kfree) |
802 | kfree(symlink_program_name); | 539 | kfree(rn.name); |
803 | kfree(tmp); | 540 | kfree(tmp); |
804 | return retval; | 541 | return retval; |
805 | } | 542 | } |
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 1c6f8238ec4..9d32f182301 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c | |||
@@ -1,48 +1,88 @@ | |||
1 | /* | 1 | /* |
2 | * security/tomoyo/file.c | 2 | * security/tomoyo/file.c |
3 | * | 3 | * |
4 | * Implementation of the Domain-Based Mandatory Access Control. | 4 | * Pathname restriction functions. |
5 | * | ||
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
7 | * | ||
8 | * Version: 2.2.0 2009/04/01 | ||
9 | * | 5 | * |
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
10 | */ | 7 | */ |
11 | 8 | ||
12 | #include "common.h" | 9 | #include "common.h" |
13 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
14 | 11 | ||
15 | /* Keyword array for single path operations. */ | 12 | /* Keyword array for operations with one pathname. */ |
16 | static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { | 13 | const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { |
17 | [TOMOYO_TYPE_READ_WRITE] = "read/write", | 14 | [TOMOYO_TYPE_READ_WRITE] = "read/write", |
18 | [TOMOYO_TYPE_EXECUTE] = "execute", | 15 | [TOMOYO_TYPE_EXECUTE] = "execute", |
19 | [TOMOYO_TYPE_READ] = "read", | 16 | [TOMOYO_TYPE_READ] = "read", |
20 | [TOMOYO_TYPE_WRITE] = "write", | 17 | [TOMOYO_TYPE_WRITE] = "write", |
21 | [TOMOYO_TYPE_CREATE] = "create", | ||
22 | [TOMOYO_TYPE_UNLINK] = "unlink", | 18 | [TOMOYO_TYPE_UNLINK] = "unlink", |
23 | [TOMOYO_TYPE_MKDIR] = "mkdir", | ||
24 | [TOMOYO_TYPE_RMDIR] = "rmdir", | 19 | [TOMOYO_TYPE_RMDIR] = "rmdir", |
25 | [TOMOYO_TYPE_MKFIFO] = "mkfifo", | ||
26 | [TOMOYO_TYPE_MKSOCK] = "mksock", | ||
27 | [TOMOYO_TYPE_MKBLOCK] = "mkblock", | ||
28 | [TOMOYO_TYPE_MKCHAR] = "mkchar", | ||
29 | [TOMOYO_TYPE_TRUNCATE] = "truncate", | 20 | [TOMOYO_TYPE_TRUNCATE] = "truncate", |
30 | [TOMOYO_TYPE_SYMLINK] = "symlink", | 21 | [TOMOYO_TYPE_SYMLINK] = "symlink", |
31 | [TOMOYO_TYPE_REWRITE] = "rewrite", | 22 | [TOMOYO_TYPE_REWRITE] = "rewrite", |
23 | [TOMOYO_TYPE_CHROOT] = "chroot", | ||
24 | [TOMOYO_TYPE_UMOUNT] = "unmount", | ||
25 | }; | ||
26 | |||
27 | /* Keyword array for operations with one pathname and three numbers. */ | ||
28 | const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = { | ||
29 | [TOMOYO_TYPE_MKBLOCK] = "mkblock", | ||
30 | [TOMOYO_TYPE_MKCHAR] = "mkchar", | ||
31 | }; | ||
32 | |||
33 | /* Keyword array for operations with two pathnames. */ | ||
34 | const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { | ||
35 | [TOMOYO_TYPE_LINK] = "link", | ||
36 | [TOMOYO_TYPE_RENAME] = "rename", | ||
37 | [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", | ||
38 | }; | ||
39 | |||
40 | /* Keyword array for operations with one pathname and one number. */ | ||
41 | const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { | ||
42 | [TOMOYO_TYPE_CREATE] = "create", | ||
43 | [TOMOYO_TYPE_MKDIR] = "mkdir", | ||
44 | [TOMOYO_TYPE_MKFIFO] = "mkfifo", | ||
45 | [TOMOYO_TYPE_MKSOCK] = "mksock", | ||
32 | [TOMOYO_TYPE_IOCTL] = "ioctl", | 46 | [TOMOYO_TYPE_IOCTL] = "ioctl", |
33 | [TOMOYO_TYPE_CHMOD] = "chmod", | 47 | [TOMOYO_TYPE_CHMOD] = "chmod", |
34 | [TOMOYO_TYPE_CHOWN] = "chown", | 48 | [TOMOYO_TYPE_CHOWN] = "chown", |
35 | [TOMOYO_TYPE_CHGRP] = "chgrp", | 49 | [TOMOYO_TYPE_CHGRP] = "chgrp", |
36 | [TOMOYO_TYPE_CHROOT] = "chroot", | ||
37 | [TOMOYO_TYPE_MOUNT] = "mount", | ||
38 | [TOMOYO_TYPE_UMOUNT] = "unmount", | ||
39 | }; | 50 | }; |
40 | 51 | ||
41 | /* Keyword array for double path operations. */ | 52 | static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { |
42 | static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { | 53 | [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN, |
43 | [TOMOYO_TYPE_LINK] = "link", | 54 | [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, |
44 | [TOMOYO_TYPE_RENAME] = "rename", | 55 | [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, |
45 | [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", | 56 | [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, |
57 | [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, | ||
58 | [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, | ||
59 | [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, | ||
60 | [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, | ||
61 | [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE, | ||
62 | [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, | ||
63 | [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, | ||
64 | }; | ||
65 | |||
66 | static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = { | ||
67 | [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, | ||
68 | [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, | ||
69 | }; | ||
70 | |||
71 | static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { | ||
72 | [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, | ||
73 | [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, | ||
74 | [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, | ||
75 | }; | ||
76 | |||
77 | static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { | ||
78 | [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, | ||
79 | [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, | ||
80 | [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, | ||
81 | [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, | ||
82 | [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, | ||
83 | [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, | ||
84 | [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, | ||
85 | [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, | ||
46 | }; | 86 | }; |
47 | 87 | ||
48 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr) | 88 | void tomoyo_put_name_union(struct tomoyo_name_union *ptr) |
@@ -50,56 +90,45 @@ void tomoyo_put_name_union(struct tomoyo_name_union *ptr) | |||
50 | if (!ptr) | 90 | if (!ptr) |
51 | return; | 91 | return; |
52 | if (ptr->is_group) | 92 | if (ptr->is_group) |
53 | tomoyo_put_path_group(ptr->group); | 93 | tomoyo_put_group(ptr->group); |
54 | else | 94 | else |
55 | tomoyo_put_name(ptr->filename); | 95 | tomoyo_put_name(ptr->filename); |
56 | } | 96 | } |
57 | 97 | ||
58 | bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, | 98 | const struct tomoyo_path_info * |
59 | const struct tomoyo_name_union *ptr) | 99 | tomoyo_compare_name_union(const struct tomoyo_path_info *name, |
100 | const struct tomoyo_name_union *ptr) | ||
60 | { | 101 | { |
61 | if (ptr->is_group) | 102 | if (ptr->is_group) |
62 | return tomoyo_path_matches_group(name, ptr->group, 1); | 103 | return tomoyo_path_matches_group(name, ptr->group); |
63 | return tomoyo_path_matches_pattern(name, ptr->filename); | 104 | if (tomoyo_path_matches_pattern(name, ptr->filename)) |
105 | return ptr->filename; | ||
106 | return NULL; | ||
64 | } | 107 | } |
65 | 108 | ||
66 | static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info | 109 | void tomoyo_put_number_union(struct tomoyo_number_union *ptr) |
67 | *name, | ||
68 | const struct tomoyo_name_union | ||
69 | *ptr, const bool may_use_pattern) | ||
70 | { | 110 | { |
71 | if (ptr->is_group) | 111 | if (ptr && ptr->is_group) |
72 | return tomoyo_path_matches_group(name, ptr->group, | 112 | tomoyo_put_group(ptr->group); |
73 | may_use_pattern); | ||
74 | if (may_use_pattern || !ptr->filename->is_patterned) | ||
75 | return tomoyo_path_matches_pattern(name, ptr->filename); | ||
76 | return false; | ||
77 | } | 113 | } |
78 | 114 | ||
79 | /** | 115 | bool tomoyo_compare_number_union(const unsigned long value, |
80 | * tomoyo_path2keyword - Get the name of single path operation. | 116 | const struct tomoyo_number_union *ptr) |
81 | * | ||
82 | * @operation: Type of operation. | ||
83 | * | ||
84 | * Returns the name of single path operation. | ||
85 | */ | ||
86 | const char *tomoyo_path2keyword(const u8 operation) | ||
87 | { | 117 | { |
88 | return (operation < TOMOYO_MAX_PATH_OPERATION) | 118 | if (ptr->is_group) |
89 | ? tomoyo_path_keyword[operation] : NULL; | 119 | return tomoyo_number_matches_group(value, value, ptr->group); |
120 | return value >= ptr->values[0] && value <= ptr->values[1]; | ||
90 | } | 121 | } |
91 | 122 | ||
92 | /** | 123 | static void tomoyo_add_slash(struct tomoyo_path_info *buf) |
93 | * tomoyo_path22keyword - Get the name of double path operation. | ||
94 | * | ||
95 | * @operation: Type of operation. | ||
96 | * | ||
97 | * Returns the name of double path operation. | ||
98 | */ | ||
99 | const char *tomoyo_path22keyword(const u8 operation) | ||
100 | { | 124 | { |
101 | return (operation < TOMOYO_MAX_PATH2_OPERATION) | 125 | if (buf->is_dir) |
102 | ? tomoyo_path2_keyword[operation] : NULL; | 126 | return; |
127 | /* | ||
128 | * This is OK because tomoyo_encode() reserves space for appending "/". | ||
129 | */ | ||
130 | strcat((char *) buf->name, "/"); | ||
131 | tomoyo_fill_path_info(buf); | ||
103 | } | 132 | } |
104 | 133 | ||
105 | /** | 134 | /** |
@@ -121,69 +150,134 @@ static bool tomoyo_strendswith(const char *name, const char *tail) | |||
121 | } | 150 | } |
122 | 151 | ||
123 | /** | 152 | /** |
124 | * tomoyo_get_path - Get realpath. | 153 | * tomoyo_get_realpath - Get realpath. |
125 | * | 154 | * |
155 | * @buf: Pointer to "struct tomoyo_path_info". | ||
126 | * @path: Pointer to "struct path". | 156 | * @path: Pointer to "struct path". |
127 | * | 157 | * |
128 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | 158 | * Returns true on success, false otherwise. |
129 | */ | 159 | */ |
130 | static struct tomoyo_path_info *tomoyo_get_path(struct path *path) | 160 | static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path) |
131 | { | 161 | { |
132 | int error; | 162 | buf->name = tomoyo_realpath_from_path(path); |
133 | struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf), | 163 | if (buf->name) { |
134 | GFP_NOFS); | 164 | tomoyo_fill_path_info(buf); |
135 | 165 | return true; | |
136 | if (!buf) | ||
137 | return NULL; | ||
138 | /* Reserve one byte for appending "/". */ | ||
139 | error = tomoyo_realpath_from_path2(path, buf->body, | ||
140 | sizeof(buf->body) - 2); | ||
141 | if (!error) { | ||
142 | buf->head.name = buf->body; | ||
143 | tomoyo_fill_path_info(&buf->head); | ||
144 | return &buf->head; | ||
145 | } | 166 | } |
146 | kfree(buf); | 167 | return false; |
147 | return NULL; | ||
148 | } | 168 | } |
149 | 169 | ||
150 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | 170 | /** |
151 | const char *filename2, | 171 | * tomoyo_audit_path_log - Audit path request log. |
152 | struct tomoyo_domain_info *const domain, | 172 | * |
153 | const bool is_delete); | 173 | * @r: Pointer to "struct tomoyo_request_info". |
154 | static int tomoyo_update_path_acl(const u8 type, const char *filename, | ||
155 | struct tomoyo_domain_info *const domain, | ||
156 | const bool is_delete); | ||
157 | |||
158 | /* | ||
159 | * tomoyo_globally_readable_list is used for holding list of pathnames which | ||
160 | * are by default allowed to be open()ed for reading by any process. | ||
161 | * | 174 | * |
162 | * An entry is added by | 175 | * Returns 0 on success, negative value otherwise. |
176 | */ | ||
177 | static int tomoyo_audit_path_log(struct tomoyo_request_info *r) | ||
178 | { | ||
179 | const char *operation = tomoyo_path_keyword[r->param.path.operation]; | ||
180 | const struct tomoyo_path_info *filename = r->param.path.filename; | ||
181 | if (r->granted) | ||
182 | return 0; | ||
183 | tomoyo_warn_log(r, "%s %s", operation, filename->name); | ||
184 | return tomoyo_supervisor(r, "allow_%s %s\n", operation, | ||
185 | tomoyo_pattern(filename)); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * tomoyo_audit_path2_log - Audit path/path request log. | ||
163 | * | 190 | * |
164 | * # echo 'allow_read /lib/libc-2.5.so' > \ | 191 | * @r: Pointer to "struct tomoyo_request_info". |
165 | * /sys/kernel/security/tomoyo/exception_policy | ||
166 | * | 192 | * |
167 | * and is deleted by | 193 | * Returns 0 on success, negative value otherwise. |
194 | */ | ||
195 | static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) | ||
196 | { | ||
197 | const char *operation = tomoyo_path2_keyword[r->param.path2.operation]; | ||
198 | const struct tomoyo_path_info *filename1 = r->param.path2.filename1; | ||
199 | const struct tomoyo_path_info *filename2 = r->param.path2.filename2; | ||
200 | if (r->granted) | ||
201 | return 0; | ||
202 | tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, | ||
203 | filename2->name); | ||
204 | return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, | ||
205 | tomoyo_pattern(filename1), | ||
206 | tomoyo_pattern(filename2)); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * tomoyo_audit_mkdev_log - Audit path/number/number/number request log. | ||
168 | * | 211 | * |
169 | * # echo 'delete allow_read /lib/libc-2.5.so' > \ | 212 | * @r: Pointer to "struct tomoyo_request_info". |
170 | * /sys/kernel/security/tomoyo/exception_policy | ||
171 | * | 213 | * |
172 | * and all entries are retrieved by | 214 | * Returns 0 on success, negative value otherwise. |
215 | */ | ||
216 | static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) | ||
217 | { | ||
218 | const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation]; | ||
219 | const struct tomoyo_path_info *filename = r->param.mkdev.filename; | ||
220 | const unsigned int major = r->param.mkdev.major; | ||
221 | const unsigned int minor = r->param.mkdev.minor; | ||
222 | const unsigned int mode = r->param.mkdev.mode; | ||
223 | if (r->granted) | ||
224 | return 0; | ||
225 | tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, | ||
226 | major, minor); | ||
227 | return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, | ||
228 | tomoyo_pattern(filename), mode, major, minor); | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * tomoyo_audit_path_number_log - Audit path/number request log. | ||
173 | * | 233 | * |
174 | * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy | 234 | * @r: Pointer to "struct tomoyo_request_info". |
235 | * @error: Error code. | ||
175 | * | 236 | * |
176 | * In the example above, any process is allowed to | 237 | * Returns 0 on success, negative value otherwise. |
177 | * open("/lib/libc-2.5.so", O_RDONLY). | ||
178 | * One exception is, if the domain which current process belongs to is marked | ||
179 | * as "ignore_global_allow_read", current process can't do so unless explicitly | ||
180 | * given "allow_read /lib/libc-2.5.so" to the domain which current process | ||
181 | * belongs to. | ||
182 | */ | 238 | */ |
183 | LIST_HEAD(tomoyo_globally_readable_list); | 239 | static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) |
240 | { | ||
241 | const u8 type = r->param.path_number.operation; | ||
242 | u8 radix; | ||
243 | const struct tomoyo_path_info *filename = r->param.path_number.filename; | ||
244 | const char *operation = tomoyo_path_number_keyword[type]; | ||
245 | char buffer[64]; | ||
246 | if (r->granted) | ||
247 | return 0; | ||
248 | switch (type) { | ||
249 | case TOMOYO_TYPE_CREATE: | ||
250 | case TOMOYO_TYPE_MKDIR: | ||
251 | case TOMOYO_TYPE_MKFIFO: | ||
252 | case TOMOYO_TYPE_MKSOCK: | ||
253 | case TOMOYO_TYPE_CHMOD: | ||
254 | radix = TOMOYO_VALUE_TYPE_OCTAL; | ||
255 | break; | ||
256 | case TOMOYO_TYPE_IOCTL: | ||
257 | radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; | ||
258 | break; | ||
259 | default: | ||
260 | radix = TOMOYO_VALUE_TYPE_DECIMAL; | ||
261 | break; | ||
262 | } | ||
263 | tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number, | ||
264 | radix); | ||
265 | tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); | ||
266 | return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, | ||
267 | tomoyo_pattern(filename), buffer); | ||
268 | } | ||
269 | |||
270 | static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, | ||
271 | const struct tomoyo_acl_head *b) | ||
272 | { | ||
273 | return container_of(a, struct tomoyo_readable_file, | ||
274 | head)->filename == | ||
275 | container_of(b, struct tomoyo_readable_file, | ||
276 | head)->filename; | ||
277 | } | ||
184 | 278 | ||
185 | /** | 279 | /** |
186 | * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. | 280 | * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list. |
187 | * | 281 | * |
188 | * @filename: Filename unconditionally permitted to open() for reading. | 282 | * @filename: Filename unconditionally permitted to open() for reading. |
189 | * @is_delete: True if it is a delete request. | 283 | * @is_delete: True if it is a delete request. |
@@ -195,41 +289,24 @@ LIST_HEAD(tomoyo_globally_readable_list); | |||
195 | static int tomoyo_update_globally_readable_entry(const char *filename, | 289 | static int tomoyo_update_globally_readable_entry(const char *filename, |
196 | const bool is_delete) | 290 | const bool is_delete) |
197 | { | 291 | { |
198 | struct tomoyo_globally_readable_file_entry *ptr; | 292 | struct tomoyo_readable_file e = { }; |
199 | struct tomoyo_globally_readable_file_entry e = { }; | 293 | int error; |
200 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
201 | 294 | ||
202 | if (!tomoyo_is_correct_path(filename, 1, 0, -1)) | 295 | if (!tomoyo_correct_word(filename)) |
203 | return -EINVAL; | 296 | return -EINVAL; |
204 | e.filename = tomoyo_get_name(filename); | 297 | e.filename = tomoyo_get_name(filename); |
205 | if (!e.filename) | 298 | if (!e.filename) |
206 | return -ENOMEM; | 299 | return -ENOMEM; |
207 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 300 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
208 | goto out; | 301 | &tomoyo_policy_list |
209 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { | 302 | [TOMOYO_ID_GLOBALLY_READABLE], |
210 | if (ptr->filename != e.filename) | 303 | tomoyo_same_globally_readable); |
211 | continue; | ||
212 | ptr->is_deleted = is_delete; | ||
213 | error = 0; | ||
214 | break; | ||
215 | } | ||
216 | if (!is_delete && error) { | ||
217 | struct tomoyo_globally_readable_file_entry *entry = | ||
218 | tomoyo_commit_ok(&e, sizeof(e)); | ||
219 | if (entry) { | ||
220 | list_add_tail_rcu(&entry->list, | ||
221 | &tomoyo_globally_readable_list); | ||
222 | error = 0; | ||
223 | } | ||
224 | } | ||
225 | mutex_unlock(&tomoyo_policy_lock); | ||
226 | out: | ||
227 | tomoyo_put_name(e.filename); | 304 | tomoyo_put_name(e.filename); |
228 | return error; | 305 | return error; |
229 | } | 306 | } |
230 | 307 | ||
231 | /** | 308 | /** |
232 | * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. | 309 | * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. |
233 | * | 310 | * |
234 | * @filename: The filename to check. | 311 | * @filename: The filename to check. |
235 | * | 312 | * |
@@ -237,14 +314,15 @@ static int tomoyo_update_globally_readable_entry(const char *filename, | |||
237 | * | 314 | * |
238 | * Caller holds tomoyo_read_lock(). | 315 | * Caller holds tomoyo_read_lock(). |
239 | */ | 316 | */ |
240 | static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | 317 | static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * |
241 | filename) | 318 | filename) |
242 | { | 319 | { |
243 | struct tomoyo_globally_readable_file_entry *ptr; | 320 | struct tomoyo_readable_file *ptr; |
244 | bool found = false; | 321 | bool found = false; |
245 | 322 | ||
246 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { | 323 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list |
247 | if (!ptr->is_deleted && | 324 | [TOMOYO_ID_GLOBALLY_READABLE], head.list) { |
325 | if (!ptr->head.is_deleted && | ||
248 | tomoyo_path_matches_pattern(filename, ptr->filename)) { | 326 | tomoyo_path_matches_pattern(filename, ptr->filename)) { |
249 | found = true; | 327 | found = true; |
250 | break; | 328 | break; |
@@ -254,7 +332,7 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | |||
254 | } | 332 | } |
255 | 333 | ||
256 | /** | 334 | /** |
257 | * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list. | 335 | * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list. |
258 | * | 336 | * |
259 | * @data: String to parse. | 337 | * @data: String to parse. |
260 | * @is_delete: True if it is a delete request. | 338 | * @is_delete: True if it is a delete request. |
@@ -263,74 +341,20 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * | |||
263 | * | 341 | * |
264 | * Caller holds tomoyo_read_lock(). | 342 | * Caller holds tomoyo_read_lock(). |
265 | */ | 343 | */ |
266 | int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) | 344 | int tomoyo_write_globally_readable(char *data, const bool is_delete) |
267 | { | 345 | { |
268 | return tomoyo_update_globally_readable_entry(data, is_delete); | 346 | return tomoyo_update_globally_readable_entry(data, is_delete); |
269 | } | 347 | } |
270 | 348 | ||
271 | /** | 349 | static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, |
272 | * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list. | 350 | const struct tomoyo_acl_head *b) |
273 | * | ||
274 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
275 | * | ||
276 | * Returns true on success, false otherwise. | ||
277 | * | ||
278 | * Caller holds tomoyo_read_lock(). | ||
279 | */ | ||
280 | bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) | ||
281 | { | 351 | { |
282 | struct list_head *pos; | 352 | return container_of(a, struct tomoyo_no_pattern, head)->pattern == |
283 | bool done = true; | 353 | container_of(b, struct tomoyo_no_pattern, head)->pattern; |
284 | |||
285 | list_for_each_cookie(pos, head->read_var2, | ||
286 | &tomoyo_globally_readable_list) { | ||
287 | struct tomoyo_globally_readable_file_entry *ptr; | ||
288 | ptr = list_entry(pos, | ||
289 | struct tomoyo_globally_readable_file_entry, | ||
290 | list); | ||
291 | if (ptr->is_deleted) | ||
292 | continue; | ||
293 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n", | ||
294 | ptr->filename->name); | ||
295 | if (!done) | ||
296 | break; | ||
297 | } | ||
298 | return done; | ||
299 | } | 354 | } |
300 | 355 | ||
301 | /* tomoyo_pattern_list is used for holding list of pathnames which are used for | ||
302 | * converting pathnames to pathname patterns during learning mode. | ||
303 | * | ||
304 | * An entry is added by | ||
305 | * | ||
306 | * # echo 'file_pattern /proc/\$/mounts' > \ | ||
307 | * /sys/kernel/security/tomoyo/exception_policy | ||
308 | * | ||
309 | * and is deleted by | ||
310 | * | ||
311 | * # echo 'delete file_pattern /proc/\$/mounts' > \ | ||
312 | * /sys/kernel/security/tomoyo/exception_policy | ||
313 | * | ||
314 | * and all entries are retrieved by | ||
315 | * | ||
316 | * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy | ||
317 | * | ||
318 | * In the example above, if a process which belongs to a domain which is in | ||
319 | * learning mode requested open("/proc/1/mounts", O_RDONLY), | ||
320 | * "allow_read /proc/\$/mounts" is automatically added to the domain which that | ||
321 | * process belongs to. | ||
322 | * | ||
323 | * It is not a desirable behavior that we have to use /proc/\$/ instead of | ||
324 | * /proc/self/ when current process needs to access only current process's | ||
325 | * information. As of now, LSM version of TOMOYO is using __d_path() for | ||
326 | * calculating pathname. Non LSM version of TOMOYO is using its own function | ||
327 | * which pretends as if /proc/self/ is not a symlink; so that we can forbid | ||
328 | * current process from accessing other process's information. | ||
329 | */ | ||
330 | LIST_HEAD(tomoyo_pattern_list); | ||
331 | |||
332 | /** | 356 | /** |
333 | * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. | 357 | * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list. |
334 | * | 358 | * |
335 | * @pattern: Pathname pattern. | 359 | * @pattern: Pathname pattern. |
336 | * @is_delete: True if it is a delete request. | 360 | * @is_delete: True if it is a delete request. |
@@ -342,39 +366,23 @@ LIST_HEAD(tomoyo_pattern_list); | |||
342 | static int tomoyo_update_file_pattern_entry(const char *pattern, | 366 | static int tomoyo_update_file_pattern_entry(const char *pattern, |
343 | const bool is_delete) | 367 | const bool is_delete) |
344 | { | 368 | { |
345 | struct tomoyo_pattern_entry *ptr; | 369 | struct tomoyo_no_pattern e = { }; |
346 | struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) }; | 370 | int error; |
347 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
348 | 371 | ||
372 | if (!tomoyo_correct_word(pattern)) | ||
373 | return -EINVAL; | ||
374 | e.pattern = tomoyo_get_name(pattern); | ||
349 | if (!e.pattern) | 375 | if (!e.pattern) |
350 | return error; | 376 | return -ENOMEM; |
351 | if (!e.pattern->is_patterned) | 377 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
352 | goto out; | 378 | &tomoyo_policy_list[TOMOYO_ID_PATTERN], |
353 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 379 | tomoyo_same_pattern); |
354 | goto out; | ||
355 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | ||
356 | if (e.pattern != ptr->pattern) | ||
357 | continue; | ||
358 | ptr->is_deleted = is_delete; | ||
359 | error = 0; | ||
360 | break; | ||
361 | } | ||
362 | if (!is_delete && error) { | ||
363 | struct tomoyo_pattern_entry *entry = | ||
364 | tomoyo_commit_ok(&e, sizeof(e)); | ||
365 | if (entry) { | ||
366 | list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); | ||
367 | error = 0; | ||
368 | } | ||
369 | } | ||
370 | mutex_unlock(&tomoyo_policy_lock); | ||
371 | out: | ||
372 | tomoyo_put_name(e.pattern); | 380 | tomoyo_put_name(e.pattern); |
373 | return error; | 381 | return error; |
374 | } | 382 | } |
375 | 383 | ||
376 | /** | 384 | /** |
377 | * tomoyo_get_file_pattern - Get patterned pathname. | 385 | * tomoyo_pattern - Get patterned pathname. |
378 | * | 386 | * |
379 | * @filename: The filename to find patterned pathname. | 387 | * @filename: The filename to find patterned pathname. |
380 | * | 388 | * |
@@ -382,14 +390,14 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, | |||
382 | * | 390 | * |
383 | * Caller holds tomoyo_read_lock(). | 391 | * Caller holds tomoyo_read_lock(). |
384 | */ | 392 | */ |
385 | static const struct tomoyo_path_info * | 393 | const char *tomoyo_pattern(const struct tomoyo_path_info *filename) |
386 | tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | ||
387 | { | 394 | { |
388 | struct tomoyo_pattern_entry *ptr; | 395 | struct tomoyo_no_pattern *ptr; |
389 | const struct tomoyo_path_info *pattern = NULL; | 396 | const struct tomoyo_path_info *pattern = NULL; |
390 | 397 | ||
391 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | 398 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN], |
392 | if (ptr->is_deleted) | 399 | head.list) { |
400 | if (ptr->head.is_deleted) | ||
393 | continue; | 401 | continue; |
394 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) | 402 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) |
395 | continue; | 403 | continue; |
@@ -403,11 +411,11 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | |||
403 | } | 411 | } |
404 | if (pattern) | 412 | if (pattern) |
405 | filename = pattern; | 413 | filename = pattern; |
406 | return filename; | 414 | return filename->name; |
407 | } | 415 | } |
408 | 416 | ||
409 | /** | 417 | /** |
410 | * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list. | 418 | * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list. |
411 | * | 419 | * |
412 | * @data: String to parse. | 420 | * @data: String to parse. |
413 | * @is_delete: True if it is a delete request. | 421 | * @is_delete: True if it is a delete request. |
@@ -416,71 +424,21 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) | |||
416 | * | 424 | * |
417 | * Caller holds tomoyo_read_lock(). | 425 | * Caller holds tomoyo_read_lock(). |
418 | */ | 426 | */ |
419 | int tomoyo_write_pattern_policy(char *data, const bool is_delete) | 427 | int tomoyo_write_pattern(char *data, const bool is_delete) |
420 | { | 428 | { |
421 | return tomoyo_update_file_pattern_entry(data, is_delete); | 429 | return tomoyo_update_file_pattern_entry(data, is_delete); |
422 | } | 430 | } |
423 | 431 | ||
424 | /** | 432 | static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, |
425 | * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list. | 433 | const struct tomoyo_acl_head *b) |
426 | * | ||
427 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
428 | * | ||
429 | * Returns true on success, false otherwise. | ||
430 | * | ||
431 | * Caller holds tomoyo_read_lock(). | ||
432 | */ | ||
433 | bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) | ||
434 | { | 434 | { |
435 | struct list_head *pos; | 435 | return container_of(a, struct tomoyo_no_rewrite, head)->pattern |
436 | bool done = true; | 436 | == container_of(b, struct tomoyo_no_rewrite, head) |
437 | 437 | ->pattern; | |
438 | list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { | ||
439 | struct tomoyo_pattern_entry *ptr; | ||
440 | ptr = list_entry(pos, struct tomoyo_pattern_entry, list); | ||
441 | if (ptr->is_deleted) | ||
442 | continue; | ||
443 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN | ||
444 | "%s\n", ptr->pattern->name); | ||
445 | if (!done) | ||
446 | break; | ||
447 | } | ||
448 | return done; | ||
449 | } | 438 | } |
450 | 439 | ||
451 | /* | ||
452 | * tomoyo_no_rewrite_list is used for holding list of pathnames which are by | ||
453 | * default forbidden to modify already written content of a file. | ||
454 | * | ||
455 | * An entry is added by | ||
456 | * | ||
457 | * # echo 'deny_rewrite /var/log/messages' > \ | ||
458 | * /sys/kernel/security/tomoyo/exception_policy | ||
459 | * | ||
460 | * and is deleted by | ||
461 | * | ||
462 | * # echo 'delete deny_rewrite /var/log/messages' > \ | ||
463 | * /sys/kernel/security/tomoyo/exception_policy | ||
464 | * | ||
465 | * and all entries are retrieved by | ||
466 | * | ||
467 | * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy | ||
468 | * | ||
469 | * In the example above, if a process requested to rewrite /var/log/messages , | ||
470 | * the process can't rewrite unless the domain which that process belongs to | ||
471 | * has "allow_rewrite /var/log/messages" entry. | ||
472 | * | ||
473 | * It is not a desirable behavior that we have to add "\040(deleted)" suffix | ||
474 | * when we want to allow rewriting already unlink()ed file. As of now, | ||
475 | * LSM version of TOMOYO is using __d_path() for calculating pathname. | ||
476 | * Non LSM version of TOMOYO is using its own function which doesn't append | ||
477 | * " (deleted)" suffix if the file is already unlink()ed; so that we don't | ||
478 | * need to worry whether the file is already unlink()ed or not. | ||
479 | */ | ||
480 | LIST_HEAD(tomoyo_no_rewrite_list); | ||
481 | |||
482 | /** | 440 | /** |
483 | * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. | 441 | * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list. |
484 | * | 442 | * |
485 | * @pattern: Pathname pattern that are not rewritable by default. | 443 | * @pattern: Pathname pattern that are not rewritable by default. |
486 | * @is_delete: True if it is a delete request. | 444 | * @is_delete: True if it is a delete request. |
@@ -492,41 +450,23 @@ LIST_HEAD(tomoyo_no_rewrite_list); | |||
492 | static int tomoyo_update_no_rewrite_entry(const char *pattern, | 450 | static int tomoyo_update_no_rewrite_entry(const char *pattern, |
493 | const bool is_delete) | 451 | const bool is_delete) |
494 | { | 452 | { |
495 | struct tomoyo_no_rewrite_entry *ptr; | 453 | struct tomoyo_no_rewrite e = { }; |
496 | struct tomoyo_no_rewrite_entry e = { }; | 454 | int error; |
497 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
498 | 455 | ||
499 | if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) | 456 | if (!tomoyo_correct_word(pattern)) |
500 | return -EINVAL; | 457 | return -EINVAL; |
501 | e.pattern = tomoyo_get_name(pattern); | 458 | e.pattern = tomoyo_get_name(pattern); |
502 | if (!e.pattern) | 459 | if (!e.pattern) |
503 | return error; | 460 | return -ENOMEM; |
504 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 461 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, |
505 | goto out; | 462 | &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], |
506 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | 463 | tomoyo_same_no_rewrite); |
507 | if (ptr->pattern != e.pattern) | ||
508 | continue; | ||
509 | ptr->is_deleted = is_delete; | ||
510 | error = 0; | ||
511 | break; | ||
512 | } | ||
513 | if (!is_delete && error) { | ||
514 | struct tomoyo_no_rewrite_entry *entry = | ||
515 | tomoyo_commit_ok(&e, sizeof(e)); | ||
516 | if (entry) { | ||
517 | list_add_tail_rcu(&entry->list, | ||
518 | &tomoyo_no_rewrite_list); | ||
519 | error = 0; | ||
520 | } | ||
521 | } | ||
522 | mutex_unlock(&tomoyo_policy_lock); | ||
523 | out: | ||
524 | tomoyo_put_name(e.pattern); | 464 | tomoyo_put_name(e.pattern); |
525 | return error; | 465 | return error; |
526 | } | 466 | } |
527 | 467 | ||
528 | /** | 468 | /** |
529 | * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. | 469 | * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. |
530 | * | 470 | * |
531 | * @filename: Filename to check. | 471 | * @filename: Filename to check. |
532 | * | 472 | * |
@@ -535,13 +475,14 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, | |||
535 | * | 475 | * |
536 | * Caller holds tomoyo_read_lock(). | 476 | * Caller holds tomoyo_read_lock(). |
537 | */ | 477 | */ |
538 | static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | 478 | static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) |
539 | { | 479 | { |
540 | struct tomoyo_no_rewrite_entry *ptr; | 480 | struct tomoyo_no_rewrite *ptr; |
541 | bool found = false; | 481 | bool found = false; |
542 | 482 | ||
543 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | 483 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], |
544 | if (ptr->is_deleted) | 484 | head.list) { |
485 | if (ptr->head.is_deleted) | ||
545 | continue; | 486 | continue; |
546 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) | 487 | if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) |
547 | continue; | 488 | continue; |
@@ -552,7 +493,7 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | |||
552 | } | 493 | } |
553 | 494 | ||
554 | /** | 495 | /** |
555 | * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list. | 496 | * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list. |
556 | * | 497 | * |
557 | * @data: String to parse. | 498 | * @data: String to parse. |
558 | * @is_delete: True if it is a delete request. | 499 | * @is_delete: True if it is a delete request. |
@@ -561,214 +502,103 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) | |||
561 | * | 502 | * |
562 | * Caller holds tomoyo_read_lock(). | 503 | * Caller holds tomoyo_read_lock(). |
563 | */ | 504 | */ |
564 | int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) | 505 | int tomoyo_write_no_rewrite(char *data, const bool is_delete) |
565 | { | 506 | { |
566 | return tomoyo_update_no_rewrite_entry(data, is_delete); | 507 | return tomoyo_update_no_rewrite_entry(data, is_delete); |
567 | } | 508 | } |
568 | 509 | ||
569 | /** | 510 | static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, |
570 | * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list. | 511 | const struct tomoyo_acl_info *ptr) |
571 | * | ||
572 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
573 | * | ||
574 | * Returns true on success, false otherwise. | ||
575 | * | ||
576 | * Caller holds tomoyo_read_lock(). | ||
577 | */ | ||
578 | bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) | ||
579 | { | 512 | { |
580 | struct list_head *pos; | 513 | const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), |
581 | bool done = true; | 514 | head); |
582 | 515 | if (acl->perm & (1 << r->param.path.operation)) { | |
583 | list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { | 516 | r->param.path.matched_path = |
584 | struct tomoyo_no_rewrite_entry *ptr; | 517 | tomoyo_compare_name_union(r->param.path.filename, |
585 | ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); | 518 | &acl->name); |
586 | if (ptr->is_deleted) | 519 | return r->param.path.matched_path != NULL; |
587 | continue; | ||
588 | done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE | ||
589 | "%s\n", ptr->pattern->name); | ||
590 | if (!done) | ||
591 | break; | ||
592 | } | 520 | } |
593 | return done; | 521 | return false; |
594 | } | 522 | } |
595 | 523 | ||
596 | /** | 524 | static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, |
597 | * tomoyo_update_file_acl - Update file's read/write/execute ACL. | 525 | const struct tomoyo_acl_info *ptr) |
598 | * | ||
599 | * @filename: Filename. | ||
600 | * @perm: Permission (between 1 to 7). | ||
601 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
602 | * @is_delete: True if it is a delete request. | ||
603 | * | ||
604 | * Returns 0 on success, negative value otherwise. | ||
605 | * | ||
606 | * This is legacy support interface for older policy syntax. | ||
607 | * Current policy syntax uses "allow_read/write" instead of "6", | ||
608 | * "allow_read" instead of "4", "allow_write" instead of "2", | ||
609 | * "allow_execute" instead of "1". | ||
610 | * | ||
611 | * Caller holds tomoyo_read_lock(). | ||
612 | */ | ||
613 | static int tomoyo_update_file_acl(const char *filename, u8 perm, | ||
614 | struct tomoyo_domain_info * const domain, | ||
615 | const bool is_delete) | ||
616 | { | 526 | { |
617 | if (perm > 7 || !perm) { | 527 | const struct tomoyo_path_number_acl *acl = |
618 | printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", | 528 | container_of(ptr, typeof(*acl), head); |
619 | __func__, perm, filename); | 529 | return (acl->perm & (1 << r->param.path_number.operation)) && |
620 | return -EINVAL; | 530 | tomoyo_compare_number_union(r->param.path_number.number, |
621 | } | 531 | &acl->number) && |
622 | if (filename[0] != '@' && tomoyo_strendswith(filename, "/")) | 532 | tomoyo_compare_name_union(r->param.path_number.filename, |
623 | /* | 533 | &acl->name); |
624 | * Only 'allow_mkdir' and 'allow_rmdir' are valid for | ||
625 | * directory permissions. | ||
626 | */ | ||
627 | return 0; | ||
628 | if (perm & 4) | ||
629 | tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain, | ||
630 | is_delete); | ||
631 | if (perm & 2) | ||
632 | tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain, | ||
633 | is_delete); | ||
634 | if (perm & 1) | ||
635 | tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain, | ||
636 | is_delete); | ||
637 | return 0; | ||
638 | } | 534 | } |
639 | 535 | ||
640 | /** | 536 | static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, |
641 | * tomoyo_path_acl2 - Check permission for single path operation. | 537 | const struct tomoyo_acl_info *ptr) |
642 | * | ||
643 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
644 | * @filename: Filename to check. | ||
645 | * @perm: Permission. | ||
646 | * @may_use_pattern: True if patterned ACL is permitted. | ||
647 | * | ||
648 | * Returns 0 on success, -EPERM otherwise. | ||
649 | * | ||
650 | * Caller holds tomoyo_read_lock(). | ||
651 | */ | ||
652 | static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, | ||
653 | const struct tomoyo_path_info *filename, | ||
654 | const u32 perm, const bool may_use_pattern) | ||
655 | { | 538 | { |
656 | struct tomoyo_acl_info *ptr; | 539 | const struct tomoyo_path2_acl *acl = |
657 | int error = -EPERM; | 540 | container_of(ptr, typeof(*acl), head); |
658 | 541 | return (acl->perm & (1 << r->param.path2.operation)) && | |
659 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 542 | tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1) |
660 | struct tomoyo_path_acl *acl; | 543 | && tomoyo_compare_name_union(r->param.path2.filename2, |
661 | if (ptr->type != TOMOYO_TYPE_PATH_ACL) | 544 | &acl->name2); |
662 | continue; | ||
663 | acl = container_of(ptr, struct tomoyo_path_acl, head); | ||
664 | if (perm <= 0xFFFF) { | ||
665 | if (!(acl->perm & perm)) | ||
666 | continue; | ||
667 | } else { | ||
668 | if (!(acl->perm_high & (perm >> 16))) | ||
669 | continue; | ||
670 | } | ||
671 | if (!tomoyo_compare_name_union_pattern(filename, &acl->name, | ||
672 | may_use_pattern)) | ||
673 | continue; | ||
674 | error = 0; | ||
675 | break; | ||
676 | } | ||
677 | return error; | ||
678 | } | 545 | } |
679 | 546 | ||
680 | /** | 547 | static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, |
681 | * tomoyo_check_file_acl - Check permission for opening files. | 548 | const struct tomoyo_acl_info *ptr) |
682 | * | ||
683 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
684 | * @filename: Filename to check. | ||
685 | * @operation: Mode ("read" or "write" or "read/write" or "execute"). | ||
686 | * | ||
687 | * Returns 0 on success, -EPERM otherwise. | ||
688 | * | ||
689 | * Caller holds tomoyo_read_lock(). | ||
690 | */ | ||
691 | static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain, | ||
692 | const struct tomoyo_path_info *filename, | ||
693 | const u8 operation) | ||
694 | { | 549 | { |
695 | u32 perm = 0; | 550 | const struct tomoyo_mkdev_acl *acl = |
696 | 551 | container_of(ptr, typeof(*acl), head); | |
697 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | 552 | return (acl->perm & (1 << r->param.mkdev.operation)) && |
698 | return 0; | 553 | tomoyo_compare_number_union(r->param.mkdev.mode, |
699 | if (operation == 6) | 554 | &acl->mode) && |
700 | perm = 1 << TOMOYO_TYPE_READ_WRITE; | 555 | tomoyo_compare_number_union(r->param.mkdev.major, |
701 | else if (operation == 4) | 556 | &acl->major) && |
702 | perm = 1 << TOMOYO_TYPE_READ; | 557 | tomoyo_compare_number_union(r->param.mkdev.minor, |
703 | else if (operation == 2) | 558 | &acl->minor) && |
704 | perm = 1 << TOMOYO_TYPE_WRITE; | 559 | tomoyo_compare_name_union(r->param.mkdev.filename, |
705 | else if (operation == 1) | 560 | &acl->name); |
706 | perm = 1 << TOMOYO_TYPE_EXECUTE; | ||
707 | else | ||
708 | BUG(); | ||
709 | return tomoyo_path_acl2(domain, filename, perm, operation != 1); | ||
710 | } | 561 | } |
711 | 562 | ||
712 | /** | 563 | static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, |
713 | * tomoyo_check_file_perm2 - Check permission for opening files. | 564 | const struct tomoyo_acl_info *b) |
714 | * | ||
715 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
716 | * @filename: Filename to check. | ||
717 | * @perm: Mode ("read" or "write" or "read/write" or "execute"). | ||
718 | * @operation: Operation name passed used for verbose mode. | ||
719 | * @mode: Access control mode. | ||
720 | * | ||
721 | * Returns 0 on success, negative value otherwise. | ||
722 | * | ||
723 | * Caller holds tomoyo_read_lock(). | ||
724 | */ | ||
725 | static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, | ||
726 | const struct tomoyo_path_info *filename, | ||
727 | const u8 perm, const char *operation, | ||
728 | const u8 mode) | ||
729 | { | 565 | { |
730 | const bool is_enforce = (mode == 3); | 566 | const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); |
731 | const char *msg = "<unknown>"; | 567 | const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); |
732 | int error = 0; | 568 | return tomoyo_same_acl_head(&p1->head, &p2->head) && |
569 | tomoyo_same_name_union(&p1->name, &p2->name); | ||
570 | } | ||
733 | 571 | ||
734 | if (!filename) | 572 | static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, |
735 | return 0; | 573 | struct tomoyo_acl_info *b, |
736 | error = tomoyo_check_file_acl(domain, filename, perm); | 574 | const bool is_delete) |
737 | if (error && perm == 4 && !domain->ignore_global_allow_read | 575 | { |
738 | && tomoyo_is_globally_readable_file(filename)) | 576 | u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head) |
739 | error = 0; | 577 | ->perm; |
740 | if (perm == 6) | 578 | u16 perm = *a_perm; |
741 | msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); | 579 | const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; |
742 | else if (perm == 4) | 580 | if (is_delete) { |
743 | msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); | 581 | perm &= ~b_perm; |
744 | else if (perm == 2) | 582 | if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK) |
745 | msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); | 583 | perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); |
746 | else if (perm == 1) | 584 | else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE))) |
747 | msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); | 585 | perm &= ~TOMOYO_RW_MASK; |
748 | else | 586 | } else { |
749 | BUG(); | 587 | perm |= b_perm; |
750 | if (!error) | 588 | if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK) |
751 | return 0; | 589 | perm |= (1 << TOMOYO_TYPE_READ_WRITE); |
752 | if (tomoyo_verbose_mode(domain)) | 590 | else if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) |
753 | printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied " | 591 | perm |= TOMOYO_RW_MASK; |
754 | "for %s\n", tomoyo_get_msg(is_enforce), msg, operation, | ||
755 | filename->name, tomoyo_get_last_name(domain)); | ||
756 | if (is_enforce) | ||
757 | return error; | ||
758 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | ||
759 | /* Don't use patterns for execute permission. */ | ||
760 | const struct tomoyo_path_info *patterned_file = (perm != 1) ? | ||
761 | tomoyo_get_file_pattern(filename) : filename; | ||
762 | tomoyo_update_file_acl(patterned_file->name, perm, | ||
763 | domain, false); | ||
764 | } | 592 | } |
765 | return 0; | 593 | *a_perm = perm; |
594 | return !perm; | ||
766 | } | 595 | } |
767 | 596 | ||
768 | /** | 597 | /** |
769 | * tomoyo_write_file_policy - Update file related list. | 598 | * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. |
770 | * | 599 | * |
771 | * @data: String to parse. | 600 | * @type: Type of operation. |
601 | * @filename: Filename. | ||
772 | * @domain: Pointer to "struct tomoyo_domain_info". | 602 | * @domain: Pointer to "struct tomoyo_domain_info". |
773 | * @is_delete: True if it is a delete request. | 603 | * @is_delete: True if it is a delete request. |
774 | * | 604 | * |
@@ -776,48 +606,65 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, | |||
776 | * | 606 | * |
777 | * Caller holds tomoyo_read_lock(). | 607 | * Caller holds tomoyo_read_lock(). |
778 | */ | 608 | */ |
779 | int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | 609 | static int tomoyo_update_path_acl(const u8 type, const char *filename, |
780 | const bool is_delete) | 610 | struct tomoyo_domain_info * const domain, |
611 | const bool is_delete) | ||
781 | { | 612 | { |
782 | char *filename = strchr(data, ' '); | 613 | struct tomoyo_path_acl e = { |
783 | char *filename2; | 614 | .head.type = TOMOYO_TYPE_PATH_ACL, |
784 | unsigned int perm; | 615 | .perm = 1 << type |
785 | u8 type; | 616 | }; |
786 | 617 | int error; | |
787 | if (!filename) | 618 | if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE)) |
619 | e.perm |= TOMOYO_RW_MASK; | ||
620 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
788 | return -EINVAL; | 621 | return -EINVAL; |
789 | *filename++ = '\0'; | 622 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
790 | if (sscanf(data, "%u", &perm) == 1) | 623 | tomoyo_same_path_acl, |
791 | return tomoyo_update_file_acl(filename, (u8) perm, domain, | 624 | tomoyo_merge_path_acl); |
792 | is_delete); | 625 | tomoyo_put_name_union(&e.name); |
793 | if (strncmp(data, "allow_", 6)) | 626 | return error; |
794 | goto out; | 627 | } |
795 | data += 6; | 628 | |
796 | for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { | 629 | static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, |
797 | if (strcmp(data, tomoyo_path_keyword[type])) | 630 | const struct tomoyo_acl_info *b) |
798 | continue; | 631 | { |
799 | return tomoyo_update_path_acl(type, filename, domain, | 632 | const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), |
800 | is_delete); | 633 | head); |
801 | } | 634 | const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), |
802 | filename2 = strchr(filename, ' '); | 635 | head); |
803 | if (!filename2) | 636 | return tomoyo_same_acl_head(&p1->head, &p2->head) |
804 | goto out; | 637 | && tomoyo_same_name_union(&p1->name, &p2->name) |
805 | *filename2++ = '\0'; | 638 | && tomoyo_same_number_union(&p1->mode, &p2->mode) |
806 | for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { | 639 | && tomoyo_same_number_union(&p1->major, &p2->major) |
807 | if (strcmp(data, tomoyo_path2_keyword[type])) | 640 | && tomoyo_same_number_union(&p1->minor, &p2->minor); |
808 | continue; | 641 | } |
809 | return tomoyo_update_path2_acl(type, filename, filename2, | 642 | |
810 | domain, is_delete); | 643 | static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, |
811 | } | 644 | struct tomoyo_acl_info *b, |
812 | out: | 645 | const bool is_delete) |
813 | return -EINVAL; | 646 | { |
647 | u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl, | ||
648 | head)->perm; | ||
649 | u8 perm = *a_perm; | ||
650 | const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head) | ||
651 | ->perm; | ||
652 | if (is_delete) | ||
653 | perm &= ~b_perm; | ||
654 | else | ||
655 | perm |= b_perm; | ||
656 | *a_perm = perm; | ||
657 | return !perm; | ||
814 | } | 658 | } |
815 | 659 | ||
816 | /** | 660 | /** |
817 | * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. | 661 | * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list. |
818 | * | 662 | * |
819 | * @type: Type of operation. | 663 | * @type: Type of operation. |
820 | * @filename: Filename. | 664 | * @filename: Filename. |
665 | * @mode: Create mode. | ||
666 | * @major: Device major number. | ||
667 | * @minor: Device minor number. | ||
821 | * @domain: Pointer to "struct tomoyo_domain_info". | 668 | * @domain: Pointer to "struct tomoyo_domain_info". |
822 | * @is_delete: True if it is a delete request. | 669 | * @is_delete: True if it is a delete request. |
823 | * | 670 | * |
@@ -825,71 +672,58 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, | |||
825 | * | 672 | * |
826 | * Caller holds tomoyo_read_lock(). | 673 | * Caller holds tomoyo_read_lock(). |
827 | */ | 674 | */ |
828 | static int tomoyo_update_path_acl(const u8 type, const char *filename, | 675 | static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, |
829 | struct tomoyo_domain_info *const domain, | 676 | char *mode, char *major, char *minor, |
830 | const bool is_delete) | 677 | struct tomoyo_domain_info * const |
678 | domain, const bool is_delete) | ||
831 | { | 679 | { |
832 | static const u32 tomoyo_rw_mask = | 680 | struct tomoyo_mkdev_acl e = { |
833 | (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); | 681 | .head.type = TOMOYO_TYPE_MKDEV_ACL, |
834 | const u32 perm = 1 << type; | 682 | .perm = 1 << type |
835 | struct tomoyo_acl_info *ptr; | ||
836 | struct tomoyo_path_acl e = { | ||
837 | .head.type = TOMOYO_TYPE_PATH_ACL, | ||
838 | .perm_high = perm >> 16, | ||
839 | .perm = perm | ||
840 | }; | 683 | }; |
841 | int error = is_delete ? -ENOENT : -ENOMEM; | 684 | int error = is_delete ? -ENOENT : -ENOMEM; |
842 | 685 | if (!tomoyo_parse_name_union(filename, &e.name) || | |
843 | if (type == TOMOYO_TYPE_READ_WRITE) | 686 | !tomoyo_parse_number_union(mode, &e.mode) || |
844 | e.perm |= tomoyo_rw_mask; | 687 | !tomoyo_parse_number_union(major, &e.major) || |
845 | if (!domain) | 688 | !tomoyo_parse_number_union(minor, &e.minor)) |
846 | return -EINVAL; | ||
847 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
848 | return -EINVAL; | ||
849 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
850 | goto out; | 689 | goto out; |
851 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 690 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
852 | struct tomoyo_path_acl *acl = | 691 | tomoyo_same_mkdev_acl, |
853 | container_of(ptr, struct tomoyo_path_acl, head); | 692 | tomoyo_merge_mkdev_acl); |
854 | if (!tomoyo_is_same_path_acl(acl, &e)) | ||
855 | continue; | ||
856 | if (is_delete) { | ||
857 | if (perm <= 0xFFFF) | ||
858 | acl->perm &= ~perm; | ||
859 | else | ||
860 | acl->perm_high &= ~(perm >> 16); | ||
861 | if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) | ||
862 | acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); | ||
863 | else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) | ||
864 | acl->perm &= ~tomoyo_rw_mask; | ||
865 | } else { | ||
866 | if (perm <= 0xFFFF) | ||
867 | acl->perm |= perm; | ||
868 | else | ||
869 | acl->perm_high |= (perm >> 16); | ||
870 | if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) | ||
871 | acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; | ||
872 | else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
873 | acl->perm |= tomoyo_rw_mask; | ||
874 | } | ||
875 | error = 0; | ||
876 | break; | ||
877 | } | ||
878 | if (!is_delete && error) { | ||
879 | struct tomoyo_path_acl *entry = | ||
880 | tomoyo_commit_ok(&e, sizeof(e)); | ||
881 | if (entry) { | ||
882 | list_add_tail_rcu(&entry->head.list, | ||
883 | &domain->acl_info_list); | ||
884 | error = 0; | ||
885 | } | ||
886 | } | ||
887 | mutex_unlock(&tomoyo_policy_lock); | ||
888 | out: | 693 | out: |
889 | tomoyo_put_name_union(&e.name); | 694 | tomoyo_put_name_union(&e.name); |
695 | tomoyo_put_number_union(&e.mode); | ||
696 | tomoyo_put_number_union(&e.major); | ||
697 | tomoyo_put_number_union(&e.minor); | ||
890 | return error; | 698 | return error; |
891 | } | 699 | } |
892 | 700 | ||
701 | static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, | ||
702 | const struct tomoyo_acl_info *b) | ||
703 | { | ||
704 | const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); | ||
705 | const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); | ||
706 | return tomoyo_same_acl_head(&p1->head, &p2->head) | ||
707 | && tomoyo_same_name_union(&p1->name1, &p2->name1) | ||
708 | && tomoyo_same_name_union(&p1->name2, &p2->name2); | ||
709 | } | ||
710 | |||
711 | static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, | ||
712 | struct tomoyo_acl_info *b, | ||
713 | const bool is_delete) | ||
714 | { | ||
715 | u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head) | ||
716 | ->perm; | ||
717 | u8 perm = *a_perm; | ||
718 | const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm; | ||
719 | if (is_delete) | ||
720 | perm &= ~b_perm; | ||
721 | else | ||
722 | perm |= b_perm; | ||
723 | *a_perm = perm; | ||
724 | return !perm; | ||
725 | } | ||
726 | |||
893 | /** | 727 | /** |
894 | * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. | 728 | * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. |
895 | * | 729 | * |
@@ -905,46 +739,20 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, | |||
905 | */ | 739 | */ |
906 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | 740 | static int tomoyo_update_path2_acl(const u8 type, const char *filename1, |
907 | const char *filename2, | 741 | const char *filename2, |
908 | struct tomoyo_domain_info *const domain, | 742 | struct tomoyo_domain_info * const domain, |
909 | const bool is_delete) | 743 | const bool is_delete) |
910 | { | 744 | { |
911 | const u8 perm = 1 << type; | ||
912 | struct tomoyo_path2_acl e = { | 745 | struct tomoyo_path2_acl e = { |
913 | .head.type = TOMOYO_TYPE_PATH2_ACL, | 746 | .head.type = TOMOYO_TYPE_PATH2_ACL, |
914 | .perm = perm | 747 | .perm = 1 << type |
915 | }; | 748 | }; |
916 | struct tomoyo_acl_info *ptr; | ||
917 | int error = is_delete ? -ENOENT : -ENOMEM; | 749 | int error = is_delete ? -ENOENT : -ENOMEM; |
918 | |||
919 | if (!domain) | ||
920 | return -EINVAL; | ||
921 | if (!tomoyo_parse_name_union(filename1, &e.name1) || | 750 | if (!tomoyo_parse_name_union(filename1, &e.name1) || |
922 | !tomoyo_parse_name_union(filename2, &e.name2)) | 751 | !tomoyo_parse_name_union(filename2, &e.name2)) |
923 | goto out; | 752 | goto out; |
924 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 753 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, |
925 | goto out; | 754 | tomoyo_same_path2_acl, |
926 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 755 | tomoyo_merge_path2_acl); |
927 | struct tomoyo_path2_acl *acl = | ||
928 | container_of(ptr, struct tomoyo_path2_acl, head); | ||
929 | if (!tomoyo_is_same_path2_acl(acl, &e)) | ||
930 | continue; | ||
931 | if (is_delete) | ||
932 | acl->perm &= ~perm; | ||
933 | else | ||
934 | acl->perm |= perm; | ||
935 | error = 0; | ||
936 | break; | ||
937 | } | ||
938 | if (!is_delete && error) { | ||
939 | struct tomoyo_path2_acl *entry = | ||
940 | tomoyo_commit_ok(&e, sizeof(e)); | ||
941 | if (entry) { | ||
942 | list_add_tail_rcu(&entry->head.list, | ||
943 | &domain->acl_info_list); | ||
944 | error = 0; | ||
945 | } | ||
946 | } | ||
947 | mutex_unlock(&tomoyo_policy_lock); | ||
948 | out: | 756 | out: |
949 | tomoyo_put_name_union(&e.name1); | 757 | tomoyo_put_name_union(&e.name1); |
950 | tomoyo_put_name_union(&e.name2); | 758 | tomoyo_put_name_union(&e.name2); |
@@ -952,134 +760,158 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, | |||
952 | } | 760 | } |
953 | 761 | ||
954 | /** | 762 | /** |
955 | * tomoyo_path_acl - Check permission for single path operation. | 763 | * tomoyo_path_permission - Check permission for single path operation. |
956 | * | ||
957 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
958 | * @type: Type of operation. | ||
959 | * @filename: Filename to check. | ||
960 | * | ||
961 | * Returns 0 on success, negative value otherwise. | ||
962 | * | ||
963 | * Caller holds tomoyo_read_lock(). | ||
964 | */ | ||
965 | static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type, | ||
966 | const struct tomoyo_path_info *filename) | ||
967 | { | ||
968 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | ||
969 | return 0; | ||
970 | return tomoyo_path_acl2(domain, filename, 1 << type, 1); | ||
971 | } | ||
972 | |||
973 | /** | ||
974 | * tomoyo_path2_acl - Check permission for double path operation. | ||
975 | * | 764 | * |
976 | * @domain: Pointer to "struct tomoyo_domain_info". | 765 | * @r: Pointer to "struct tomoyo_request_info". |
977 | * @type: Type of operation. | ||
978 | * @filename1: First filename to check. | ||
979 | * @filename2: Second filename to check. | ||
980 | * | ||
981 | * Returns 0 on success, -EPERM otherwise. | ||
982 | * | ||
983 | * Caller holds tomoyo_read_lock(). | ||
984 | */ | ||
985 | static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, | ||
986 | const u8 type, | ||
987 | const struct tomoyo_path_info *filename1, | ||
988 | const struct tomoyo_path_info *filename2) | ||
989 | { | ||
990 | struct tomoyo_acl_info *ptr; | ||
991 | const u8 perm = 1 << type; | ||
992 | int error = -EPERM; | ||
993 | |||
994 | if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) | ||
995 | return 0; | ||
996 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
997 | struct tomoyo_path2_acl *acl; | ||
998 | if (ptr->type != TOMOYO_TYPE_PATH2_ACL) | ||
999 | continue; | ||
1000 | acl = container_of(ptr, struct tomoyo_path2_acl, head); | ||
1001 | if (!(acl->perm & perm)) | ||
1002 | continue; | ||
1003 | if (!tomoyo_compare_name_union(filename1, &acl->name1)) | ||
1004 | continue; | ||
1005 | if (!tomoyo_compare_name_union(filename2, &acl->name2)) | ||
1006 | continue; | ||
1007 | error = 0; | ||
1008 | break; | ||
1009 | } | ||
1010 | return error; | ||
1011 | } | ||
1012 | |||
1013 | /** | ||
1014 | * tomoyo_path_permission2 - Check permission for single path operation. | ||
1015 | * | ||
1016 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
1017 | * @operation: Type of operation. | 766 | * @operation: Type of operation. |
1018 | * @filename: Filename to check. | 767 | * @filename: Filename to check. |
1019 | * @mode: Access control mode. | ||
1020 | * | 768 | * |
1021 | * Returns 0 on success, negative value otherwise. | 769 | * Returns 0 on success, negative value otherwise. |
1022 | * | 770 | * |
1023 | * Caller holds tomoyo_read_lock(). | 771 | * Caller holds tomoyo_read_lock(). |
1024 | */ | 772 | */ |
1025 | static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain, | 773 | int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, |
1026 | u8 operation, | 774 | const struct tomoyo_path_info *filename) |
1027 | const struct tomoyo_path_info *filename, | ||
1028 | const u8 mode) | ||
1029 | { | 775 | { |
1030 | const char *msg; | ||
1031 | int error; | 776 | int error; |
1032 | const bool is_enforce = (mode == 3); | ||
1033 | 777 | ||
1034 | if (!mode) | ||
1035 | return 0; | ||
1036 | next: | 778 | next: |
1037 | error = tomoyo_path_acl(domain, operation, filename); | 779 | r->type = tomoyo_p2mac[operation]; |
1038 | msg = tomoyo_path2keyword(operation); | 780 | r->mode = tomoyo_get_mode(r->profile, r->type); |
1039 | if (!error) | 781 | if (r->mode == TOMOYO_CONFIG_DISABLED) |
1040 | goto ok; | 782 | return 0; |
1041 | if (tomoyo_verbose_mode(domain)) | 783 | r->param_type = TOMOYO_TYPE_PATH_ACL; |
1042 | printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n", | 784 | r->param.path.filename = filename; |
1043 | tomoyo_get_msg(is_enforce), msg, filename->name, | 785 | r->param.path.operation = operation; |
1044 | tomoyo_get_last_name(domain)); | 786 | do { |
1045 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | 787 | tomoyo_check_acl(r, tomoyo_check_path_acl); |
1046 | const char *name = tomoyo_get_file_pattern(filename)->name; | 788 | if (!r->granted && operation == TOMOYO_TYPE_READ && |
1047 | tomoyo_update_path_acl(operation, name, domain, false); | 789 | !r->domain->ignore_global_allow_read && |
1048 | } | 790 | tomoyo_globally_readable_file(filename)) |
1049 | if (!is_enforce) | 791 | r->granted = true; |
1050 | error = 0; | 792 | error = tomoyo_audit_path_log(r); |
1051 | ok: | 793 | /* |
794 | * Do not retry for execute request, for alias may have | ||
795 | * changed. | ||
796 | */ | ||
797 | } while (error == TOMOYO_RETRY_REQUEST && | ||
798 | operation != TOMOYO_TYPE_EXECUTE); | ||
1052 | /* | 799 | /* |
1053 | * Since "allow_truncate" doesn't imply "allow_rewrite" permission, | 800 | * Since "allow_truncate" doesn't imply "allow_rewrite" permission, |
1054 | * we need to check "allow_rewrite" permission if the filename is | 801 | * we need to check "allow_rewrite" permission if the filename is |
1055 | * specified by "deny_rewrite" keyword. | 802 | * specified by "deny_rewrite" keyword. |
1056 | */ | 803 | */ |
1057 | if (!error && operation == TOMOYO_TYPE_TRUNCATE && | 804 | if (!error && operation == TOMOYO_TYPE_TRUNCATE && |
1058 | tomoyo_is_no_rewrite_file(filename)) { | 805 | tomoyo_no_rewrite_file(filename)) { |
1059 | operation = TOMOYO_TYPE_REWRITE; | 806 | operation = TOMOYO_TYPE_REWRITE; |
1060 | goto next; | 807 | goto next; |
1061 | } | 808 | } |
1062 | return error; | 809 | return error; |
1063 | } | 810 | } |
1064 | 811 | ||
812 | static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, | ||
813 | const struct tomoyo_acl_info *b) | ||
814 | { | ||
815 | const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1), | ||
816 | head); | ||
817 | const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), | ||
818 | head); | ||
819 | return tomoyo_same_acl_head(&p1->head, &p2->head) | ||
820 | && tomoyo_same_name_union(&p1->name, &p2->name) | ||
821 | && tomoyo_same_number_union(&p1->number, &p2->number); | ||
822 | } | ||
823 | |||
824 | static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, | ||
825 | struct tomoyo_acl_info *b, | ||
826 | const bool is_delete) | ||
827 | { | ||
828 | u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl, | ||
829 | head)->perm; | ||
830 | u8 perm = *a_perm; | ||
831 | const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head) | ||
832 | ->perm; | ||
833 | if (is_delete) | ||
834 | perm &= ~b_perm; | ||
835 | else | ||
836 | perm |= b_perm; | ||
837 | *a_perm = perm; | ||
838 | return !perm; | ||
839 | } | ||
840 | |||
1065 | /** | 841 | /** |
1066 | * tomoyo_check_exec_perm - Check permission for "execute". | 842 | * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. |
843 | * | ||
844 | * @type: Type of operation. | ||
845 | * @filename: Filename. | ||
846 | * @number: Number. | ||
847 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
848 | * @is_delete: True if it is a delete request. | ||
1067 | * | 849 | * |
1068 | * @domain: Pointer to "struct tomoyo_domain_info". | 850 | * Returns 0 on success, negative value otherwise. |
1069 | * @filename: Check permission for "execute". | 851 | */ |
852 | static int tomoyo_update_path_number_acl(const u8 type, const char *filename, | ||
853 | char *number, | ||
854 | struct tomoyo_domain_info * const | ||
855 | domain, | ||
856 | const bool is_delete) | ||
857 | { | ||
858 | struct tomoyo_path_number_acl e = { | ||
859 | .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, | ||
860 | .perm = 1 << type | ||
861 | }; | ||
862 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
863 | if (!tomoyo_parse_name_union(filename, &e.name)) | ||
864 | return -EINVAL; | ||
865 | if (!tomoyo_parse_number_union(number, &e.number)) | ||
866 | goto out; | ||
867 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, | ||
868 | tomoyo_same_path_number_acl, | ||
869 | tomoyo_merge_path_number_acl); | ||
870 | out: | ||
871 | tomoyo_put_name_union(&e.name); | ||
872 | tomoyo_put_number_union(&e.number); | ||
873 | return error; | ||
874 | } | ||
875 | |||
876 | /** | ||
877 | * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". | ||
1070 | * | 878 | * |
1071 | * Returns 0 on success, negativevalue otherwise. | 879 | * @type: Type of operation. |
880 | * @path: Pointer to "struct path". | ||
881 | * @number: Number. | ||
1072 | * | 882 | * |
1073 | * Caller holds tomoyo_read_lock(). | 883 | * Returns 0 on success, negative value otherwise. |
1074 | */ | 884 | */ |
1075 | int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, | 885 | int tomoyo_path_number_perm(const u8 type, struct path *path, |
1076 | const struct tomoyo_path_info *filename) | 886 | unsigned long number) |
1077 | { | 887 | { |
1078 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 888 | struct tomoyo_request_info r; |
889 | int error = -ENOMEM; | ||
890 | struct tomoyo_path_info buf; | ||
891 | int idx; | ||
1079 | 892 | ||
1080 | if (!mode) | 893 | if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) |
894 | == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) | ||
1081 | return 0; | 895 | return 0; |
1082 | return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode); | 896 | idx = tomoyo_read_lock(); |
897 | if (!tomoyo_get_realpath(&buf, path)) | ||
898 | goto out; | ||
899 | if (type == TOMOYO_TYPE_MKDIR) | ||
900 | tomoyo_add_slash(&buf); | ||
901 | r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; | ||
902 | r.param.path_number.operation = type; | ||
903 | r.param.path_number.filename = &buf; | ||
904 | r.param.path_number.number = number; | ||
905 | do { | ||
906 | tomoyo_check_acl(&r, tomoyo_check_path_number_acl); | ||
907 | error = tomoyo_audit_path_number_log(&r); | ||
908 | } while (error == TOMOYO_RETRY_REQUEST); | ||
909 | kfree(buf.name); | ||
910 | out: | ||
911 | tomoyo_read_unlock(idx); | ||
912 | if (r.mode != TOMOYO_CONFIG_ENFORCING) | ||
913 | error = 0; | ||
914 | return error; | ||
1083 | } | 915 | } |
1084 | 916 | ||
1085 | /** | 917 | /** |
@@ -1096,24 +928,17 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
1096 | { | 928 | { |
1097 | const u8 acc_mode = ACC_MODE(flag); | 929 | const u8 acc_mode = ACC_MODE(flag); |
1098 | int error = -ENOMEM; | 930 | int error = -ENOMEM; |
1099 | struct tomoyo_path_info *buf; | 931 | struct tomoyo_path_info buf; |
1100 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 932 | struct tomoyo_request_info r; |
1101 | const bool is_enforce = (mode == 3); | ||
1102 | int idx; | 933 | int idx; |
1103 | 934 | ||
1104 | if (!mode || !path->mnt) | 935 | if (!path->mnt || |
1105 | return 0; | 936 | (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))) |
1106 | if (acc_mode == 0) | ||
1107 | return 0; | ||
1108 | if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) | ||
1109 | /* | ||
1110 | * I don't check directories here because mkdir() and rmdir() | ||
1111 | * don't call me. | ||
1112 | */ | ||
1113 | return 0; | 937 | return 0; |
938 | buf.name = NULL; | ||
939 | r.mode = TOMOYO_CONFIG_DISABLED; | ||
1114 | idx = tomoyo_read_lock(); | 940 | idx = tomoyo_read_lock(); |
1115 | buf = tomoyo_get_path(path); | 941 | if (!tomoyo_get_realpath(&buf, path)) |
1116 | if (!buf) | ||
1117 | goto out; | 942 | goto out; |
1118 | error = 0; | 943 | error = 0; |
1119 | /* | 944 | /* |
@@ -1121,28 +946,43 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
1121 | * we need to check "allow_rewrite" permission when the filename is not | 946 | * we need to check "allow_rewrite" permission when the filename is not |
1122 | * opened for append mode or the filename is truncated at open time. | 947 | * opened for append mode or the filename is truncated at open time. |
1123 | */ | 948 | */ |
1124 | if ((acc_mode & MAY_WRITE) && | 949 | if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND) |
1125 | ((flag & O_TRUNC) || !(flag & O_APPEND)) && | 950 | && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE) |
1126 | (tomoyo_is_no_rewrite_file(buf))) { | 951 | != TOMOYO_CONFIG_DISABLED) { |
1127 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, | 952 | if (!tomoyo_get_realpath(&buf, path)) { |
1128 | buf, mode); | 953 | error = -ENOMEM; |
954 | goto out; | ||
955 | } | ||
956 | if (tomoyo_no_rewrite_file(&buf)) | ||
957 | error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, | ||
958 | &buf); | ||
959 | } | ||
960 | if (!error && acc_mode && | ||
961 | tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) | ||
962 | != TOMOYO_CONFIG_DISABLED) { | ||
963 | u8 operation; | ||
964 | if (!buf.name && !tomoyo_get_realpath(&buf, path)) { | ||
965 | error = -ENOMEM; | ||
966 | goto out; | ||
967 | } | ||
968 | if (acc_mode == (MAY_READ | MAY_WRITE)) | ||
969 | operation = TOMOYO_TYPE_READ_WRITE; | ||
970 | else if (acc_mode == MAY_READ) | ||
971 | operation = TOMOYO_TYPE_READ; | ||
972 | else | ||
973 | operation = TOMOYO_TYPE_WRITE; | ||
974 | error = tomoyo_path_permission(&r, operation, &buf); | ||
1129 | } | 975 | } |
1130 | if (!error) | ||
1131 | error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open", | ||
1132 | mode); | ||
1133 | if (!error && (flag & O_TRUNC)) | ||
1134 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE, | ||
1135 | buf, mode); | ||
1136 | out: | 976 | out: |
1137 | kfree(buf); | 977 | kfree(buf.name); |
1138 | tomoyo_read_unlock(idx); | 978 | tomoyo_read_unlock(idx); |
1139 | if (!is_enforce) | 979 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
1140 | error = 0; | 980 | error = 0; |
1141 | return error; | 981 | return error; |
1142 | } | 982 | } |
1143 | 983 | ||
1144 | /** | 984 | /** |
1145 | * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". | 985 | * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount". |
1146 | * | 986 | * |
1147 | * @operation: Type of operation. | 987 | * @operation: Type of operation. |
1148 | * @path: Pointer to "struct path". | 988 | * @path: Pointer to "struct path". |
@@ -1152,71 +992,79 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, | |||
1152 | int tomoyo_path_perm(const u8 operation, struct path *path) | 992 | int tomoyo_path_perm(const u8 operation, struct path *path) |
1153 | { | 993 | { |
1154 | int error = -ENOMEM; | 994 | int error = -ENOMEM; |
1155 | struct tomoyo_path_info *buf; | 995 | struct tomoyo_path_info buf; |
1156 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 996 | struct tomoyo_request_info r; |
1157 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | ||
1158 | const bool is_enforce = (mode == 3); | ||
1159 | int idx; | 997 | int idx; |
1160 | 998 | ||
1161 | if (!mode || !path->mnt) | 999 | if (!path->mnt) |
1162 | return 0; | 1000 | return 0; |
1001 | if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) | ||
1002 | == TOMOYO_CONFIG_DISABLED) | ||
1003 | return 0; | ||
1004 | buf.name = NULL; | ||
1163 | idx = tomoyo_read_lock(); | 1005 | idx = tomoyo_read_lock(); |
1164 | buf = tomoyo_get_path(path); | 1006 | if (!tomoyo_get_realpath(&buf, path)) |
1165 | if (!buf) | ||
1166 | goto out; | 1007 | goto out; |
1167 | switch (operation) { | 1008 | switch (operation) { |
1168 | case TOMOYO_TYPE_MKDIR: | 1009 | case TOMOYO_TYPE_REWRITE: |
1010 | if (!tomoyo_no_rewrite_file(&buf)) { | ||
1011 | error = 0; | ||
1012 | goto out; | ||
1013 | } | ||
1014 | break; | ||
1169 | case TOMOYO_TYPE_RMDIR: | 1015 | case TOMOYO_TYPE_RMDIR: |
1170 | case TOMOYO_TYPE_CHROOT: | 1016 | case TOMOYO_TYPE_CHROOT: |
1171 | if (!buf->is_dir) { | 1017 | case TOMOYO_TYPE_UMOUNT: |
1172 | /* | 1018 | tomoyo_add_slash(&buf); |
1173 | * tomoyo_get_path() reserves space for appending "/." | 1019 | break; |
1174 | */ | ||
1175 | strcat((char *) buf->name, "/"); | ||
1176 | tomoyo_fill_path_info(buf); | ||
1177 | } | ||
1178 | } | 1020 | } |
1179 | error = tomoyo_path_permission2(domain, operation, buf, mode); | 1021 | error = tomoyo_path_permission(&r, operation, &buf); |
1180 | out: | 1022 | out: |
1181 | kfree(buf); | 1023 | kfree(buf.name); |
1182 | tomoyo_read_unlock(idx); | 1024 | tomoyo_read_unlock(idx); |
1183 | if (!is_enforce) | 1025 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
1184 | error = 0; | 1026 | error = 0; |
1185 | return error; | 1027 | return error; |
1186 | } | 1028 | } |
1187 | 1029 | ||
1188 | /** | 1030 | /** |
1189 | * tomoyo_check_rewrite_permission - Check permission for "rewrite". | 1031 | * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar". |
1190 | * | 1032 | * |
1191 | * @filp: Pointer to "struct file". | 1033 | * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) |
1034 | * @path: Pointer to "struct path". | ||
1035 | * @mode: Create mode. | ||
1036 | * @dev: Device number. | ||
1192 | * | 1037 | * |
1193 | * Returns 0 on success, negative value otherwise. | 1038 | * Returns 0 on success, negative value otherwise. |
1194 | */ | 1039 | */ |
1195 | int tomoyo_check_rewrite_permission(struct file *filp) | 1040 | int tomoyo_mkdev_perm(const u8 operation, struct path *path, |
1041 | const unsigned int mode, unsigned int dev) | ||
1196 | { | 1042 | { |
1043 | struct tomoyo_request_info r; | ||
1197 | int error = -ENOMEM; | 1044 | int error = -ENOMEM; |
1198 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 1045 | struct tomoyo_path_info buf; |
1199 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | ||
1200 | const bool is_enforce = (mode == 3); | ||
1201 | struct tomoyo_path_info *buf; | ||
1202 | int idx; | 1046 | int idx; |
1203 | 1047 | ||
1204 | if (!mode || !filp->f_path.mnt) | 1048 | if (!path->mnt || |
1049 | tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) | ||
1050 | == TOMOYO_CONFIG_DISABLED) | ||
1205 | return 0; | 1051 | return 0; |
1206 | |||
1207 | idx = tomoyo_read_lock(); | 1052 | idx = tomoyo_read_lock(); |
1208 | buf = tomoyo_get_path(&filp->f_path); | 1053 | error = -ENOMEM; |
1209 | if (!buf) | 1054 | if (tomoyo_get_realpath(&buf, path)) { |
1210 | goto out; | 1055 | dev = new_decode_dev(dev); |
1211 | if (!tomoyo_is_no_rewrite_file(buf)) { | 1056 | r.param_type = TOMOYO_TYPE_MKDEV_ACL; |
1212 | error = 0; | 1057 | r.param.mkdev.filename = &buf; |
1213 | goto out; | 1058 | r.param.mkdev.operation = operation; |
1059 | r.param.mkdev.mode = mode; | ||
1060 | r.param.mkdev.major = MAJOR(dev); | ||
1061 | r.param.mkdev.minor = MINOR(dev); | ||
1062 | tomoyo_check_acl(&r, tomoyo_check_mkdev_acl); | ||
1063 | error = tomoyo_audit_mkdev_log(&r); | ||
1064 | kfree(buf.name); | ||
1214 | } | 1065 | } |
1215 | error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode); | ||
1216 | out: | ||
1217 | kfree(buf); | ||
1218 | tomoyo_read_unlock(idx); | 1066 | tomoyo_read_unlock(idx); |
1219 | if (!is_enforce) | 1067 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
1220 | error = 0; | 1068 | error = 0; |
1221 | return error; | 1069 | return error; |
1222 | } | 1070 | } |
@@ -1234,56 +1082,99 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, | |||
1234 | struct path *path2) | 1082 | struct path *path2) |
1235 | { | 1083 | { |
1236 | int error = -ENOMEM; | 1084 | int error = -ENOMEM; |
1237 | struct tomoyo_path_info *buf1, *buf2; | 1085 | struct tomoyo_path_info buf1; |
1238 | struct tomoyo_domain_info *domain = tomoyo_domain(); | 1086 | struct tomoyo_path_info buf2; |
1239 | const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); | 1087 | struct tomoyo_request_info r; |
1240 | const bool is_enforce = (mode == 3); | ||
1241 | const char *msg; | ||
1242 | int idx; | 1088 | int idx; |
1243 | 1089 | ||
1244 | if (!mode || !path1->mnt || !path2->mnt) | 1090 | if (!path1->mnt || !path2->mnt || |
1091 | tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) | ||
1092 | == TOMOYO_CONFIG_DISABLED) | ||
1245 | return 0; | 1093 | return 0; |
1094 | buf1.name = NULL; | ||
1095 | buf2.name = NULL; | ||
1246 | idx = tomoyo_read_lock(); | 1096 | idx = tomoyo_read_lock(); |
1247 | buf1 = tomoyo_get_path(path1); | 1097 | if (!tomoyo_get_realpath(&buf1, path1) || |
1248 | buf2 = tomoyo_get_path(path2); | 1098 | !tomoyo_get_realpath(&buf2, path2)) |
1249 | if (!buf1 || !buf2) | ||
1250 | goto out; | ||
1251 | { | ||
1252 | struct dentry *dentry = path1->dentry; | ||
1253 | if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { | ||
1254 | /* | ||
1255 | * tomoyo_get_path() reserves space for appending "/." | ||
1256 | */ | ||
1257 | if (!buf1->is_dir) { | ||
1258 | strcat((char *) buf1->name, "/"); | ||
1259 | tomoyo_fill_path_info(buf1); | ||
1260 | } | ||
1261 | if (!buf2->is_dir) { | ||
1262 | strcat((char *) buf2->name, "/"); | ||
1263 | tomoyo_fill_path_info(buf2); | ||
1264 | } | ||
1265 | } | ||
1266 | } | ||
1267 | error = tomoyo_path2_acl(domain, operation, buf1, buf2); | ||
1268 | msg = tomoyo_path22keyword(operation); | ||
1269 | if (!error) | ||
1270 | goto out; | 1099 | goto out; |
1271 | if (tomoyo_verbose_mode(domain)) | 1100 | switch (operation) { |
1272 | printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' " | 1101 | struct dentry *dentry; |
1273 | "denied for %s\n", tomoyo_get_msg(is_enforce), | 1102 | case TOMOYO_TYPE_RENAME: |
1274 | msg, buf1->name, buf2->name, | 1103 | case TOMOYO_TYPE_LINK: |
1275 | tomoyo_get_last_name(domain)); | 1104 | dentry = path1->dentry; |
1276 | if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { | 1105 | if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)) |
1277 | const char *name1 = tomoyo_get_file_pattern(buf1)->name; | 1106 | break; |
1278 | const char *name2 = tomoyo_get_file_pattern(buf2)->name; | 1107 | /* fall through */ |
1279 | tomoyo_update_path2_acl(operation, name1, name2, domain, | 1108 | case TOMOYO_TYPE_PIVOT_ROOT: |
1280 | false); | 1109 | tomoyo_add_slash(&buf1); |
1281 | } | 1110 | tomoyo_add_slash(&buf2); |
1111 | break; | ||
1112 | } | ||
1113 | r.param_type = TOMOYO_TYPE_PATH2_ACL; | ||
1114 | r.param.path2.operation = operation; | ||
1115 | r.param.path2.filename1 = &buf1; | ||
1116 | r.param.path2.filename2 = &buf2; | ||
1117 | do { | ||
1118 | tomoyo_check_acl(&r, tomoyo_check_path2_acl); | ||
1119 | error = tomoyo_audit_path2_log(&r); | ||
1120 | } while (error == TOMOYO_RETRY_REQUEST); | ||
1282 | out: | 1121 | out: |
1283 | kfree(buf1); | 1122 | kfree(buf1.name); |
1284 | kfree(buf2); | 1123 | kfree(buf2.name); |
1285 | tomoyo_read_unlock(idx); | 1124 | tomoyo_read_unlock(idx); |
1286 | if (!is_enforce) | 1125 | if (r.mode != TOMOYO_CONFIG_ENFORCING) |
1287 | error = 0; | 1126 | error = 0; |
1288 | return error; | 1127 | return error; |
1289 | } | 1128 | } |
1129 | |||
1130 | /** | ||
1131 | * tomoyo_write_file - Update file related list. | ||
1132 | * | ||
1133 | * @data: String to parse. | ||
1134 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
1135 | * @is_delete: True if it is a delete request. | ||
1136 | * | ||
1137 | * Returns 0 on success, negative value otherwise. | ||
1138 | * | ||
1139 | * Caller holds tomoyo_read_lock(). | ||
1140 | */ | ||
1141 | int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, | ||
1142 | const bool is_delete) | ||
1143 | { | ||
1144 | char *w[5]; | ||
1145 | u8 type; | ||
1146 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
1147 | return -EINVAL; | ||
1148 | if (strncmp(w[0], "allow_", 6)) | ||
1149 | goto out; | ||
1150 | w[0] += 6; | ||
1151 | for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { | ||
1152 | if (strcmp(w[0], tomoyo_path_keyword[type])) | ||
1153 | continue; | ||
1154 | return tomoyo_update_path_acl(type, w[1], domain, is_delete); | ||
1155 | } | ||
1156 | if (!w[2][0]) | ||
1157 | goto out; | ||
1158 | for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { | ||
1159 | if (strcmp(w[0], tomoyo_path2_keyword[type])) | ||
1160 | continue; | ||
1161 | return tomoyo_update_path2_acl(type, w[1], w[2], domain, | ||
1162 | is_delete); | ||
1163 | } | ||
1164 | for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { | ||
1165 | if (strcmp(w[0], tomoyo_path_number_keyword[type])) | ||
1166 | continue; | ||
1167 | return tomoyo_update_path_number_acl(type, w[1], w[2], domain, | ||
1168 | is_delete); | ||
1169 | } | ||
1170 | if (!w[3][0] || !w[4][0]) | ||
1171 | goto out; | ||
1172 | for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) { | ||
1173 | if (strcmp(w[0], tomoyo_mkdev_keyword[type])) | ||
1174 | continue; | ||
1175 | return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3], | ||
1176 | w[4], domain, is_delete); | ||
1177 | } | ||
1178 | out: | ||
1179 | return -EINVAL; | ||
1180 | } | ||
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index b9cc71b0431..a877e4c3b10 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c | |||
@@ -11,83 +11,75 @@ | |||
11 | #include <linux/kthread.h> | 11 | #include <linux/kthread.h> |
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | 13 | ||
14 | enum tomoyo_gc_id { | 14 | struct tomoyo_gc { |
15 | TOMOYO_ID_PATH_GROUP, | ||
16 | TOMOYO_ID_PATH_GROUP_MEMBER, | ||
17 | TOMOYO_ID_DOMAIN_INITIALIZER, | ||
18 | TOMOYO_ID_DOMAIN_KEEPER, | ||
19 | TOMOYO_ID_ALIAS, | ||
20 | TOMOYO_ID_GLOBALLY_READABLE, | ||
21 | TOMOYO_ID_PATTERN, | ||
22 | TOMOYO_ID_NO_REWRITE, | ||
23 | TOMOYO_ID_MANAGER, | ||
24 | TOMOYO_ID_NAME, | ||
25 | TOMOYO_ID_ACL, | ||
26 | TOMOYO_ID_DOMAIN | ||
27 | }; | ||
28 | |||
29 | struct tomoyo_gc_entry { | ||
30 | struct list_head list; | 15 | struct list_head list; |
31 | int type; | 16 | int type; |
32 | void *element; | 17 | struct list_head *element; |
33 | }; | 18 | }; |
34 | static LIST_HEAD(tomoyo_gc_queue); | 19 | static LIST_HEAD(tomoyo_gc_queue); |
35 | static DEFINE_MUTEX(tomoyo_gc_mutex); | 20 | static DEFINE_MUTEX(tomoyo_gc_mutex); |
36 | 21 | ||
37 | /* Caller holds tomoyo_policy_lock mutex. */ | 22 | /* Caller holds tomoyo_policy_lock mutex. */ |
38 | static bool tomoyo_add_to_gc(const int type, void *element) | 23 | static bool tomoyo_add_to_gc(const int type, struct list_head *element) |
39 | { | 24 | { |
40 | struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | 25 | struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); |
41 | if (!entry) | 26 | if (!entry) |
42 | return false; | 27 | return false; |
43 | entry->type = type; | 28 | entry->type = type; |
44 | entry->element = element; | 29 | entry->element = element; |
45 | list_add(&entry->list, &tomoyo_gc_queue); | 30 | list_add(&entry->list, &tomoyo_gc_queue); |
31 | list_del_rcu(element); | ||
46 | return true; | 32 | return true; |
47 | } | 33 | } |
48 | 34 | ||
49 | static void tomoyo_del_allow_read | 35 | static void tomoyo_del_allow_read(struct list_head *element) |
50 | (struct tomoyo_globally_readable_file_entry *ptr) | ||
51 | { | 36 | { |
37 | struct tomoyo_readable_file *ptr = | ||
38 | container_of(element, typeof(*ptr), head.list); | ||
52 | tomoyo_put_name(ptr->filename); | 39 | tomoyo_put_name(ptr->filename); |
53 | } | 40 | } |
54 | 41 | ||
55 | static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr) | 42 | static void tomoyo_del_file_pattern(struct list_head *element) |
56 | { | 43 | { |
44 | struct tomoyo_no_pattern *ptr = | ||
45 | container_of(element, typeof(*ptr), head.list); | ||
57 | tomoyo_put_name(ptr->pattern); | 46 | tomoyo_put_name(ptr->pattern); |
58 | } | 47 | } |
59 | 48 | ||
60 | static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr) | 49 | static void tomoyo_del_no_rewrite(struct list_head *element) |
61 | { | 50 | { |
51 | struct tomoyo_no_rewrite *ptr = | ||
52 | container_of(element, typeof(*ptr), head.list); | ||
62 | tomoyo_put_name(ptr->pattern); | 53 | tomoyo_put_name(ptr->pattern); |
63 | } | 54 | } |
64 | 55 | ||
65 | static void tomoyo_del_domain_initializer | 56 | static void tomoyo_del_transition_control(struct list_head *element) |
66 | (struct tomoyo_domain_initializer_entry *ptr) | ||
67 | { | 57 | { |
58 | struct tomoyo_transition_control *ptr = | ||
59 | container_of(element, typeof(*ptr), head.list); | ||
68 | tomoyo_put_name(ptr->domainname); | 60 | tomoyo_put_name(ptr->domainname); |
69 | tomoyo_put_name(ptr->program); | 61 | tomoyo_put_name(ptr->program); |
70 | } | 62 | } |
71 | 63 | ||
72 | static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) | 64 | static void tomoyo_del_aggregator(struct list_head *element) |
73 | { | ||
74 | tomoyo_put_name(ptr->domainname); | ||
75 | tomoyo_put_name(ptr->program); | ||
76 | } | ||
77 | |||
78 | static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) | ||
79 | { | 65 | { |
66 | struct tomoyo_aggregator *ptr = | ||
67 | container_of(element, typeof(*ptr), head.list); | ||
80 | tomoyo_put_name(ptr->original_name); | 68 | tomoyo_put_name(ptr->original_name); |
81 | tomoyo_put_name(ptr->aliased_name); | 69 | tomoyo_put_name(ptr->aggregated_name); |
82 | } | 70 | } |
83 | 71 | ||
84 | static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr) | 72 | static void tomoyo_del_manager(struct list_head *element) |
85 | { | 73 | { |
74 | struct tomoyo_manager *ptr = | ||
75 | container_of(element, typeof(*ptr), head.list); | ||
86 | tomoyo_put_name(ptr->manager); | 76 | tomoyo_put_name(ptr->manager); |
87 | } | 77 | } |
88 | 78 | ||
89 | static void tomoyo_del_acl(struct tomoyo_acl_info *acl) | 79 | static void tomoyo_del_acl(struct list_head *element) |
90 | { | 80 | { |
81 | struct tomoyo_acl_info *acl = | ||
82 | container_of(element, typeof(*acl), list); | ||
91 | switch (acl->type) { | 83 | switch (acl->type) { |
92 | case TOMOYO_TYPE_PATH_ACL: | 84 | case TOMOYO_TYPE_PATH_ACL: |
93 | { | 85 | { |
@@ -104,14 +96,41 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) | |||
104 | tomoyo_put_name_union(&entry->name2); | 96 | tomoyo_put_name_union(&entry->name2); |
105 | } | 97 | } |
106 | break; | 98 | break; |
107 | default: | 99 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
108 | printk(KERN_WARNING "Unknown type\n"); | 100 | { |
101 | struct tomoyo_path_number_acl *entry | ||
102 | = container_of(acl, typeof(*entry), head); | ||
103 | tomoyo_put_name_union(&entry->name); | ||
104 | tomoyo_put_number_union(&entry->number); | ||
105 | } | ||
106 | break; | ||
107 | case TOMOYO_TYPE_MKDEV_ACL: | ||
108 | { | ||
109 | struct tomoyo_mkdev_acl *entry | ||
110 | = container_of(acl, typeof(*entry), head); | ||
111 | tomoyo_put_name_union(&entry->name); | ||
112 | tomoyo_put_number_union(&entry->mode); | ||
113 | tomoyo_put_number_union(&entry->major); | ||
114 | tomoyo_put_number_union(&entry->minor); | ||
115 | } | ||
116 | break; | ||
117 | case TOMOYO_TYPE_MOUNT_ACL: | ||
118 | { | ||
119 | struct tomoyo_mount_acl *entry | ||
120 | = container_of(acl, typeof(*entry), head); | ||
121 | tomoyo_put_name_union(&entry->dev_name); | ||
122 | tomoyo_put_name_union(&entry->dir_name); | ||
123 | tomoyo_put_name_union(&entry->fs_type); | ||
124 | tomoyo_put_number_union(&entry->flags); | ||
125 | } | ||
109 | break; | 126 | break; |
110 | } | 127 | } |
111 | } | 128 | } |
112 | 129 | ||
113 | static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | 130 | static bool tomoyo_del_domain(struct list_head *element) |
114 | { | 131 | { |
132 | struct tomoyo_domain_info *domain = | ||
133 | container_of(element, typeof(*domain), list); | ||
115 | struct tomoyo_acl_info *acl; | 134 | struct tomoyo_acl_info *acl; |
116 | struct tomoyo_acl_info *tmp; | 135 | struct tomoyo_acl_info *tmp; |
117 | /* | 136 | /* |
@@ -139,7 +158,7 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | |||
139 | if (atomic_read(&domain->users)) | 158 | if (atomic_read(&domain->users)) |
140 | return false; | 159 | return false; |
141 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | 160 | list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { |
142 | tomoyo_del_acl(acl); | 161 | tomoyo_del_acl(&acl->list); |
143 | tomoyo_memory_free(acl); | 162 | tomoyo_memory_free(acl); |
144 | } | 163 | } |
145 | tomoyo_put_name(domain->domainname); | 164 | tomoyo_put_name(domain->domainname); |
@@ -147,135 +166,70 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) | |||
147 | } | 166 | } |
148 | 167 | ||
149 | 168 | ||
150 | static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) | 169 | static void tomoyo_del_name(struct list_head *element) |
151 | { | 170 | { |
171 | const struct tomoyo_name *ptr = | ||
172 | container_of(element, typeof(*ptr), list); | ||
152 | } | 173 | } |
153 | 174 | ||
154 | static void tomoyo_del_path_group_member(struct tomoyo_path_group_member | 175 | static void tomoyo_del_path_group(struct list_head *element) |
155 | *member) | ||
156 | { | 176 | { |
177 | struct tomoyo_path_group *member = | ||
178 | container_of(element, typeof(*member), head.list); | ||
157 | tomoyo_put_name(member->member_name); | 179 | tomoyo_put_name(member->member_name); |
158 | } | 180 | } |
159 | 181 | ||
160 | static void tomoyo_del_path_group(struct tomoyo_path_group *group) | 182 | static void tomoyo_del_group(struct list_head *element) |
161 | { | 183 | { |
184 | struct tomoyo_group *group = | ||
185 | container_of(element, typeof(*group), list); | ||
162 | tomoyo_put_name(group->group_name); | 186 | tomoyo_put_name(group->group_name); |
163 | } | 187 | } |
164 | 188 | ||
189 | static void tomoyo_del_number_group(struct list_head *element) | ||
190 | { | ||
191 | struct tomoyo_number_group *member = | ||
192 | container_of(element, typeof(*member), head.list); | ||
193 | } | ||
194 | |||
195 | static bool tomoyo_collect_member(struct list_head *member_list, int id) | ||
196 | { | ||
197 | struct tomoyo_acl_head *member; | ||
198 | list_for_each_entry(member, member_list, list) { | ||
199 | if (!member->is_deleted) | ||
200 | continue; | ||
201 | if (!tomoyo_add_to_gc(id, &member->list)) | ||
202 | return false; | ||
203 | } | ||
204 | return true; | ||
205 | } | ||
206 | |||
207 | static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) | ||
208 | { | ||
209 | struct tomoyo_acl_info *acl; | ||
210 | list_for_each_entry(acl, &domain->acl_info_list, list) { | ||
211 | if (!acl->is_deleted) | ||
212 | continue; | ||
213 | if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) | ||
214 | return false; | ||
215 | } | ||
216 | return true; | ||
217 | } | ||
218 | |||
165 | static void tomoyo_collect_entry(void) | 219 | static void tomoyo_collect_entry(void) |
166 | { | 220 | { |
221 | int i; | ||
167 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 222 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
168 | return; | 223 | return; |
169 | { | 224 | for (i = 0; i < TOMOYO_MAX_POLICY; i++) { |
170 | struct tomoyo_globally_readable_file_entry *ptr; | 225 | if (!tomoyo_collect_member(&tomoyo_policy_list[i], i)) |
171 | list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, | 226 | goto unlock; |
172 | list) { | ||
173 | if (!ptr->is_deleted) | ||
174 | continue; | ||
175 | if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) | ||
176 | list_del_rcu(&ptr->list); | ||
177 | else | ||
178 | break; | ||
179 | } | ||
180 | } | ||
181 | { | ||
182 | struct tomoyo_pattern_entry *ptr; | ||
183 | list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { | ||
184 | if (!ptr->is_deleted) | ||
185 | continue; | ||
186 | if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) | ||
187 | list_del_rcu(&ptr->list); | ||
188 | else | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | { | ||
193 | struct tomoyo_no_rewrite_entry *ptr; | ||
194 | list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { | ||
195 | if (!ptr->is_deleted) | ||
196 | continue; | ||
197 | if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) | ||
198 | list_del_rcu(&ptr->list); | ||
199 | else | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | { | ||
204 | struct tomoyo_domain_initializer_entry *ptr; | ||
205 | list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, | ||
206 | list) { | ||
207 | if (!ptr->is_deleted) | ||
208 | continue; | ||
209 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) | ||
210 | list_del_rcu(&ptr->list); | ||
211 | else | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | { | ||
216 | struct tomoyo_domain_keeper_entry *ptr; | ||
217 | list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { | ||
218 | if (!ptr->is_deleted) | ||
219 | continue; | ||
220 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) | ||
221 | list_del_rcu(&ptr->list); | ||
222 | else | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | { | ||
227 | struct tomoyo_alias_entry *ptr; | ||
228 | list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { | ||
229 | if (!ptr->is_deleted) | ||
230 | continue; | ||
231 | if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) | ||
232 | list_del_rcu(&ptr->list); | ||
233 | else | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | { | ||
238 | struct tomoyo_policy_manager_entry *ptr; | ||
239 | list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, | ||
240 | list) { | ||
241 | if (!ptr->is_deleted) | ||
242 | continue; | ||
243 | if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) | ||
244 | list_del_rcu(&ptr->list); | ||
245 | else | ||
246 | break; | ||
247 | } | ||
248 | } | 227 | } |
249 | { | 228 | { |
250 | struct tomoyo_domain_info *domain; | 229 | struct tomoyo_domain_info *domain; |
251 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 230 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
252 | struct tomoyo_acl_info *acl; | 231 | if (!tomoyo_collect_acl(domain)) |
253 | list_for_each_entry_rcu(acl, &domain->acl_info_list, | 232 | goto unlock; |
254 | list) { | ||
255 | switch (acl->type) { | ||
256 | case TOMOYO_TYPE_PATH_ACL: | ||
257 | if (container_of(acl, | ||
258 | struct tomoyo_path_acl, | ||
259 | head)->perm || | ||
260 | container_of(acl, | ||
261 | struct tomoyo_path_acl, | ||
262 | head)->perm_high) | ||
263 | continue; | ||
264 | break; | ||
265 | case TOMOYO_TYPE_PATH2_ACL: | ||
266 | if (container_of(acl, | ||
267 | struct tomoyo_path2_acl, | ||
268 | head)->perm) | ||
269 | continue; | ||
270 | break; | ||
271 | default: | ||
272 | continue; | ||
273 | } | ||
274 | if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) | ||
275 | list_del_rcu(&acl->list); | ||
276 | else | ||
277 | break; | ||
278 | } | ||
279 | if (!domain->is_deleted || atomic_read(&domain->users)) | 233 | if (!domain->is_deleted || atomic_read(&domain->users)) |
280 | continue; | 234 | continue; |
281 | /* | 235 | /* |
@@ -283,104 +237,92 @@ static void tomoyo_collect_entry(void) | |||
283 | * refer this domain after successful execve(). | 237 | * refer this domain after successful execve(). |
284 | * We recheck domain->users after SRCU synchronization. | 238 | * We recheck domain->users after SRCU synchronization. |
285 | */ | 239 | */ |
286 | if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) | 240 | if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) |
287 | list_del_rcu(&domain->list); | 241 | goto unlock; |
288 | else | ||
289 | break; | ||
290 | } | 242 | } |
291 | } | 243 | } |
292 | { | 244 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { |
293 | int i; | 245 | struct tomoyo_name *ptr; |
294 | for (i = 0; i < TOMOYO_MAX_HASH; i++) { | 246 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { |
295 | struct tomoyo_name_entry *ptr; | 247 | if (atomic_read(&ptr->users)) |
296 | list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], | 248 | continue; |
297 | list) { | 249 | if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) |
298 | if (atomic_read(&ptr->users)) | 250 | goto unlock; |
299 | continue; | ||
300 | if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) | ||
301 | list_del_rcu(&ptr->list); | ||
302 | else { | ||
303 | i = TOMOYO_MAX_HASH; | ||
304 | break; | ||
305 | } | ||
306 | } | ||
307 | } | 251 | } |
308 | } | 252 | } |
309 | { | 253 | for (i = 0; i < TOMOYO_MAX_GROUP; i++) { |
310 | struct tomoyo_path_group *group; | 254 | struct list_head *list = &tomoyo_group_list[i]; |
311 | list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { | 255 | int id; |
312 | struct tomoyo_path_group_member *member; | 256 | struct tomoyo_group *group; |
313 | list_for_each_entry_rcu(member, &group->member_list, | 257 | switch (i) { |
314 | list) { | 258 | case 0: |
315 | if (!member->is_deleted) | 259 | id = TOMOYO_ID_PATH_GROUP; |
316 | continue; | 260 | break; |
317 | if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER, | 261 | default: |
318 | member)) | 262 | id = TOMOYO_ID_NUMBER_GROUP; |
319 | list_del_rcu(&member->list); | 263 | break; |
320 | else | 264 | } |
321 | break; | 265 | list_for_each_entry(group, list, list) { |
322 | } | 266 | if (!tomoyo_collect_member(&group->member_list, id)) |
267 | goto unlock; | ||
323 | if (!list_empty(&group->member_list) || | 268 | if (!list_empty(&group->member_list) || |
324 | atomic_read(&group->users)) | 269 | atomic_read(&group->users)) |
325 | continue; | 270 | continue; |
326 | if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group)) | 271 | if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) |
327 | list_del_rcu(&group->list); | 272 | goto unlock; |
328 | else | ||
329 | break; | ||
330 | } | 273 | } |
331 | } | 274 | } |
275 | unlock: | ||
332 | mutex_unlock(&tomoyo_policy_lock); | 276 | mutex_unlock(&tomoyo_policy_lock); |
333 | } | 277 | } |
334 | 278 | ||
335 | static void tomoyo_kfree_entry(void) | 279 | static void tomoyo_kfree_entry(void) |
336 | { | 280 | { |
337 | struct tomoyo_gc_entry *p; | 281 | struct tomoyo_gc *p; |
338 | struct tomoyo_gc_entry *tmp; | 282 | struct tomoyo_gc *tmp; |
339 | 283 | ||
340 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { | 284 | list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { |
285 | struct list_head *element = p->element; | ||
341 | switch (p->type) { | 286 | switch (p->type) { |
342 | case TOMOYO_ID_DOMAIN_INITIALIZER: | 287 | case TOMOYO_ID_TRANSITION_CONTROL: |
343 | tomoyo_del_domain_initializer(p->element); | 288 | tomoyo_del_transition_control(element); |
344 | break; | ||
345 | case TOMOYO_ID_DOMAIN_KEEPER: | ||
346 | tomoyo_del_domain_keeper(p->element); | ||
347 | break; | 289 | break; |
348 | case TOMOYO_ID_ALIAS: | 290 | case TOMOYO_ID_AGGREGATOR: |
349 | tomoyo_del_alias(p->element); | 291 | tomoyo_del_aggregator(element); |
350 | break; | 292 | break; |
351 | case TOMOYO_ID_GLOBALLY_READABLE: | 293 | case TOMOYO_ID_GLOBALLY_READABLE: |
352 | tomoyo_del_allow_read(p->element); | 294 | tomoyo_del_allow_read(element); |
353 | break; | 295 | break; |
354 | case TOMOYO_ID_PATTERN: | 296 | case TOMOYO_ID_PATTERN: |
355 | tomoyo_del_file_pattern(p->element); | 297 | tomoyo_del_file_pattern(element); |
356 | break; | 298 | break; |
357 | case TOMOYO_ID_NO_REWRITE: | 299 | case TOMOYO_ID_NO_REWRITE: |
358 | tomoyo_del_no_rewrite(p->element); | 300 | tomoyo_del_no_rewrite(element); |
359 | break; | 301 | break; |
360 | case TOMOYO_ID_MANAGER: | 302 | case TOMOYO_ID_MANAGER: |
361 | tomoyo_del_manager(p->element); | 303 | tomoyo_del_manager(element); |
362 | break; | 304 | break; |
363 | case TOMOYO_ID_NAME: | 305 | case TOMOYO_ID_NAME: |
364 | tomoyo_del_name(p->element); | 306 | tomoyo_del_name(element); |
365 | break; | 307 | break; |
366 | case TOMOYO_ID_ACL: | 308 | case TOMOYO_ID_ACL: |
367 | tomoyo_del_acl(p->element); | 309 | tomoyo_del_acl(element); |
368 | break; | 310 | break; |
369 | case TOMOYO_ID_DOMAIN: | 311 | case TOMOYO_ID_DOMAIN: |
370 | if (!tomoyo_del_domain(p->element)) | 312 | if (!tomoyo_del_domain(element)) |
371 | continue; | 313 | continue; |
372 | break; | 314 | break; |
373 | case TOMOYO_ID_PATH_GROUP_MEMBER: | ||
374 | tomoyo_del_path_group_member(p->element); | ||
375 | break; | ||
376 | case TOMOYO_ID_PATH_GROUP: | 315 | case TOMOYO_ID_PATH_GROUP: |
377 | tomoyo_del_path_group(p->element); | 316 | tomoyo_del_path_group(element); |
378 | break; | 317 | break; |
379 | default: | 318 | case TOMOYO_ID_GROUP: |
380 | printk(KERN_WARNING "Unknown type\n"); | 319 | tomoyo_del_group(element); |
320 | break; | ||
321 | case TOMOYO_ID_NUMBER_GROUP: | ||
322 | tomoyo_del_number_group(element); | ||
381 | break; | 323 | break; |
382 | } | 324 | } |
383 | tomoyo_memory_free(p->element); | 325 | tomoyo_memory_free(element); |
384 | list_del(&p->list); | 326 | list_del(&p->list); |
385 | kfree(p); | 327 | kfree(p); |
386 | } | 328 | } |
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c new file mode 100644 index 00000000000..e94352ce723 --- /dev/null +++ b/security/tomoyo/group.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * security/tomoyo/group.c | ||
3 | * | ||
4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
5 | */ | ||
6 | |||
7 | #include <linux/slab.h> | ||
8 | #include "common.h" | ||
9 | |||
10 | static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, | ||
11 | const struct tomoyo_acl_head *b) | ||
12 | { | ||
13 | return container_of(a, struct tomoyo_path_group, head)->member_name == | ||
14 | container_of(b, struct tomoyo_path_group, head)->member_name; | ||
15 | } | ||
16 | |||
17 | static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, | ||
18 | const struct tomoyo_acl_head *b) | ||
19 | { | ||
20 | return !memcmp(&container_of(a, struct tomoyo_number_group, head) | ||
21 | ->number, | ||
22 | &container_of(b, struct tomoyo_number_group, head) | ||
23 | ->number, | ||
24 | sizeof(container_of(a, struct tomoyo_number_group, head) | ||
25 | ->number)); | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. | ||
30 | * | ||
31 | * @data: String to parse. | ||
32 | * @is_delete: True if it is a delete request. | ||
33 | * @type: Type of this group. | ||
34 | * | ||
35 | * Returns 0 on success, negative value otherwise. | ||
36 | */ | ||
37 | int tomoyo_write_group(char *data, const bool is_delete, const u8 type) | ||
38 | { | ||
39 | struct tomoyo_group *group; | ||
40 | struct list_head *member; | ||
41 | char *w[2]; | ||
42 | int error = -EINVAL; | ||
43 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
44 | return -EINVAL; | ||
45 | group = tomoyo_get_group(w[0], type); | ||
46 | if (!group) | ||
47 | return -ENOMEM; | ||
48 | member = &group->member_list; | ||
49 | if (type == TOMOYO_PATH_GROUP) { | ||
50 | struct tomoyo_path_group e = { }; | ||
51 | e.member_name = tomoyo_get_name(w[1]); | ||
52 | if (!e.member_name) { | ||
53 | error = -ENOMEM; | ||
54 | goto out; | ||
55 | } | ||
56 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | ||
57 | member, tomoyo_same_path_group); | ||
58 | tomoyo_put_name(e.member_name); | ||
59 | } else if (type == TOMOYO_NUMBER_GROUP) { | ||
60 | struct tomoyo_number_group e = { }; | ||
61 | if (w[1][0] == '@' | ||
62 | || !tomoyo_parse_number_union(w[1], &e.number) | ||
63 | || e.number.values[0] > e.number.values[1]) | ||
64 | goto out; | ||
65 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | ||
66 | member, tomoyo_same_number_group); | ||
67 | /* | ||
68 | * tomoyo_put_number_union() is not needed because | ||
69 | * w[1][0] != '@'. | ||
70 | */ | ||
71 | } | ||
72 | out: | ||
73 | tomoyo_put_group(group); | ||
74 | return error; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. | ||
79 | * | ||
80 | * @pathname: The name of pathname. | ||
81 | * @group: Pointer to "struct tomoyo_path_group". | ||
82 | * | ||
83 | * Returns matched member's pathname if @pathname matches pathnames in @group, | ||
84 | * NULL otherwise. | ||
85 | * | ||
86 | * Caller holds tomoyo_read_lock(). | ||
87 | */ | ||
88 | const struct tomoyo_path_info * | ||
89 | tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | ||
90 | const struct tomoyo_group *group) | ||
91 | { | ||
92 | struct tomoyo_path_group *member; | ||
93 | list_for_each_entry_rcu(member, &group->member_list, head.list) { | ||
94 | if (member->head.is_deleted) | ||
95 | continue; | ||
96 | if (!tomoyo_path_matches_pattern(pathname, member->member_name)) | ||
97 | continue; | ||
98 | return member->member_name; | ||
99 | } | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. | ||
105 | * | ||
106 | * @min: Min number. | ||
107 | * @max: Max number. | ||
108 | * @group: Pointer to "struct tomoyo_number_group". | ||
109 | * | ||
110 | * Returns true if @min and @max partially overlaps @group, false otherwise. | ||
111 | * | ||
112 | * Caller holds tomoyo_read_lock(). | ||
113 | */ | ||
114 | bool tomoyo_number_matches_group(const unsigned long min, | ||
115 | const unsigned long max, | ||
116 | const struct tomoyo_group *group) | ||
117 | { | ||
118 | struct tomoyo_number_group *member; | ||
119 | bool matched = false; | ||
120 | list_for_each_entry_rcu(member, &group->member_list, head.list) { | ||
121 | if (member->head.is_deleted) | ||
122 | continue; | ||
123 | if (min > member->number.values[1] || | ||
124 | max < member->number.values[0]) | ||
125 | continue; | ||
126 | matched = true; | ||
127 | break; | ||
128 | } | ||
129 | return matched; | ||
130 | } | ||
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c new file mode 100644 index 00000000000..bbada7ca1b9 --- /dev/null +++ b/security/tomoyo/load_policy.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * security/tomoyo/load_policy.c | ||
3 | * | ||
4 | * Policy loader launcher for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | ||
8 | |||
9 | #include "common.h" | ||
10 | |||
11 | /* path to policy loader */ | ||
12 | static const char *tomoyo_loader = "/sbin/tomoyo-init"; | ||
13 | |||
14 | /** | ||
15 | * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. | ||
16 | * | ||
17 | * Returns true if /sbin/tomoyo-init exists, false otherwise. | ||
18 | */ | ||
19 | static bool tomoyo_policy_loader_exists(void) | ||
20 | { | ||
21 | /* | ||
22 | * Don't activate MAC if the policy loader doesn't exist. | ||
23 | * If the initrd includes /sbin/init but real-root-dev has not | ||
24 | * mounted on / yet, activating MAC will block the system since | ||
25 | * policies are not loaded yet. | ||
26 | * Thus, let do_execve() call this function everytime. | ||
27 | */ | ||
28 | struct path path; | ||
29 | |||
30 | if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { | ||
31 | printk(KERN_INFO "Not activating Mandatory Access Control now " | ||
32 | "since %s doesn't exist.\n", tomoyo_loader); | ||
33 | return false; | ||
34 | } | ||
35 | path_put(&path); | ||
36 | return true; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * tomoyo_load_policy - Run external policy loader to load policy. | ||
41 | * | ||
42 | * @filename: The program about to start. | ||
43 | * | ||
44 | * This function checks whether @filename is /sbin/init , and if so | ||
45 | * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init | ||
46 | * and then continues invocation of /sbin/init. | ||
47 | * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and | ||
48 | * writes to /sys/kernel/security/tomoyo/ interfaces. | ||
49 | * | ||
50 | * Returns nothing. | ||
51 | */ | ||
52 | void tomoyo_load_policy(const char *filename) | ||
53 | { | ||
54 | char *argv[2]; | ||
55 | char *envp[3]; | ||
56 | |||
57 | if (tomoyo_policy_loaded) | ||
58 | return; | ||
59 | /* | ||
60 | * Check filename is /sbin/init or /sbin/tomoyo-start. | ||
61 | * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't | ||
62 | * be passed. | ||
63 | * You can create /sbin/tomoyo-start by | ||
64 | * "ln -s /bin/true /sbin/tomoyo-start". | ||
65 | */ | ||
66 | if (strcmp(filename, "/sbin/init") && | ||
67 | strcmp(filename, "/sbin/tomoyo-start")) | ||
68 | return; | ||
69 | if (!tomoyo_policy_loader_exists()) | ||
70 | return; | ||
71 | |||
72 | printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | ||
73 | tomoyo_loader); | ||
74 | argv[0] = (char *) tomoyo_loader; | ||
75 | argv[1] = NULL; | ||
76 | envp[0] = "HOME=/"; | ||
77 | envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
78 | envp[2] = NULL; | ||
79 | call_usermodehelper(argv[0], argv, envp, 1); | ||
80 | tomoyo_check_profile(); | ||
81 | } | ||
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c new file mode 100644 index 00000000000..297612669c7 --- /dev/null +++ b/security/tomoyo/memory.c | |||
@@ -0,0 +1,282 @@ | |||
1 | /* | ||
2 | * security/tomoyo/memory.c | ||
3 | * | ||
4 | * Memory management functions for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | ||
8 | |||
9 | #include <linux/hash.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include "common.h" | ||
12 | |||
13 | /** | ||
14 | * tomoyo_warn_oom - Print out of memory warning message. | ||
15 | * | ||
16 | * @function: Function's name. | ||
17 | */ | ||
18 | void tomoyo_warn_oom(const char *function) | ||
19 | { | ||
20 | /* Reduce error messages. */ | ||
21 | static pid_t tomoyo_last_pid; | ||
22 | const pid_t pid = current->pid; | ||
23 | if (tomoyo_last_pid != pid) { | ||
24 | printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | ||
25 | function); | ||
26 | tomoyo_last_pid = pid; | ||
27 | } | ||
28 | if (!tomoyo_policy_loaded) | ||
29 | panic("MAC Initialization failed.\n"); | ||
30 | } | ||
31 | |||
32 | /* Memory allocated for policy. */ | ||
33 | static atomic_t tomoyo_policy_memory_size; | ||
34 | /* Quota for holding policy. */ | ||
35 | static unsigned int tomoyo_quota_for_policy; | ||
36 | |||
37 | /** | ||
38 | * tomoyo_memory_ok - Check memory quota. | ||
39 | * | ||
40 | * @ptr: Pointer to allocated memory. | ||
41 | * | ||
42 | * Returns true on success, false otherwise. | ||
43 | * | ||
44 | * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | ||
45 | */ | ||
46 | bool tomoyo_memory_ok(void *ptr) | ||
47 | { | ||
48 | size_t s = ptr ? ksize(ptr) : 0; | ||
49 | atomic_add(s, &tomoyo_policy_memory_size); | ||
50 | if (ptr && (!tomoyo_quota_for_policy || | ||
51 | atomic_read(&tomoyo_policy_memory_size) | ||
52 | <= tomoyo_quota_for_policy)) { | ||
53 | memset(ptr, 0, s); | ||
54 | return true; | ||
55 | } | ||
56 | atomic_sub(s, &tomoyo_policy_memory_size); | ||
57 | tomoyo_warn_oom(__func__); | ||
58 | return false; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * tomoyo_commit_ok - Check memory quota. | ||
63 | * | ||
64 | * @data: Data to copy from. | ||
65 | * @size: Size in byte. | ||
66 | * | ||
67 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
68 | * @data is zero-cleared on success. | ||
69 | */ | ||
70 | void *tomoyo_commit_ok(void *data, const unsigned int size) | ||
71 | { | ||
72 | void *ptr = kzalloc(size, GFP_NOFS); | ||
73 | if (tomoyo_memory_ok(ptr)) { | ||
74 | memmove(ptr, data, size); | ||
75 | memset(data, 0, size); | ||
76 | return ptr; | ||
77 | } | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * tomoyo_memory_free - Free memory for elements. | ||
83 | * | ||
84 | * @ptr: Pointer to allocated memory. | ||
85 | */ | ||
86 | void tomoyo_memory_free(void *ptr) | ||
87 | { | ||
88 | atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); | ||
89 | kfree(ptr); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". | ||
94 | * | ||
95 | * @group_name: The name of address group. | ||
96 | * @idx: Index number. | ||
97 | * | ||
98 | * Returns pointer to "struct tomoyo_group" on success, NULL otherwise. | ||
99 | */ | ||
100 | struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) | ||
101 | { | ||
102 | struct tomoyo_group e = { }; | ||
103 | struct tomoyo_group *group = NULL; | ||
104 | bool found = false; | ||
105 | if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP) | ||
106 | return NULL; | ||
107 | e.group_name = tomoyo_get_name(group_name); | ||
108 | if (!e.group_name) | ||
109 | return NULL; | ||
110 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
111 | goto out; | ||
112 | list_for_each_entry(group, &tomoyo_group_list[idx], list) { | ||
113 | if (e.group_name != group->group_name) | ||
114 | continue; | ||
115 | atomic_inc(&group->users); | ||
116 | found = true; | ||
117 | break; | ||
118 | } | ||
119 | if (!found) { | ||
120 | struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e)); | ||
121 | if (entry) { | ||
122 | INIT_LIST_HEAD(&entry->member_list); | ||
123 | atomic_set(&entry->users, 1); | ||
124 | list_add_tail_rcu(&entry->list, | ||
125 | &tomoyo_group_list[idx]); | ||
126 | group = entry; | ||
127 | found = true; | ||
128 | } | ||
129 | } | ||
130 | mutex_unlock(&tomoyo_policy_lock); | ||
131 | out: | ||
132 | tomoyo_put_name(e.group_name); | ||
133 | return found ? group : NULL; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * tomoyo_name_list is used for holding string data used by TOMOYO. | ||
138 | * Since same string data is likely used for multiple times (e.g. | ||
139 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of | ||
140 | * "const struct tomoyo_path_info *". | ||
141 | */ | ||
142 | struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | ||
143 | |||
144 | /** | ||
145 | * tomoyo_get_name - Allocate permanent memory for string data. | ||
146 | * | ||
147 | * @name: The string to store into the permernent memory. | ||
148 | * | ||
149 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | ||
150 | */ | ||
151 | const struct tomoyo_path_info *tomoyo_get_name(const char *name) | ||
152 | { | ||
153 | struct tomoyo_name *ptr; | ||
154 | unsigned int hash; | ||
155 | int len; | ||
156 | int allocated_len; | ||
157 | struct list_head *head; | ||
158 | |||
159 | if (!name) | ||
160 | return NULL; | ||
161 | len = strlen(name) + 1; | ||
162 | hash = full_name_hash((const unsigned char *) name, len - 1); | ||
163 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; | ||
164 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
165 | return NULL; | ||
166 | list_for_each_entry(ptr, head, list) { | ||
167 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | ||
168 | continue; | ||
169 | atomic_inc(&ptr->users); | ||
170 | goto out; | ||
171 | } | ||
172 | ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); | ||
173 | allocated_len = ptr ? ksize(ptr) : 0; | ||
174 | if (!ptr || (tomoyo_quota_for_policy && | ||
175 | atomic_read(&tomoyo_policy_memory_size) + allocated_len | ||
176 | > tomoyo_quota_for_policy)) { | ||
177 | kfree(ptr); | ||
178 | ptr = NULL; | ||
179 | tomoyo_warn_oom(__func__); | ||
180 | goto out; | ||
181 | } | ||
182 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
183 | ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | ||
184 | memmove((char *) ptr->entry.name, name, len); | ||
185 | atomic_set(&ptr->users, 1); | ||
186 | tomoyo_fill_path_info(&ptr->entry); | ||
187 | list_add_tail(&ptr->list, head); | ||
188 | out: | ||
189 | mutex_unlock(&tomoyo_policy_lock); | ||
190 | return ptr ? &ptr->entry : NULL; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * tomoyo_mm_init - Initialize mm related code. | ||
195 | */ | ||
196 | void __init tomoyo_mm_init(void) | ||
197 | { | ||
198 | int idx; | ||
199 | |||
200 | for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) | ||
201 | INIT_LIST_HEAD(&tomoyo_policy_list[idx]); | ||
202 | for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) | ||
203 | INIT_LIST_HEAD(&tomoyo_group_list[idx]); | ||
204 | for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) | ||
205 | INIT_LIST_HEAD(&tomoyo_name_list[idx]); | ||
206 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | ||
207 | tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); | ||
208 | list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
209 | idx = tomoyo_read_lock(); | ||
210 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | ||
211 | panic("Can't register tomoyo_kernel_domain"); | ||
212 | { | ||
213 | /* Load built-in policy. */ | ||
214 | tomoyo_write_transition_control("/sbin/hotplug", false, | ||
215 | TOMOYO_TRANSITION_CONTROL_INITIALIZE); | ||
216 | tomoyo_write_transition_control("/sbin/modprobe", false, | ||
217 | TOMOYO_TRANSITION_CONTROL_INITIALIZE); | ||
218 | } | ||
219 | tomoyo_read_unlock(idx); | ||
220 | } | ||
221 | |||
222 | |||
223 | /* Memory allocated for query lists. */ | ||
224 | unsigned int tomoyo_query_memory_size; | ||
225 | /* Quota for holding query lists. */ | ||
226 | unsigned int tomoyo_quota_for_query; | ||
227 | |||
228 | /** | ||
229 | * tomoyo_read_memory_counter - Check for memory usage in bytes. | ||
230 | * | ||
231 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
232 | * | ||
233 | * Returns memory usage. | ||
234 | */ | ||
235 | void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | ||
236 | { | ||
237 | if (!head->r.eof) { | ||
238 | const unsigned int policy | ||
239 | = atomic_read(&tomoyo_policy_memory_size); | ||
240 | const unsigned int query = tomoyo_query_memory_size; | ||
241 | char buffer[64]; | ||
242 | |||
243 | memset(buffer, 0, sizeof(buffer)); | ||
244 | if (tomoyo_quota_for_policy) | ||
245 | snprintf(buffer, sizeof(buffer) - 1, | ||
246 | " (Quota: %10u)", | ||
247 | tomoyo_quota_for_policy); | ||
248 | else | ||
249 | buffer[0] = '\0'; | ||
250 | tomoyo_io_printf(head, "Policy: %10u%s\n", policy, | ||
251 | buffer); | ||
252 | if (tomoyo_quota_for_query) | ||
253 | snprintf(buffer, sizeof(buffer) - 1, | ||
254 | " (Quota: %10u)", | ||
255 | tomoyo_quota_for_query); | ||
256 | else | ||
257 | buffer[0] = '\0'; | ||
258 | tomoyo_io_printf(head, "Query lists: %10u%s\n", query, | ||
259 | buffer); | ||
260 | tomoyo_io_printf(head, "Total: %10u\n", policy + query); | ||
261 | head->r.eof = true; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * tomoyo_write_memory_quota - Set memory quota. | ||
267 | * | ||
268 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
269 | * | ||
270 | * Returns 0. | ||
271 | */ | ||
272 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | ||
273 | { | ||
274 | char *data = head->write_buf; | ||
275 | unsigned int size; | ||
276 | |||
277 | if (sscanf(data, "Policy: %u", &size) == 1) | ||
278 | tomoyo_quota_for_policy = size; | ||
279 | else if (sscanf(data, "Query lists: %u", &size) == 1) | ||
280 | tomoyo_quota_for_query = size; | ||
281 | return 0; | ||
282 | } | ||
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c new file mode 100644 index 00000000000..82bf8c2390b --- /dev/null +++ b/security/tomoyo/mount.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | * security/tomoyo/mount.c | ||
3 | * | ||
4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
5 | */ | ||
6 | |||
7 | #include <linux/slab.h> | ||
8 | #include "common.h" | ||
9 | |||
10 | /* Keywords for mount restrictions. */ | ||
11 | |||
12 | /* Allow to call 'mount --bind /source_dir /dest_dir' */ | ||
13 | #define TOMOYO_MOUNT_BIND_KEYWORD "--bind" | ||
14 | /* Allow to call 'mount --move /old_dir /new_dir ' */ | ||
15 | #define TOMOYO_MOUNT_MOVE_KEYWORD "--move" | ||
16 | /* Allow to call 'mount -o remount /dir ' */ | ||
17 | #define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount" | ||
18 | /* Allow to call 'mount --make-unbindable /dir' */ | ||
19 | #define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" | ||
20 | /* Allow to call 'mount --make-private /dir' */ | ||
21 | #define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" | ||
22 | /* Allow to call 'mount --make-slave /dir' */ | ||
23 | #define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" | ||
24 | /* Allow to call 'mount --make-shared /dir' */ | ||
25 | #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" | ||
26 | |||
27 | /** | ||
28 | * tomoyo_audit_mount_log - Audit mount log. | ||
29 | * | ||
30 | * @r: Pointer to "struct tomoyo_request_info". | ||
31 | * | ||
32 | * Returns 0 on success, negative value otherwise. | ||
33 | */ | ||
34 | static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) | ||
35 | { | ||
36 | const char *dev = r->param.mount.dev->name; | ||
37 | const char *dir = r->param.mount.dir->name; | ||
38 | const char *type = r->param.mount.type->name; | ||
39 | const unsigned long flags = r->param.mount.flags; | ||
40 | if (r->granted) | ||
41 | return 0; | ||
42 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) | ||
43 | tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags); | ||
44 | else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) | ||
45 | || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) | ||
46 | tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir, | ||
47 | flags); | ||
48 | else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
49 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
50 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
51 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) | ||
52 | tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags); | ||
53 | else | ||
54 | tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir, | ||
55 | flags); | ||
56 | return tomoyo_supervisor(r, | ||
57 | TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", | ||
58 | tomoyo_pattern(r->param.mount.dev), | ||
59 | tomoyo_pattern(r->param.mount.dir), type, | ||
60 | flags); | ||
61 | } | ||
62 | |||
63 | static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, | ||
64 | const struct tomoyo_acl_info *ptr) | ||
65 | { | ||
66 | const struct tomoyo_mount_acl *acl = | ||
67 | container_of(ptr, typeof(*acl), head); | ||
68 | return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && | ||
69 | tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) && | ||
70 | tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) && | ||
71 | (!r->param.mount.need_dev || | ||
72 | tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name)); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * tomoyo_mount_acl - Check permission for mount() operation. | ||
77 | * | ||
78 | * @r: Pointer to "struct tomoyo_request_info". | ||
79 | * @dev_name: Name of device file. | ||
80 | * @dir: Pointer to "struct path". | ||
81 | * @type: Name of filesystem type. | ||
82 | * @flags: Mount options. | ||
83 | * | ||
84 | * Returns 0 on success, negative value otherwise. | ||
85 | * | ||
86 | * Caller holds tomoyo_read_lock(). | ||
87 | */ | ||
88 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | ||
89 | struct path *dir, char *type, unsigned long flags) | ||
90 | { | ||
91 | struct path path; | ||
92 | struct file_system_type *fstype = NULL; | ||
93 | const char *requested_type = NULL; | ||
94 | const char *requested_dir_name = NULL; | ||
95 | const char *requested_dev_name = NULL; | ||
96 | struct tomoyo_path_info rtype; | ||
97 | struct tomoyo_path_info rdev; | ||
98 | struct tomoyo_path_info rdir; | ||
99 | int need_dev = 0; | ||
100 | int error = -ENOMEM; | ||
101 | |||
102 | /* Get fstype. */ | ||
103 | requested_type = tomoyo_encode(type); | ||
104 | if (!requested_type) | ||
105 | goto out; | ||
106 | rtype.name = requested_type; | ||
107 | tomoyo_fill_path_info(&rtype); | ||
108 | |||
109 | /* Get mount point. */ | ||
110 | requested_dir_name = tomoyo_realpath_from_path(dir); | ||
111 | if (!requested_dir_name) { | ||
112 | error = -ENOMEM; | ||
113 | goto out; | ||
114 | } | ||
115 | rdir.name = requested_dir_name; | ||
116 | tomoyo_fill_path_info(&rdir); | ||
117 | |||
118 | /* Compare fs name. */ | ||
119 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { | ||
120 | /* dev_name is ignored. */ | ||
121 | } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
122 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
123 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
124 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { | ||
125 | /* dev_name is ignored. */ | ||
126 | } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || | ||
127 | !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { | ||
128 | need_dev = -1; /* dev_name is a directory */ | ||
129 | } else { | ||
130 | fstype = get_fs_type(type); | ||
131 | if (!fstype) { | ||
132 | error = -ENODEV; | ||
133 | goto out; | ||
134 | } | ||
135 | if (fstype->fs_flags & FS_REQUIRES_DEV) | ||
136 | /* dev_name is a block device file. */ | ||
137 | need_dev = 1; | ||
138 | } | ||
139 | if (need_dev) { | ||
140 | /* Get mount point or device file. */ | ||
141 | if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | ||
142 | error = -ENOENT; | ||
143 | goto out; | ||
144 | } | ||
145 | requested_dev_name = tomoyo_realpath_from_path(&path); | ||
146 | if (!requested_dev_name) { | ||
147 | error = -ENOENT; | ||
148 | goto out; | ||
149 | } | ||
150 | } else { | ||
151 | /* Map dev_name to "<NULL>" if no dev_name given. */ | ||
152 | if (!dev_name) | ||
153 | dev_name = "<NULL>"; | ||
154 | requested_dev_name = tomoyo_encode(dev_name); | ||
155 | if (!requested_dev_name) { | ||
156 | error = -ENOMEM; | ||
157 | goto out; | ||
158 | } | ||
159 | } | ||
160 | rdev.name = requested_dev_name; | ||
161 | tomoyo_fill_path_info(&rdev); | ||
162 | r->param_type = TOMOYO_TYPE_MOUNT_ACL; | ||
163 | r->param.mount.need_dev = need_dev; | ||
164 | r->param.mount.dev = &rdev; | ||
165 | r->param.mount.dir = &rdir; | ||
166 | r->param.mount.type = &rtype; | ||
167 | r->param.mount.flags = flags; | ||
168 | do { | ||
169 | tomoyo_check_acl(r, tomoyo_check_mount_acl); | ||
170 | error = tomoyo_audit_mount_log(r); | ||
171 | } while (error == TOMOYO_RETRY_REQUEST); | ||
172 | out: | ||
173 | kfree(requested_dev_name); | ||
174 | kfree(requested_dir_name); | ||
175 | if (fstype) | ||
176 | put_filesystem(fstype); | ||
177 | kfree(requested_type); | ||
178 | return error; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * tomoyo_mount_permission - Check permission for mount() operation. | ||
183 | * | ||
184 | * @dev_name: Name of device file. | ||
185 | * @path: Pointer to "struct path". | ||
186 | * @type: Name of filesystem type. May be NULL. | ||
187 | * @flags: Mount options. | ||
188 | * @data_page: Optional data. May be NULL. | ||
189 | * | ||
190 | * Returns 0 on success, negative value otherwise. | ||
191 | */ | ||
192 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, | ||
193 | unsigned long flags, void *data_page) | ||
194 | { | ||
195 | struct tomoyo_request_info r; | ||
196 | int error; | ||
197 | int idx; | ||
198 | |||
199 | if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) | ||
200 | == TOMOYO_CONFIG_DISABLED) | ||
201 | return 0; | ||
202 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
203 | flags &= ~MS_MGC_MSK; | ||
204 | if (flags & MS_REMOUNT) { | ||
205 | type = TOMOYO_MOUNT_REMOUNT_KEYWORD; | ||
206 | flags &= ~MS_REMOUNT; | ||
207 | } | ||
208 | if (flags & MS_MOVE) { | ||
209 | type = TOMOYO_MOUNT_MOVE_KEYWORD; | ||
210 | flags &= ~MS_MOVE; | ||
211 | } | ||
212 | if (flags & MS_BIND) { | ||
213 | type = TOMOYO_MOUNT_BIND_KEYWORD; | ||
214 | flags &= ~MS_BIND; | ||
215 | } | ||
216 | if (flags & MS_UNBINDABLE) { | ||
217 | type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; | ||
218 | flags &= ~MS_UNBINDABLE; | ||
219 | } | ||
220 | if (flags & MS_PRIVATE) { | ||
221 | type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; | ||
222 | flags &= ~MS_PRIVATE; | ||
223 | } | ||
224 | if (flags & MS_SLAVE) { | ||
225 | type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; | ||
226 | flags &= ~MS_SLAVE; | ||
227 | } | ||
228 | if (flags & MS_SHARED) { | ||
229 | type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; | ||
230 | flags &= ~MS_SHARED; | ||
231 | } | ||
232 | if (!type) | ||
233 | type = "<NULL>"; | ||
234 | idx = tomoyo_read_lock(); | ||
235 | error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | ||
236 | tomoyo_read_unlock(idx); | ||
237 | return error; | ||
238 | } | ||
239 | |||
240 | static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, | ||
241 | const struct tomoyo_acl_info *b) | ||
242 | { | ||
243 | const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); | ||
244 | const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); | ||
245 | return tomoyo_same_acl_head(&p1->head, &p2->head) && | ||
246 | tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && | ||
247 | tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && | ||
248 | tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && | ||
249 | tomoyo_same_number_union(&p1->flags, &p2->flags); | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. | ||
254 | * | ||
255 | * @data: String to parse. | ||
256 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
257 | * @is_delete: True if it is a delete request. | ||
258 | * | ||
259 | * Returns 0 on success, negative value otherwise. | ||
260 | * | ||
261 | * Caller holds tomoyo_read_lock(). | ||
262 | */ | ||
263 | int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, | ||
264 | const bool is_delete) | ||
265 | { | ||
266 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; | ||
267 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
268 | char *w[4]; | ||
269 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) | ||
270 | return -EINVAL; | ||
271 | if (!tomoyo_parse_name_union(w[0], &e.dev_name) || | ||
272 | !tomoyo_parse_name_union(w[1], &e.dir_name) || | ||
273 | !tomoyo_parse_name_union(w[2], &e.fs_type) || | ||
274 | !tomoyo_parse_number_union(w[3], &e.flags)) | ||
275 | goto out; | ||
276 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, | ||
277 | tomoyo_same_mount_acl, NULL); | ||
278 | out: | ||
279 | tomoyo_put_name_union(&e.dev_name); | ||
280 | tomoyo_put_name_union(&e.dir_name); | ||
281 | tomoyo_put_name_union(&e.fs_type); | ||
282 | tomoyo_put_number_union(&e.flags); | ||
283 | return error; | ||
284 | } | ||
diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c deleted file mode 100644 index c988041c8e1..00000000000 --- a/security/tomoyo/path_group.c +++ /dev/null | |||
@@ -1,172 +0,0 @@ | |||
1 | /* | ||
2 | * security/tomoyo/path_group.c | ||
3 | * | ||
4 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
5 | */ | ||
6 | |||
7 | #include <linux/slab.h> | ||
8 | #include "common.h" | ||
9 | /* The list for "struct ccs_path_group". */ | ||
10 | LIST_HEAD(tomoyo_path_group_list); | ||
11 | |||
12 | /** | ||
13 | * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group". | ||
14 | * | ||
15 | * @group_name: The name of pathname group. | ||
16 | * | ||
17 | * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise. | ||
18 | */ | ||
19 | struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) | ||
20 | { | ||
21 | struct tomoyo_path_group *entry = NULL; | ||
22 | struct tomoyo_path_group *group = NULL; | ||
23 | const struct tomoyo_path_info *saved_group_name; | ||
24 | int error = -ENOMEM; | ||
25 | if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || | ||
26 | !group_name[0]) | ||
27 | return NULL; | ||
28 | saved_group_name = tomoyo_get_name(group_name); | ||
29 | if (!saved_group_name) | ||
30 | return NULL; | ||
31 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
32 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
33 | goto out; | ||
34 | list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { | ||
35 | if (saved_group_name != group->group_name) | ||
36 | continue; | ||
37 | atomic_inc(&group->users); | ||
38 | error = 0; | ||
39 | break; | ||
40 | } | ||
41 | if (error && tomoyo_memory_ok(entry)) { | ||
42 | INIT_LIST_HEAD(&entry->member_list); | ||
43 | entry->group_name = saved_group_name; | ||
44 | saved_group_name = NULL; | ||
45 | atomic_set(&entry->users, 1); | ||
46 | list_add_tail_rcu(&entry->list, &tomoyo_path_group_list); | ||
47 | group = entry; | ||
48 | entry = NULL; | ||
49 | error = 0; | ||
50 | } | ||
51 | mutex_unlock(&tomoyo_policy_lock); | ||
52 | out: | ||
53 | tomoyo_put_name(saved_group_name); | ||
54 | kfree(entry); | ||
55 | return !error ? group : NULL; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list. | ||
60 | * | ||
61 | * @data: String to parse. | ||
62 | * @is_delete: True if it is a delete request. | ||
63 | * | ||
64 | * Returns 0 on success, nagative value otherwise. | ||
65 | */ | ||
66 | int tomoyo_write_path_group_policy(char *data, const bool is_delete) | ||
67 | { | ||
68 | struct tomoyo_path_group *group; | ||
69 | struct tomoyo_path_group_member *member; | ||
70 | struct tomoyo_path_group_member e = { }; | ||
71 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
72 | char *w[2]; | ||
73 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) | ||
74 | return -EINVAL; | ||
75 | group = tomoyo_get_path_group(w[0]); | ||
76 | if (!group) | ||
77 | return -ENOMEM; | ||
78 | e.member_name = tomoyo_get_name(w[1]); | ||
79 | if (!e.member_name) | ||
80 | goto out; | ||
81 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
82 | goto out; | ||
83 | list_for_each_entry_rcu(member, &group->member_list, list) { | ||
84 | if (member->member_name != e.member_name) | ||
85 | continue; | ||
86 | member->is_deleted = is_delete; | ||
87 | error = 0; | ||
88 | break; | ||
89 | } | ||
90 | if (!is_delete && error) { | ||
91 | struct tomoyo_path_group_member *entry = | ||
92 | tomoyo_commit_ok(&e, sizeof(e)); | ||
93 | if (entry) { | ||
94 | list_add_tail_rcu(&entry->list, &group->member_list); | ||
95 | error = 0; | ||
96 | } | ||
97 | } | ||
98 | mutex_unlock(&tomoyo_policy_lock); | ||
99 | out: | ||
100 | tomoyo_put_name(e.member_name); | ||
101 | tomoyo_put_path_group(group); | ||
102 | return error; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list. | ||
107 | * | ||
108 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
109 | * | ||
110 | * Returns true on success, false otherwise. | ||
111 | * | ||
112 | * Caller holds tomoyo_read_lock(). | ||
113 | */ | ||
114 | bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) | ||
115 | { | ||
116 | struct list_head *gpos; | ||
117 | struct list_head *mpos; | ||
118 | list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) { | ||
119 | struct tomoyo_path_group *group; | ||
120 | group = list_entry(gpos, struct tomoyo_path_group, list); | ||
121 | list_for_each_cookie(mpos, head->read_var2, | ||
122 | &group->member_list) { | ||
123 | struct tomoyo_path_group_member *member; | ||
124 | member = list_entry(mpos, | ||
125 | struct tomoyo_path_group_member, | ||
126 | list); | ||
127 | if (member->is_deleted) | ||
128 | continue; | ||
129 | if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP | ||
130 | "%s %s\n", | ||
131 | group->group_name->name, | ||
132 | member->member_name->name)) | ||
133 | return false; | ||
134 | } | ||
135 | } | ||
136 | return true; | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. | ||
141 | * | ||
142 | * @pathname: The name of pathname. | ||
143 | * @group: Pointer to "struct tomoyo_path_group". | ||
144 | * @may_use_pattern: True if wild card is permitted. | ||
145 | * | ||
146 | * Returns true if @pathname matches pathnames in @group, false otherwise. | ||
147 | * | ||
148 | * Caller holds tomoyo_read_lock(). | ||
149 | */ | ||
150 | bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, | ||
151 | const struct tomoyo_path_group *group, | ||
152 | const bool may_use_pattern) | ||
153 | { | ||
154 | struct tomoyo_path_group_member *member; | ||
155 | bool matched = false; | ||
156 | list_for_each_entry_rcu(member, &group->member_list, list) { | ||
157 | if (member->is_deleted) | ||
158 | continue; | ||
159 | if (!member->member_name->is_patterned) { | ||
160 | if (tomoyo_pathcmp(pathname, member->member_name)) | ||
161 | continue; | ||
162 | } else if (may_use_pattern) { | ||
163 | if (!tomoyo_path_matches_pattern(pathname, | ||
164 | member->member_name)) | ||
165 | continue; | ||
166 | } else | ||
167 | continue; | ||
168 | matched = true; | ||
169 | break; | ||
170 | } | ||
171 | return matched; | ||
172 | } | ||
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1b96f01962..ed8ccd68010 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
@@ -1,174 +1,164 @@ | |||
1 | /* | 1 | /* |
2 | * security/tomoyo/realpath.c | 2 | * security/tomoyo/realpath.c |
3 | * | 3 | * |
4 | * Get the canonicalized absolute pathnames. The basis for TOMOYO. | 4 | * Pathname calculation functions for TOMOYO. |
5 | * | ||
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
7 | * | ||
8 | * Version: 2.2.0 2009/04/01 | ||
9 | * | 5 | * |
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
10 | */ | 7 | */ |
11 | 8 | ||
12 | #include <linux/types.h> | 9 | #include <linux/types.h> |
13 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
14 | #include <linux/mnt_namespace.h> | 11 | #include <linux/mnt_namespace.h> |
15 | #include <linux/fs_struct.h> | 12 | #include <linux/fs_struct.h> |
16 | #include <linux/hash.h> | ||
17 | #include <linux/magic.h> | 13 | #include <linux/magic.h> |
18 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <net/sock.h> | ||
19 | #include "common.h" | 16 | #include "common.h" |
20 | 17 | ||
21 | /** | 18 | /** |
22 | * tomoyo_encode: Convert binary string to ascii string. | 19 | * tomoyo_encode: Convert binary string to ascii string. |
23 | * | 20 | * |
24 | * @buffer: Buffer for ASCII string. | 21 | * @str: String in binary format. |
25 | * @buflen: Size of @buffer. | 22 | * |
26 | * @str: Binary string. | 23 | * Returns pointer to @str in ascii format on success, NULL otherwise. |
27 | * | 24 | * |
28 | * Returns 0 on success, -ENOMEM otherwise. | 25 | * This function uses kzalloc(), so caller must kfree() if this function |
26 | * didn't return NULL. | ||
29 | */ | 27 | */ |
30 | int tomoyo_encode(char *buffer, int buflen, const char *str) | 28 | char *tomoyo_encode(const char *str) |
31 | { | 29 | { |
32 | while (1) { | 30 | int len = 0; |
33 | const unsigned char c = *(unsigned char *) str++; | 31 | const char *p = str; |
32 | char *cp; | ||
33 | char *cp0; | ||
34 | 34 | ||
35 | if (tomoyo_is_valid(c)) { | 35 | if (!p) |
36 | if (--buflen <= 0) | 36 | return NULL; |
37 | break; | 37 | while (*p) { |
38 | *buffer++ = (char) c; | 38 | const unsigned char c = *p++; |
39 | if (c != '\\') | 39 | if (c == '\\') |
40 | continue; | 40 | len += 2; |
41 | if (--buflen <= 0) | 41 | else if (c > ' ' && c < 127) |
42 | break; | 42 | len++; |
43 | *buffer++ = (char) c; | 43 | else |
44 | continue; | 44 | len += 4; |
45 | } | 45 | } |
46 | if (!c) { | 46 | len++; |
47 | if (--buflen <= 0) | 47 | /* Reserve space for appending "/". */ |
48 | break; | 48 | cp = kzalloc(len + 10, GFP_NOFS); |
49 | *buffer = '\0'; | 49 | if (!cp) |
50 | return 0; | 50 | return NULL; |
51 | cp0 = cp; | ||
52 | p = str; | ||
53 | while (*p) { | ||
54 | const unsigned char c = *p++; | ||
55 | |||
56 | if (c == '\\') { | ||
57 | *cp++ = '\\'; | ||
58 | *cp++ = '\\'; | ||
59 | } else if (c > ' ' && c < 127) { | ||
60 | *cp++ = c; | ||
61 | } else { | ||
62 | *cp++ = '\\'; | ||
63 | *cp++ = (c >> 6) + '0'; | ||
64 | *cp++ = ((c >> 3) & 7) + '0'; | ||
65 | *cp++ = (c & 7) + '0'; | ||
51 | } | 66 | } |
52 | buflen -= 4; | ||
53 | if (buflen <= 0) | ||
54 | break; | ||
55 | *buffer++ = '\\'; | ||
56 | *buffer++ = (c >> 6) + '0'; | ||
57 | *buffer++ = ((c >> 3) & 7) + '0'; | ||
58 | *buffer++ = (c & 7) + '0'; | ||
59 | } | 67 | } |
60 | return -ENOMEM; | 68 | return cp0; |
61 | } | 69 | } |
62 | 70 | ||
63 | /** | 71 | /** |
64 | * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. | 72 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
65 | * | 73 | * |
66 | * @path: Pointer to "struct path". | 74 | * @path: Pointer to "struct path". |
67 | * @newname: Pointer to buffer to return value in. | ||
68 | * @newname_len: Size of @newname. | ||
69 | * | 75 | * |
70 | * Returns 0 on success, negative value otherwise. | 76 | * Returns the realpath of the given @path on success, NULL otherwise. |
71 | * | 77 | * |
72 | * If dentry is a directory, trailing '/' is appended. | 78 | * If dentry is a directory, trailing '/' is appended. |
73 | * Characters out of 0x20 < c < 0x7F range are converted to | 79 | * Characters out of 0x20 < c < 0x7F range are converted to |
74 | * \ooo style octal string. | 80 | * \ooo style octal string. |
75 | * Character \ is converted to \\ string. | 81 | * Character \ is converted to \\ string. |
82 | * | ||
83 | * These functions use kzalloc(), so the caller must call kfree() | ||
84 | * if these functions didn't return NULL. | ||
76 | */ | 85 | */ |
77 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | 86 | char *tomoyo_realpath_from_path(struct path *path) |
78 | int newname_len) | ||
79 | { | 87 | { |
80 | int error = -ENOMEM; | 88 | char *buf = NULL; |
89 | char *name = NULL; | ||
90 | unsigned int buf_len = PAGE_SIZE / 2; | ||
81 | struct dentry *dentry = path->dentry; | 91 | struct dentry *dentry = path->dentry; |
82 | char *sp; | 92 | bool is_dir; |
83 | 93 | if (!dentry) | |
84 | if (!dentry || !path->mnt || !newname || newname_len <= 2048) | 94 | return NULL; |
85 | return -EINVAL; | 95 | is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); |
86 | if (dentry->d_op && dentry->d_op->d_dname) { | 96 | while (1) { |
97 | struct path ns_root = { .mnt = NULL, .dentry = NULL }; | ||
98 | char *pos; | ||
99 | buf_len <<= 1; | ||
100 | kfree(buf); | ||
101 | buf = kmalloc(buf_len, GFP_NOFS); | ||
102 | if (!buf) | ||
103 | break; | ||
104 | /* Get better name for socket. */ | ||
105 | if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { | ||
106 | struct inode *inode = dentry->d_inode; | ||
107 | struct socket *sock = inode ? SOCKET_I(inode) : NULL; | ||
108 | struct sock *sk = sock ? sock->sk : NULL; | ||
109 | if (sk) { | ||
110 | snprintf(buf, buf_len - 1, "socket:[family=%u:" | ||
111 | "type=%u:protocol=%u]", sk->sk_family, | ||
112 | sk->sk_type, sk->sk_protocol); | ||
113 | } else { | ||
114 | snprintf(buf, buf_len - 1, "socket:[unknown]"); | ||
115 | } | ||
116 | name = tomoyo_encode(buf); | ||
117 | break; | ||
118 | } | ||
87 | /* For "socket:[\$]" and "pipe:[\$]". */ | 119 | /* For "socket:[\$]" and "pipe:[\$]". */ |
88 | static const int offset = 1536; | 120 | if (dentry->d_op && dentry->d_op->d_dname) { |
89 | sp = dentry->d_op->d_dname(dentry, newname + offset, | 121 | pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); |
90 | newname_len - offset); | 122 | if (IS_ERR(pos)) |
91 | } else { | 123 | continue; |
92 | struct path ns_root = {.mnt = NULL, .dentry = NULL}; | 124 | name = tomoyo_encode(pos); |
93 | 125 | break; | |
126 | } | ||
127 | /* If we don't have a vfsmount, we can't calculate. */ | ||
128 | if (!path->mnt) | ||
129 | break; | ||
94 | spin_lock(&dcache_lock); | 130 | spin_lock(&dcache_lock); |
95 | /* go to whatever namespace root we are under */ | 131 | /* go to whatever namespace root we are under */ |
96 | sp = __d_path(path, &ns_root, newname, newname_len); | 132 | pos = __d_path(path, &ns_root, buf, buf_len); |
97 | spin_unlock(&dcache_lock); | 133 | spin_unlock(&dcache_lock); |
98 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ | 134 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ |
99 | if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && | 135 | if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && |
100 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { | 136 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { |
101 | sp -= 5; | 137 | pos -= 5; |
102 | if (sp >= newname) | 138 | if (pos >= buf) |
103 | memcpy(sp, "/proc", 5); | 139 | memcpy(pos, "/proc", 5); |
104 | else | 140 | else |
105 | sp = ERR_PTR(-ENOMEM); | 141 | pos = ERR_PTR(-ENOMEM); |
106 | } | ||
107 | } | ||
108 | if (IS_ERR(sp)) | ||
109 | error = PTR_ERR(sp); | ||
110 | else | ||
111 | error = tomoyo_encode(newname, sp - newname, sp); | ||
112 | /* Append trailing '/' if dentry is a directory. */ | ||
113 | if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) | ||
114 | && *newname) { | ||
115 | sp = newname + strlen(newname); | ||
116 | if (*(sp - 1) != '/') { | ||
117 | if (sp < newname + newname_len - 4) { | ||
118 | *sp++ = '/'; | ||
119 | *sp = '\0'; | ||
120 | } else { | ||
121 | error = -ENOMEM; | ||
122 | } | ||
123 | } | 142 | } |
143 | if (IS_ERR(pos)) | ||
144 | continue; | ||
145 | name = tomoyo_encode(pos); | ||
146 | break; | ||
124 | } | 147 | } |
125 | if (error) | ||
126 | printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n"); | ||
127 | return error; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. | ||
132 | * | ||
133 | * @path: Pointer to "struct path". | ||
134 | * | ||
135 | * Returns the realpath of the given @path on success, NULL otherwise. | ||
136 | * | ||
137 | * These functions use kzalloc(), so the caller must call kfree() | ||
138 | * if these functions didn't return NULL. | ||
139 | */ | ||
140 | char *tomoyo_realpath_from_path(struct path *path) | ||
141 | { | ||
142 | char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); | ||
143 | |||
144 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) | ||
145 | <= TOMOYO_MAX_PATHNAME_LEN - 1); | ||
146 | if (!buf) | ||
147 | return NULL; | ||
148 | if (tomoyo_realpath_from_path2(path, buf, | ||
149 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) | ||
150 | return buf; | ||
151 | kfree(buf); | 148 | kfree(buf); |
152 | return NULL; | 149 | if (!name) |
153 | } | 150 | tomoyo_warn_oom(__func__); |
154 | 151 | else if (is_dir && *name) { | |
155 | /** | 152 | /* Append trailing '/' if dentry is a directory. */ |
156 | * tomoyo_realpath - Get realpath of a pathname. | 153 | char *pos = name + strlen(name) - 1; |
157 | * | 154 | if (*pos != '/') |
158 | * @pathname: The pathname to solve. | 155 | /* |
159 | * | 156 | * This is OK because tomoyo_encode() reserves space |
160 | * Returns the realpath of @pathname on success, NULL otherwise. | 157 | * for appending "/". |
161 | */ | 158 | */ |
162 | char *tomoyo_realpath(const char *pathname) | 159 | *++pos = '/'; |
163 | { | ||
164 | struct path path; | ||
165 | |||
166 | if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) { | ||
167 | char *buf = tomoyo_realpath_from_path(&path); | ||
168 | path_put(&path); | ||
169 | return buf; | ||
170 | } | 160 | } |
171 | return NULL; | 161 | return name; |
172 | } | 162 | } |
173 | 163 | ||
174 | /** | 164 | /** |
@@ -189,191 +179,3 @@ char *tomoyo_realpath_nofollow(const char *pathname) | |||
189 | } | 179 | } |
190 | return NULL; | 180 | return NULL; |
191 | } | 181 | } |
192 | |||
193 | /* Memory allocated for non-string data. */ | ||
194 | static atomic_t tomoyo_policy_memory_size; | ||
195 | /* Quota for holding policy. */ | ||
196 | static unsigned int tomoyo_quota_for_policy; | ||
197 | |||
198 | /** | ||
199 | * tomoyo_memory_ok - Check memory quota. | ||
200 | * | ||
201 | * @ptr: Pointer to allocated memory. | ||
202 | * | ||
203 | * Returns true on success, false otherwise. | ||
204 | * | ||
205 | * Caller holds tomoyo_policy_lock. | ||
206 | * Memory pointed by @ptr will be zeroed on success. | ||
207 | */ | ||
208 | bool tomoyo_memory_ok(void *ptr) | ||
209 | { | ||
210 | int allocated_len = ptr ? ksize(ptr) : 0; | ||
211 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
212 | if (ptr && (!tomoyo_quota_for_policy || | ||
213 | atomic_read(&tomoyo_policy_memory_size) | ||
214 | <= tomoyo_quota_for_policy)) { | ||
215 | memset(ptr, 0, allocated_len); | ||
216 | return true; | ||
217 | } | ||
218 | printk(KERN_WARNING "ERROR: Out of memory " | ||
219 | "for tomoyo_alloc_element().\n"); | ||
220 | if (!tomoyo_policy_loaded) | ||
221 | panic("MAC Initialization failed.\n"); | ||
222 | return false; | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * tomoyo_commit_ok - Check memory quota. | ||
227 | * | ||
228 | * @data: Data to copy from. | ||
229 | * @size: Size in byte. | ||
230 | * | ||
231 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
232 | */ | ||
233 | void *tomoyo_commit_ok(void *data, const unsigned int size) | ||
234 | { | ||
235 | void *ptr = kzalloc(size, GFP_NOFS); | ||
236 | if (tomoyo_memory_ok(ptr)) { | ||
237 | memmove(ptr, data, size); | ||
238 | memset(data, 0, size); | ||
239 | return ptr; | ||
240 | } | ||
241 | return NULL; | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * tomoyo_memory_free - Free memory for elements. | ||
246 | * | ||
247 | * @ptr: Pointer to allocated memory. | ||
248 | */ | ||
249 | void tomoyo_memory_free(void *ptr) | ||
250 | { | ||
251 | atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); | ||
252 | kfree(ptr); | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * tomoyo_name_list is used for holding string data used by TOMOYO. | ||
257 | * Since same string data is likely used for multiple times (e.g. | ||
258 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of | ||
259 | * "const struct tomoyo_path_info *". | ||
260 | */ | ||
261 | struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | ||
262 | |||
263 | /** | ||
264 | * tomoyo_get_name - Allocate permanent memory for string data. | ||
265 | * | ||
266 | * @name: The string to store into the permernent memory. | ||
267 | * | ||
268 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | ||
269 | */ | ||
270 | const struct tomoyo_path_info *tomoyo_get_name(const char *name) | ||
271 | { | ||
272 | struct tomoyo_name_entry *ptr; | ||
273 | unsigned int hash; | ||
274 | int len; | ||
275 | int allocated_len; | ||
276 | struct list_head *head; | ||
277 | |||
278 | if (!name) | ||
279 | return NULL; | ||
280 | len = strlen(name) + 1; | ||
281 | hash = full_name_hash((const unsigned char *) name, len - 1); | ||
282 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; | ||
283 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
284 | return NULL; | ||
285 | list_for_each_entry(ptr, head, list) { | ||
286 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) | ||
287 | continue; | ||
288 | atomic_inc(&ptr->users); | ||
289 | goto out; | ||
290 | } | ||
291 | ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); | ||
292 | allocated_len = ptr ? ksize(ptr) : 0; | ||
293 | if (!ptr || (tomoyo_quota_for_policy && | ||
294 | atomic_read(&tomoyo_policy_memory_size) + allocated_len | ||
295 | > tomoyo_quota_for_policy)) { | ||
296 | kfree(ptr); | ||
297 | printk(KERN_WARNING "ERROR: Out of memory " | ||
298 | "for tomoyo_get_name().\n"); | ||
299 | if (!tomoyo_policy_loaded) | ||
300 | panic("MAC Initialization failed.\n"); | ||
301 | ptr = NULL; | ||
302 | goto out; | ||
303 | } | ||
304 | atomic_add(allocated_len, &tomoyo_policy_memory_size); | ||
305 | ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | ||
306 | memmove((char *) ptr->entry.name, name, len); | ||
307 | atomic_set(&ptr->users, 1); | ||
308 | tomoyo_fill_path_info(&ptr->entry); | ||
309 | list_add_tail(&ptr->list, head); | ||
310 | out: | ||
311 | mutex_unlock(&tomoyo_policy_lock); | ||
312 | return ptr ? &ptr->entry : NULL; | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * tomoyo_realpath_init - Initialize realpath related code. | ||
317 | */ | ||
318 | void __init tomoyo_realpath_init(void) | ||
319 | { | ||
320 | int i; | ||
321 | |||
322 | BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); | ||
323 | for (i = 0; i < TOMOYO_MAX_HASH; i++) | ||
324 | INIT_LIST_HEAD(&tomoyo_name_list[i]); | ||
325 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | ||
326 | tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); | ||
327 | /* | ||
328 | * tomoyo_read_lock() is not needed because this function is | ||
329 | * called before the first "delete" request. | ||
330 | */ | ||
331 | list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
332 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | ||
333 | panic("Can't register tomoyo_kernel_domain"); | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * tomoyo_read_memory_counter - Check for memory usage in bytes. | ||
338 | * | ||
339 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
340 | * | ||
341 | * Returns memory usage. | ||
342 | */ | ||
343 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | ||
344 | { | ||
345 | if (!head->read_eof) { | ||
346 | const unsigned int policy | ||
347 | = atomic_read(&tomoyo_policy_memory_size); | ||
348 | char buffer[64]; | ||
349 | |||
350 | memset(buffer, 0, sizeof(buffer)); | ||
351 | if (tomoyo_quota_for_policy) | ||
352 | snprintf(buffer, sizeof(buffer) - 1, | ||
353 | " (Quota: %10u)", | ||
354 | tomoyo_quota_for_policy); | ||
355 | else | ||
356 | buffer[0] = '\0'; | ||
357 | tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); | ||
358 | tomoyo_io_printf(head, "Total: %10u\n", policy); | ||
359 | head->read_eof = true; | ||
360 | } | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * tomoyo_write_memory_quota - Set memory quota. | ||
366 | * | ||
367 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
368 | * | ||
369 | * Returns 0. | ||
370 | */ | ||
371 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | ||
372 | { | ||
373 | char *data = head->write_buf; | ||
374 | unsigned int size; | ||
375 | |||
376 | if (sscanf(data, "Policy: %u", &size) == 1) | ||
377 | tomoyo_quota_for_policy = size; | ||
378 | return 0; | ||
379 | } | ||
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c new file mode 100644 index 00000000000..e43d5554b50 --- /dev/null +++ b/security/tomoyo/securityfs_if.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * security/tomoyo/common.c | ||
3 | * | ||
4 | * Securityfs interface for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | ||
8 | |||
9 | #include <linux/security.h> | ||
10 | #include "common.h" | ||
11 | |||
12 | /** | ||
13 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | ||
14 | * | ||
15 | * @inode: Pointer to "struct inode". | ||
16 | * @file: Pointer to "struct file". | ||
17 | * | ||
18 | * Returns 0 on success, negative value otherwise. | ||
19 | */ | ||
20 | static int tomoyo_open(struct inode *inode, struct file *file) | ||
21 | { | ||
22 | const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) | ||
23 | - ((u8 *) NULL); | ||
24 | return tomoyo_open_control(key, file); | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. | ||
29 | * | ||
30 | * @inode: Pointer to "struct inode". | ||
31 | * @file: Pointer to "struct file". | ||
32 | * | ||
33 | * Returns 0 on success, negative value otherwise. | ||
34 | */ | ||
35 | static int tomoyo_release(struct inode *inode, struct file *file) | ||
36 | { | ||
37 | return tomoyo_close_control(file); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * tomoyo_poll - poll() for /proc/ccs/ interface. | ||
42 | * | ||
43 | * @file: Pointer to "struct file". | ||
44 | * @wait: Pointer to "poll_table". | ||
45 | * | ||
46 | * Returns 0 on success, negative value otherwise. | ||
47 | */ | ||
48 | static unsigned int tomoyo_poll(struct file *file, poll_table *wait) | ||
49 | { | ||
50 | return tomoyo_poll_control(file, wait); | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. | ||
55 | * | ||
56 | * @file: Pointer to "struct file". | ||
57 | * @buf: Pointer to buffer. | ||
58 | * @count: Size of @buf. | ||
59 | * @ppos: Unused. | ||
60 | * | ||
61 | * Returns bytes read on success, negative value otherwise. | ||
62 | */ | ||
63 | static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, | ||
64 | loff_t *ppos) | ||
65 | { | ||
66 | return tomoyo_read_control(file, buf, count); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. | ||
71 | * | ||
72 | * @file: Pointer to "struct file". | ||
73 | * @buf: Pointer to buffer. | ||
74 | * @count: Size of @buf. | ||
75 | * @ppos: Unused. | ||
76 | * | ||
77 | * Returns @count on success, negative value otherwise. | ||
78 | */ | ||
79 | static ssize_t tomoyo_write(struct file *file, const char __user *buf, | ||
80 | size_t count, loff_t *ppos) | ||
81 | { | ||
82 | return tomoyo_write_control(file, buf, count); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * tomoyo_operations is a "struct file_operations" which is used for handling | ||
87 | * /sys/kernel/security/tomoyo/ interface. | ||
88 | * | ||
89 | * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). | ||
90 | * See tomoyo_io_buffer for internals. | ||
91 | */ | ||
92 | static const struct file_operations tomoyo_operations = { | ||
93 | .open = tomoyo_open, | ||
94 | .release = tomoyo_release, | ||
95 | .poll = tomoyo_poll, | ||
96 | .read = tomoyo_read, | ||
97 | .write = tomoyo_write, | ||
98 | .llseek = noop_llseek, | ||
99 | }; | ||
100 | |||
101 | /** | ||
102 | * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. | ||
103 | * | ||
104 | * @name: The name of the interface file. | ||
105 | * @mode: The permission of the interface file. | ||
106 | * @parent: The parent directory. | ||
107 | * @key: Type of interface. | ||
108 | * | ||
109 | * Returns nothing. | ||
110 | */ | ||
111 | static void __init tomoyo_create_entry(const char *name, const mode_t mode, | ||
112 | struct dentry *parent, const u8 key) | ||
113 | { | ||
114 | securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | ||
115 | &tomoyo_operations); | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. | ||
120 | * | ||
121 | * Returns 0. | ||
122 | */ | ||
123 | static int __init tomoyo_initerface_init(void) | ||
124 | { | ||
125 | struct dentry *tomoyo_dir; | ||
126 | |||
127 | /* Don't create securityfs entries unless registered. */ | ||
128 | if (current_cred()->security != &tomoyo_kernel_domain) | ||
129 | return 0; | ||
130 | |||
131 | tomoyo_dir = securityfs_create_dir("tomoyo", NULL); | ||
132 | tomoyo_create_entry("query", 0600, tomoyo_dir, | ||
133 | TOMOYO_QUERY); | ||
134 | tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, | ||
135 | TOMOYO_DOMAINPOLICY); | ||
136 | tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, | ||
137 | TOMOYO_EXCEPTIONPOLICY); | ||
138 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
139 | TOMOYO_SELFDOMAIN); | ||
140 | tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, | ||
141 | TOMOYO_DOMAIN_STATUS); | ||
142 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | ||
143 | TOMOYO_PROCESS_STATUS); | ||
144 | tomoyo_create_entry("meminfo", 0600, tomoyo_dir, | ||
145 | TOMOYO_MEMINFO); | ||
146 | tomoyo_create_entry("profile", 0600, tomoyo_dir, | ||
147 | TOMOYO_PROFILE); | ||
148 | tomoyo_create_entry("manager", 0600, tomoyo_dir, | ||
149 | TOMOYO_MANAGER); | ||
150 | tomoyo_create_entry("version", 0400, tomoyo_dir, | ||
151 | TOMOYO_VERSION); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | fs_initcall(tomoyo_initerface_init); | ||
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index dedd97d0c16..95d3f957223 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c | |||
@@ -3,10 +3,7 @@ | |||
3 | * | 3 | * |
4 | * LSM hooks for TOMOYO Linux. | 4 | * LSM hooks for TOMOYO Linux. |
5 | * | 5 | * |
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
7 | * | ||
8 | * Version: 2.2.0 2009/04/01 | ||
9 | * | ||
10 | */ | 7 | */ |
11 | 8 | ||
12 | #include <linux/security.h> | 9 | #include <linux/security.h> |
@@ -96,8 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) | |||
96 | return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); | 93 | return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); |
97 | } | 94 | } |
98 | 95 | ||
99 | static int tomoyo_path_truncate(struct path *path, loff_t length, | 96 | static int tomoyo_path_truncate(struct path *path) |
100 | unsigned int time_attrs) | ||
101 | { | 97 | { |
102 | return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); | 98 | return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); |
103 | } | 99 | } |
@@ -112,7 +108,8 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, | |||
112 | int mode) | 108 | int mode) |
113 | { | 109 | { |
114 | struct path path = { parent->mnt, dentry }; | 110 | struct path path = { parent->mnt, dentry }; |
115 | return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path); | 111 | return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, |
112 | mode & S_IALLUGO); | ||
116 | } | 113 | } |
117 | 114 | ||
118 | static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) | 115 | static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) |
@@ -133,6 +130,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
133 | { | 130 | { |
134 | struct path path = { parent->mnt, dentry }; | 131 | struct path path = { parent->mnt, dentry }; |
135 | int type = TOMOYO_TYPE_CREATE; | 132 | int type = TOMOYO_TYPE_CREATE; |
133 | const unsigned int perm = mode & S_IALLUGO; | ||
136 | 134 | ||
137 | switch (mode & S_IFMT) { | 135 | switch (mode & S_IFMT) { |
138 | case S_IFCHR: | 136 | case S_IFCHR: |
@@ -141,6 +139,12 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
141 | case S_IFBLK: | 139 | case S_IFBLK: |
142 | type = TOMOYO_TYPE_MKBLOCK; | 140 | type = TOMOYO_TYPE_MKBLOCK; |
143 | break; | 141 | break; |
142 | default: | ||
143 | goto no_dev; | ||
144 | } | ||
145 | return tomoyo_mkdev_perm(type, &path, perm, dev); | ||
146 | no_dev: | ||
147 | switch (mode & S_IFMT) { | ||
144 | case S_IFIFO: | 148 | case S_IFIFO: |
145 | type = TOMOYO_TYPE_MKFIFO; | 149 | type = TOMOYO_TYPE_MKFIFO; |
146 | break; | 150 | break; |
@@ -148,7 +152,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, | |||
148 | type = TOMOYO_TYPE_MKSOCK; | 152 | type = TOMOYO_TYPE_MKSOCK; |
149 | break; | 153 | break; |
150 | } | 154 | } |
151 | return tomoyo_path_perm(type, &path); | 155 | return tomoyo_path_number_perm(type, &path, perm); |
152 | } | 156 | } |
153 | 157 | ||
154 | static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, | 158 | static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, |
@@ -173,7 +177,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, | |||
173 | unsigned long arg) | 177 | unsigned long arg) |
174 | { | 178 | { |
175 | if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) | 179 | if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) |
176 | return tomoyo_check_rewrite_permission(file); | 180 | return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path); |
177 | return 0; | 181 | return 0; |
178 | } | 182 | } |
179 | 183 | ||
@@ -189,23 +193,24 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) | |||
189 | static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, | 193 | static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, |
190 | unsigned long arg) | 194 | unsigned long arg) |
191 | { | 195 | { |
192 | return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path); | 196 | return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd); |
193 | } | 197 | } |
194 | 198 | ||
195 | static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, | 199 | static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, |
196 | mode_t mode) | 200 | mode_t mode) |
197 | { | 201 | { |
198 | struct path path = { mnt, dentry }; | 202 | struct path path = { mnt, dentry }; |
199 | return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path); | 203 | return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path, |
204 | mode & S_IALLUGO); | ||
200 | } | 205 | } |
201 | 206 | ||
202 | static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) | 207 | static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) |
203 | { | 208 | { |
204 | int error = 0; | 209 | int error = 0; |
205 | if (uid != (uid_t) -1) | 210 | if (uid != (uid_t) -1) |
206 | error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path); | 211 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); |
207 | if (!error && gid != (gid_t) -1) | 212 | if (!error && gid != (gid_t) -1) |
208 | error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path); | 213 | error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); |
209 | return error; | 214 | return error; |
210 | } | 215 | } |
211 | 216 | ||
@@ -217,7 +222,7 @@ static int tomoyo_path_chroot(struct path *path) | |||
217 | static int tomoyo_sb_mount(char *dev_name, struct path *path, | 222 | static int tomoyo_sb_mount(char *dev_name, struct path *path, |
218 | char *type, unsigned long flags, void *data) | 223 | char *type, unsigned long flags, void *data) |
219 | { | 224 | { |
220 | return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path); | 225 | return tomoyo_mount_permission(dev_name, path, type, flags, data); |
221 | } | 226 | } |
222 | 227 | ||
223 | static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) | 228 | static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) |
@@ -277,7 +282,7 @@ static int __init tomoyo_init(void) | |||
277 | panic("Failure registering TOMOYO Linux"); | 282 | panic("Failure registering TOMOYO Linux"); |
278 | printk(KERN_INFO "TOMOYO Linux initialized\n"); | 283 | printk(KERN_INFO "TOMOYO Linux initialized\n"); |
279 | cred->security = &tomoyo_kernel_domain; | 284 | cred->security = &tomoyo_kernel_domain; |
280 | tomoyo_realpath_init(); | 285 | tomoyo_mm_init(); |
281 | return 0; | 286 | return 0; |
282 | } | 287 | } |
283 | 288 | ||
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 00000000000..9bfc1ee8222 --- /dev/null +++ b/security/tomoyo/util.c | |||
@@ -0,0 +1,963 @@ | |||
1 | /* | ||
2 | * security/tomoyo/util.c | ||
3 | * | ||
4 | * Utility functions for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | ||
8 | |||
9 | #include <linux/slab.h> | ||
10 | #include "common.h" | ||
11 | |||
12 | /* Lock for protecting policy. */ | ||
13 | DEFINE_MUTEX(tomoyo_policy_lock); | ||
14 | |||
15 | /* Has /sbin/init started? */ | ||
16 | bool tomoyo_policy_loaded; | ||
17 | |||
18 | /** | ||
19 | * tomoyo_parse_ulong - Parse an "unsigned long" value. | ||
20 | * | ||
21 | * @result: Pointer to "unsigned long". | ||
22 | * @str: Pointer to string to parse. | ||
23 | * | ||
24 | * Returns value type on success, 0 otherwise. | ||
25 | * | ||
26 | * The @src is updated to point the first character after the value | ||
27 | * on success. | ||
28 | */ | ||
29 | static u8 tomoyo_parse_ulong(unsigned long *result, char **str) | ||
30 | { | ||
31 | const char *cp = *str; | ||
32 | char *ep; | ||
33 | int base = 10; | ||
34 | if (*cp == '0') { | ||
35 | char c = *(cp + 1); | ||
36 | if (c == 'x' || c == 'X') { | ||
37 | base = 16; | ||
38 | cp += 2; | ||
39 | } else if (c >= '0' && c <= '7') { | ||
40 | base = 8; | ||
41 | cp++; | ||
42 | } | ||
43 | } | ||
44 | *result = simple_strtoul(cp, &ep, base); | ||
45 | if (cp == ep) | ||
46 | return 0; | ||
47 | *str = ep; | ||
48 | switch (base) { | ||
49 | case 16: | ||
50 | return TOMOYO_VALUE_TYPE_HEXADECIMAL; | ||
51 | case 8: | ||
52 | return TOMOYO_VALUE_TYPE_OCTAL; | ||
53 | default: | ||
54 | return TOMOYO_VALUE_TYPE_DECIMAL; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * tomoyo_print_ulong - Print an "unsigned long" value. | ||
60 | * | ||
61 | * @buffer: Pointer to buffer. | ||
62 | * @buffer_len: Size of @buffer. | ||
63 | * @value: An "unsigned long" value. | ||
64 | * @type: Type of @value. | ||
65 | * | ||
66 | * Returns nothing. | ||
67 | */ | ||
68 | void tomoyo_print_ulong(char *buffer, const int buffer_len, | ||
69 | const unsigned long value, const u8 type) | ||
70 | { | ||
71 | if (type == TOMOYO_VALUE_TYPE_DECIMAL) | ||
72 | snprintf(buffer, buffer_len, "%lu", value); | ||
73 | else if (type == TOMOYO_VALUE_TYPE_OCTAL) | ||
74 | snprintf(buffer, buffer_len, "0%lo", value); | ||
75 | else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) | ||
76 | snprintf(buffer, buffer_len, "0x%lX", value); | ||
77 | else | ||
78 | snprintf(buffer, buffer_len, "type(%u)", type); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * tomoyo_parse_name_union - Parse a tomoyo_name_union. | ||
83 | * | ||
84 | * @filename: Name or name group. | ||
85 | * @ptr: Pointer to "struct tomoyo_name_union". | ||
86 | * | ||
87 | * Returns true on success, false otherwise. | ||
88 | */ | ||
89 | bool tomoyo_parse_name_union(const char *filename, | ||
90 | struct tomoyo_name_union *ptr) | ||
91 | { | ||
92 | if (!tomoyo_correct_word(filename)) | ||
93 | return false; | ||
94 | if (filename[0] == '@') { | ||
95 | ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); | ||
96 | ptr->is_group = true; | ||
97 | return ptr->group != NULL; | ||
98 | } | ||
99 | ptr->filename = tomoyo_get_name(filename); | ||
100 | ptr->is_group = false; | ||
101 | return ptr->filename != NULL; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * tomoyo_parse_number_union - Parse a tomoyo_number_union. | ||
106 | * | ||
107 | * @data: Number or number range or number group. | ||
108 | * @ptr: Pointer to "struct tomoyo_number_union". | ||
109 | * | ||
110 | * Returns true on success, false otherwise. | ||
111 | */ | ||
112 | bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) | ||
113 | { | ||
114 | u8 type; | ||
115 | unsigned long v; | ||
116 | memset(num, 0, sizeof(*num)); | ||
117 | if (data[0] == '@') { | ||
118 | if (!tomoyo_correct_word(data)) | ||
119 | return false; | ||
120 | num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); | ||
121 | num->is_group = true; | ||
122 | return num->group != NULL; | ||
123 | } | ||
124 | type = tomoyo_parse_ulong(&v, &data); | ||
125 | if (!type) | ||
126 | return false; | ||
127 | num->values[0] = v; | ||
128 | num->min_type = type; | ||
129 | if (!*data) { | ||
130 | num->values[1] = v; | ||
131 | num->max_type = type; | ||
132 | return true; | ||
133 | } | ||
134 | if (*data++ != '-') | ||
135 | return false; | ||
136 | type = tomoyo_parse_ulong(&v, &data); | ||
137 | if (!type || *data) | ||
138 | return false; | ||
139 | num->values[1] = v; | ||
140 | num->max_type = type; | ||
141 | return true; | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * tomoyo_byte_range - Check whether the string is a \ooo style octal value. | ||
146 | * | ||
147 | * @str: Pointer to the string. | ||
148 | * | ||
149 | * Returns true if @str is a \ooo style octal value, false otherwise. | ||
150 | * | ||
151 | * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. | ||
152 | * This function verifies that \ooo is in valid range. | ||
153 | */ | ||
154 | static inline bool tomoyo_byte_range(const char *str) | ||
155 | { | ||
156 | return *str >= '0' && *str++ <= '3' && | ||
157 | *str >= '0' && *str++ <= '7' && | ||
158 | *str >= '0' && *str <= '7'; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * tomoyo_alphabet_char - Check whether the character is an alphabet. | ||
163 | * | ||
164 | * @c: The character to check. | ||
165 | * | ||
166 | * Returns true if @c is an alphabet character, false otherwise. | ||
167 | */ | ||
168 | static inline bool tomoyo_alphabet_char(const char c) | ||
169 | { | ||
170 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * tomoyo_make_byte - Make byte value from three octal characters. | ||
175 | * | ||
176 | * @c1: The first character. | ||
177 | * @c2: The second character. | ||
178 | * @c3: The third character. | ||
179 | * | ||
180 | * Returns byte value. | ||
181 | */ | ||
182 | static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) | ||
183 | { | ||
184 | return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * tomoyo_str_starts - Check whether the given string starts with the given keyword. | ||
189 | * | ||
190 | * @src: Pointer to pointer to the string. | ||
191 | * @find: Pointer to the keyword. | ||
192 | * | ||
193 | * Returns true if @src starts with @find, false otherwise. | ||
194 | * | ||
195 | * The @src is updated to point the first character after the @find | ||
196 | * if @src starts with @find. | ||
197 | */ | ||
198 | bool tomoyo_str_starts(char **src, const char *find) | ||
199 | { | ||
200 | const int len = strlen(find); | ||
201 | char *tmp = *src; | ||
202 | |||
203 | if (strncmp(tmp, find, len)) | ||
204 | return false; | ||
205 | tmp += len; | ||
206 | *src = tmp; | ||
207 | return true; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * tomoyo_normalize_line - Format string. | ||
212 | * | ||
213 | * @buffer: The line to normalize. | ||
214 | * | ||
215 | * Leading and trailing whitespaces are removed. | ||
216 | * Multiple whitespaces are packed into single space. | ||
217 | * | ||
218 | * Returns nothing. | ||
219 | */ | ||
220 | void tomoyo_normalize_line(unsigned char *buffer) | ||
221 | { | ||
222 | unsigned char *sp = buffer; | ||
223 | unsigned char *dp = buffer; | ||
224 | bool first = true; | ||
225 | |||
226 | while (tomoyo_invalid(*sp)) | ||
227 | sp++; | ||
228 | while (*sp) { | ||
229 | if (!first) | ||
230 | *dp++ = ' '; | ||
231 | first = false; | ||
232 | while (tomoyo_valid(*sp)) | ||
233 | *dp++ = *sp++; | ||
234 | while (tomoyo_invalid(*sp)) | ||
235 | sp++; | ||
236 | } | ||
237 | *dp = '\0'; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * tomoyo_tokenize - Tokenize string. | ||
242 | * | ||
243 | * @buffer: The line to tokenize. | ||
244 | * @w: Pointer to "char *". | ||
245 | * @size: Sizeof @w . | ||
246 | * | ||
247 | * Returns true on success, false otherwise. | ||
248 | */ | ||
249 | bool tomoyo_tokenize(char *buffer, char *w[], size_t size) | ||
250 | { | ||
251 | int count = size / sizeof(char *); | ||
252 | int i; | ||
253 | for (i = 0; i < count; i++) | ||
254 | w[i] = ""; | ||
255 | for (i = 0; i < count; i++) { | ||
256 | char *cp = strchr(buffer, ' '); | ||
257 | if (cp) | ||
258 | *cp = '\0'; | ||
259 | w[i] = buffer; | ||
260 | if (!cp) | ||
261 | break; | ||
262 | buffer = cp + 1; | ||
263 | } | ||
264 | return i < count || !*buffer; | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * tomoyo_correct_word2 - Validate a string. | ||
269 | * | ||
270 | * @string: The string to check. May be non-'\0'-terminated. | ||
271 | * @len: Length of @string. | ||
272 | * | ||
273 | * Check whether the given string follows the naming rules. | ||
274 | * Returns true if @string follows the naming rules, false otherwise. | ||
275 | */ | ||
276 | static bool tomoyo_correct_word2(const char *string, size_t len) | ||
277 | { | ||
278 | const char *const start = string; | ||
279 | bool in_repetition = false; | ||
280 | unsigned char c; | ||
281 | unsigned char d; | ||
282 | unsigned char e; | ||
283 | if (!len) | ||
284 | goto out; | ||
285 | while (len--) { | ||
286 | c = *string++; | ||
287 | if (c == '\\') { | ||
288 | if (!len--) | ||
289 | goto out; | ||
290 | c = *string++; | ||
291 | switch (c) { | ||
292 | case '\\': /* "\\" */ | ||
293 | continue; | ||
294 | case '$': /* "\$" */ | ||
295 | case '+': /* "\+" */ | ||
296 | case '?': /* "\?" */ | ||
297 | case '*': /* "\*" */ | ||
298 | case '@': /* "\@" */ | ||
299 | case 'x': /* "\x" */ | ||
300 | case 'X': /* "\X" */ | ||
301 | case 'a': /* "\a" */ | ||
302 | case 'A': /* "\A" */ | ||
303 | case '-': /* "\-" */ | ||
304 | continue; | ||
305 | case '{': /* "/\{" */ | ||
306 | if (string - 3 < start || *(string - 3) != '/') | ||
307 | break; | ||
308 | in_repetition = true; | ||
309 | continue; | ||
310 | case '}': /* "\}/" */ | ||
311 | if (*string != '/') | ||
312 | break; | ||
313 | if (!in_repetition) | ||
314 | break; | ||
315 | in_repetition = false; | ||
316 | continue; | ||
317 | case '0': /* "\ooo" */ | ||
318 | case '1': | ||
319 | case '2': | ||
320 | case '3': | ||
321 | if (!len-- || !len--) | ||
322 | break; | ||
323 | d = *string++; | ||
324 | e = *string++; | ||
325 | if (d < '0' || d > '7' || e < '0' || e > '7') | ||
326 | break; | ||
327 | c = tomoyo_make_byte(c, d, e); | ||
328 | if (tomoyo_invalid(c)) | ||
329 | continue; /* pattern is not \000 */ | ||
330 | } | ||
331 | goto out; | ||
332 | } else if (in_repetition && c == '/') { | ||
333 | goto out; | ||
334 | } else if (tomoyo_invalid(c)) { | ||
335 | goto out; | ||
336 | } | ||
337 | } | ||
338 | if (in_repetition) | ||
339 | goto out; | ||
340 | return true; | ||
341 | out: | ||
342 | return false; | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * tomoyo_correct_word - Validate a string. | ||
347 | * | ||
348 | * @string: The string to check. | ||
349 | * | ||
350 | * Check whether the given string follows the naming rules. | ||
351 | * Returns true if @string follows the naming rules, false otherwise. | ||
352 | */ | ||
353 | bool tomoyo_correct_word(const char *string) | ||
354 | { | ||
355 | return tomoyo_correct_word2(string, strlen(string)); | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * tomoyo_correct_path - Validate a pathname. | ||
360 | * | ||
361 | * @filename: The pathname to check. | ||
362 | * | ||
363 | * Check whether the given pathname follows the naming rules. | ||
364 | * Returns true if @filename follows the naming rules, false otherwise. | ||
365 | */ | ||
366 | bool tomoyo_correct_path(const char *filename) | ||
367 | { | ||
368 | return *filename == '/' && tomoyo_correct_word(filename); | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * tomoyo_correct_domain - Check whether the given domainname follows the naming rules. | ||
373 | * | ||
374 | * @domainname: The domainname to check. | ||
375 | * | ||
376 | * Returns true if @domainname follows the naming rules, false otherwise. | ||
377 | */ | ||
378 | bool tomoyo_correct_domain(const unsigned char *domainname) | ||
379 | { | ||
380 | if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, | ||
381 | TOMOYO_ROOT_NAME_LEN)) | ||
382 | goto out; | ||
383 | domainname += TOMOYO_ROOT_NAME_LEN; | ||
384 | if (!*domainname) | ||
385 | return true; | ||
386 | if (*domainname++ != ' ') | ||
387 | goto out; | ||
388 | while (1) { | ||
389 | const unsigned char *cp = strchr(domainname, ' '); | ||
390 | if (!cp) | ||
391 | break; | ||
392 | if (*domainname != '/' || | ||
393 | !tomoyo_correct_word2(domainname, cp - domainname - 1)) | ||
394 | goto out; | ||
395 | domainname = cp + 1; | ||
396 | } | ||
397 | return tomoyo_correct_path(domainname); | ||
398 | out: | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * tomoyo_domain_def - Check whether the given token can be a domainname. | ||
404 | * | ||
405 | * @buffer: The token to check. | ||
406 | * | ||
407 | * Returns true if @buffer possibly be a domainname, false otherwise. | ||
408 | */ | ||
409 | bool tomoyo_domain_def(const unsigned char *buffer) | ||
410 | { | ||
411 | return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * tomoyo_find_domain - Find a domain by the given name. | ||
416 | * | ||
417 | * @domainname: The domainname to find. | ||
418 | * | ||
419 | * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. | ||
420 | * | ||
421 | * Caller holds tomoyo_read_lock(). | ||
422 | */ | ||
423 | struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | ||
424 | { | ||
425 | struct tomoyo_domain_info *domain; | ||
426 | struct tomoyo_path_info name; | ||
427 | |||
428 | name.name = domainname; | ||
429 | tomoyo_fill_path_info(&name); | ||
430 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | ||
431 | if (!domain->is_deleted && | ||
432 | !tomoyo_pathcmp(&name, domain->domainname)) | ||
433 | return domain; | ||
434 | } | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | ||
440 | * | ||
441 | * @filename: The string to evaluate. | ||
442 | * | ||
443 | * Returns the initial length without a pattern in @filename. | ||
444 | */ | ||
445 | static int tomoyo_const_part_length(const char *filename) | ||
446 | { | ||
447 | char c; | ||
448 | int len = 0; | ||
449 | |||
450 | if (!filename) | ||
451 | return 0; | ||
452 | while ((c = *filename++) != '\0') { | ||
453 | if (c != '\\') { | ||
454 | len++; | ||
455 | continue; | ||
456 | } | ||
457 | c = *filename++; | ||
458 | switch (c) { | ||
459 | case '\\': /* "\\" */ | ||
460 | len += 2; | ||
461 | continue; | ||
462 | case '0': /* "\ooo" */ | ||
463 | case '1': | ||
464 | case '2': | ||
465 | case '3': | ||
466 | c = *filename++; | ||
467 | if (c < '0' || c > '7') | ||
468 | break; | ||
469 | c = *filename++; | ||
470 | if (c < '0' || c > '7') | ||
471 | break; | ||
472 | len += 4; | ||
473 | continue; | ||
474 | } | ||
475 | break; | ||
476 | } | ||
477 | return len; | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. | ||
482 | * | ||
483 | * @ptr: Pointer to "struct tomoyo_path_info" to fill in. | ||
484 | * | ||
485 | * The caller sets "struct tomoyo_path_info"->name. | ||
486 | */ | ||
487 | void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | ||
488 | { | ||
489 | const char *name = ptr->name; | ||
490 | const int len = strlen(name); | ||
491 | |||
492 | ptr->const_len = tomoyo_const_part_length(name); | ||
493 | ptr->is_dir = len && (name[len - 1] == '/'); | ||
494 | ptr->is_patterned = (ptr->const_len < len); | ||
495 | ptr->hash = full_name_hash(name, len); | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | ||
500 | * | ||
501 | * @filename: The start of string to check. | ||
502 | * @filename_end: The end of string to check. | ||
503 | * @pattern: The start of pattern to compare. | ||
504 | * @pattern_end: The end of pattern to compare. | ||
505 | * | ||
506 | * Returns true if @filename matches @pattern, false otherwise. | ||
507 | */ | ||
508 | static bool tomoyo_file_matches_pattern2(const char *filename, | ||
509 | const char *filename_end, | ||
510 | const char *pattern, | ||
511 | const char *pattern_end) | ||
512 | { | ||
513 | while (filename < filename_end && pattern < pattern_end) { | ||
514 | char c; | ||
515 | if (*pattern != '\\') { | ||
516 | if (*filename++ != *pattern++) | ||
517 | return false; | ||
518 | continue; | ||
519 | } | ||
520 | c = *filename; | ||
521 | pattern++; | ||
522 | switch (*pattern) { | ||
523 | int i; | ||
524 | int j; | ||
525 | case '?': | ||
526 | if (c == '/') { | ||
527 | return false; | ||
528 | } else if (c == '\\') { | ||
529 | if (filename[1] == '\\') | ||
530 | filename++; | ||
531 | else if (tomoyo_byte_range(filename + 1)) | ||
532 | filename += 3; | ||
533 | else | ||
534 | return false; | ||
535 | } | ||
536 | break; | ||
537 | case '\\': | ||
538 | if (c != '\\') | ||
539 | return false; | ||
540 | if (*++filename != '\\') | ||
541 | return false; | ||
542 | break; | ||
543 | case '+': | ||
544 | if (!isdigit(c)) | ||
545 | return false; | ||
546 | break; | ||
547 | case 'x': | ||
548 | if (!isxdigit(c)) | ||
549 | return false; | ||
550 | break; | ||
551 | case 'a': | ||
552 | if (!tomoyo_alphabet_char(c)) | ||
553 | return false; | ||
554 | break; | ||
555 | case '0': | ||
556 | case '1': | ||
557 | case '2': | ||
558 | case '3': | ||
559 | if (c == '\\' && tomoyo_byte_range(filename + 1) | ||
560 | && strncmp(filename + 1, pattern, 3) == 0) { | ||
561 | filename += 3; | ||
562 | pattern += 2; | ||
563 | break; | ||
564 | } | ||
565 | return false; /* Not matched. */ | ||
566 | case '*': | ||
567 | case '@': | ||
568 | for (i = 0; i <= filename_end - filename; i++) { | ||
569 | if (tomoyo_file_matches_pattern2( | ||
570 | filename + i, filename_end, | ||
571 | pattern + 1, pattern_end)) | ||
572 | return true; | ||
573 | c = filename[i]; | ||
574 | if (c == '.' && *pattern == '@') | ||
575 | break; | ||
576 | if (c != '\\') | ||
577 | continue; | ||
578 | if (filename[i + 1] == '\\') | ||
579 | i++; | ||
580 | else if (tomoyo_byte_range(filename + i + 1)) | ||
581 | i += 3; | ||
582 | else | ||
583 | break; /* Bad pattern. */ | ||
584 | } | ||
585 | return false; /* Not matched. */ | ||
586 | default: | ||
587 | j = 0; | ||
588 | c = *pattern; | ||
589 | if (c == '$') { | ||
590 | while (isdigit(filename[j])) | ||
591 | j++; | ||
592 | } else if (c == 'X') { | ||
593 | while (isxdigit(filename[j])) | ||
594 | j++; | ||
595 | } else if (c == 'A') { | ||
596 | while (tomoyo_alphabet_char(filename[j])) | ||
597 | j++; | ||
598 | } | ||
599 | for (i = 1; i <= j; i++) { | ||
600 | if (tomoyo_file_matches_pattern2( | ||
601 | filename + i, filename_end, | ||
602 | pattern + 1, pattern_end)) | ||
603 | return true; | ||
604 | } | ||
605 | return false; /* Not matched or bad pattern. */ | ||
606 | } | ||
607 | filename++; | ||
608 | pattern++; | ||
609 | } | ||
610 | while (*pattern == '\\' && | ||
611 | (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
612 | pattern += 2; | ||
613 | return filename == filename_end && pattern == pattern_end; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * tomoyo_file_matches_pattern - Pattern matching without '/' character. | ||
618 | * | ||
619 | * @filename: The start of string to check. | ||
620 | * @filename_end: The end of string to check. | ||
621 | * @pattern: The start of pattern to compare. | ||
622 | * @pattern_end: The end of pattern to compare. | ||
623 | * | ||
624 | * Returns true if @filename matches @pattern, false otherwise. | ||
625 | */ | ||
626 | static bool tomoyo_file_matches_pattern(const char *filename, | ||
627 | const char *filename_end, | ||
628 | const char *pattern, | ||
629 | const char *pattern_end) | ||
630 | { | ||
631 | const char *pattern_start = pattern; | ||
632 | bool first = true; | ||
633 | bool result; | ||
634 | |||
635 | while (pattern < pattern_end - 1) { | ||
636 | /* Split at "\-" pattern. */ | ||
637 | if (*pattern++ != '\\' || *pattern++ != '-') | ||
638 | continue; | ||
639 | result = tomoyo_file_matches_pattern2(filename, | ||
640 | filename_end, | ||
641 | pattern_start, | ||
642 | pattern - 2); | ||
643 | if (first) | ||
644 | result = !result; | ||
645 | if (result) | ||
646 | return false; | ||
647 | first = false; | ||
648 | pattern_start = pattern; | ||
649 | } | ||
650 | result = tomoyo_file_matches_pattern2(filename, filename_end, | ||
651 | pattern_start, pattern_end); | ||
652 | return first ? result : !result; | ||
653 | } | ||
654 | |||
655 | /** | ||
656 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | ||
657 | * | ||
658 | * @f: The start of string to check. | ||
659 | * @p: The start of pattern to compare. | ||
660 | * | ||
661 | * Returns true if @f matches @p, false otherwise. | ||
662 | */ | ||
663 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
664 | { | ||
665 | const char *f_delimiter; | ||
666 | const char *p_delimiter; | ||
667 | |||
668 | while (*f && *p) { | ||
669 | f_delimiter = strchr(f, '/'); | ||
670 | if (!f_delimiter) | ||
671 | f_delimiter = f + strlen(f); | ||
672 | p_delimiter = strchr(p, '/'); | ||
673 | if (!p_delimiter) | ||
674 | p_delimiter = p + strlen(p); | ||
675 | if (*p == '\\' && *(p + 1) == '{') | ||
676 | goto recursive; | ||
677 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
678 | p_delimiter)) | ||
679 | return false; | ||
680 | f = f_delimiter; | ||
681 | if (*f) | ||
682 | f++; | ||
683 | p = p_delimiter; | ||
684 | if (*p) | ||
685 | p++; | ||
686 | } | ||
687 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
688 | while (*p == '\\' && | ||
689 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
690 | p += 2; | ||
691 | return !*f && !*p; | ||
692 | recursive: | ||
693 | /* | ||
694 | * The "\{" pattern is permitted only after '/' character. | ||
695 | * This guarantees that below "*(p - 1)" is safe. | ||
696 | * Also, the "\}" pattern is permitted only before '/' character | ||
697 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
698 | */ | ||
699 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
700 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
701 | return false; /* Bad pattern. */ | ||
702 | do { | ||
703 | /* Compare current component with pattern. */ | ||
704 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
705 | p_delimiter - 2)) | ||
706 | break; | ||
707 | /* Proceed to next component. */ | ||
708 | f = f_delimiter; | ||
709 | if (!*f) | ||
710 | break; | ||
711 | f++; | ||
712 | /* Continue comparison. */ | ||
713 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
714 | return true; | ||
715 | f_delimiter = strchr(f, '/'); | ||
716 | } while (f_delimiter); | ||
717 | return false; /* Not matched. */ | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
722 | * | ||
723 | * @filename: The filename to check. | ||
724 | * @pattern: The pattern to compare. | ||
725 | * | ||
726 | * Returns true if matches, false otherwise. | ||
727 | * | ||
728 | * The following patterns are available. | ||
729 | * \\ \ itself. | ||
730 | * \ooo Octal representation of a byte. | ||
731 | * \* Zero or more repetitions of characters other than '/'. | ||
732 | * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
733 | * \? 1 byte character other than '/'. | ||
734 | * \$ One or more repetitions of decimal digits. | ||
735 | * \+ 1 decimal digit. | ||
736 | * \X One or more repetitions of hexadecimal digits. | ||
737 | * \x 1 hexadecimal digit. | ||
738 | * \A One or more repetitions of alphabet characters. | ||
739 | * \a 1 alphabet character. | ||
740 | * | ||
741 | * \- Subtraction operator. | ||
742 | * | ||
743 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
744 | * /dir/dir/dir/ ). | ||
745 | */ | ||
746 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | ||
747 | const struct tomoyo_path_info *pattern) | ||
748 | { | ||
749 | const char *f = filename->name; | ||
750 | const char *p = pattern->name; | ||
751 | const int len = pattern->const_len; | ||
752 | |||
753 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
754 | if (!pattern->is_patterned) | ||
755 | return !tomoyo_pathcmp(filename, pattern); | ||
756 | /* Don't compare directory and non-directory. */ | ||
757 | if (filename->is_dir != pattern->is_dir) | ||
758 | return false; | ||
759 | /* Compare the initial length without patterns. */ | ||
760 | if (strncmp(f, p, len)) | ||
761 | return false; | ||
762 | f += len; | ||
763 | p += len; | ||
764 | return tomoyo_path_matches_pattern2(f, p); | ||
765 | } | ||
766 | |||
767 | /** | ||
768 | * tomoyo_get_exe - Get tomoyo_realpath() of current process. | ||
769 | * | ||
770 | * Returns the tomoyo_realpath() of current process on success, NULL otherwise. | ||
771 | * | ||
772 | * This function uses kzalloc(), so the caller must call kfree() | ||
773 | * if this function didn't return NULL. | ||
774 | */ | ||
775 | const char *tomoyo_get_exe(void) | ||
776 | { | ||
777 | struct mm_struct *mm = current->mm; | ||
778 | struct vm_area_struct *vma; | ||
779 | const char *cp = NULL; | ||
780 | |||
781 | if (!mm) | ||
782 | return NULL; | ||
783 | down_read(&mm->mmap_sem); | ||
784 | for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
785 | if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
786 | cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); | ||
787 | break; | ||
788 | } | ||
789 | } | ||
790 | up_read(&mm->mmap_sem); | ||
791 | return cp; | ||
792 | } | ||
793 | |||
794 | /** | ||
795 | * tomoyo_get_mode - Get MAC mode. | ||
796 | * | ||
797 | * @profile: Profile number. | ||
798 | * @index: Index number of functionality. | ||
799 | * | ||
800 | * Returns mode. | ||
801 | */ | ||
802 | int tomoyo_get_mode(const u8 profile, const u8 index) | ||
803 | { | ||
804 | u8 mode; | ||
805 | const u8 category = TOMOYO_MAC_CATEGORY_FILE; | ||
806 | if (!tomoyo_policy_loaded) | ||
807 | return TOMOYO_CONFIG_DISABLED; | ||
808 | mode = tomoyo_profile(profile)->config[index]; | ||
809 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
810 | mode = tomoyo_profile(profile)->config[category]; | ||
811 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) | ||
812 | mode = tomoyo_profile(profile)->default_config; | ||
813 | return mode & 3; | ||
814 | } | ||
815 | |||
816 | /** | ||
817 | * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. | ||
818 | * | ||
819 | * @r: Pointer to "struct tomoyo_request_info" to initialize. | ||
820 | * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). | ||
821 | * @index: Index number of functionality. | ||
822 | * | ||
823 | * Returns mode. | ||
824 | */ | ||
825 | int tomoyo_init_request_info(struct tomoyo_request_info *r, | ||
826 | struct tomoyo_domain_info *domain, const u8 index) | ||
827 | { | ||
828 | u8 profile; | ||
829 | memset(r, 0, sizeof(*r)); | ||
830 | if (!domain) | ||
831 | domain = tomoyo_domain(); | ||
832 | r->domain = domain; | ||
833 | profile = domain->profile; | ||
834 | r->profile = profile; | ||
835 | r->type = index; | ||
836 | r->mode = tomoyo_get_mode(profile, index); | ||
837 | return r->mode; | ||
838 | } | ||
839 | |||
840 | /** | ||
841 | * tomoyo_last_word - Get last component of a line. | ||
842 | * | ||
843 | * @line: A line. | ||
844 | * | ||
845 | * Returns the last word of a line. | ||
846 | */ | ||
847 | const char *tomoyo_last_word(const char *name) | ||
848 | { | ||
849 | const char *cp = strrchr(name, ' '); | ||
850 | if (cp) | ||
851 | return cp + 1; | ||
852 | return name; | ||
853 | } | ||
854 | |||
855 | /** | ||
856 | * tomoyo_warn_log - Print warning or error message on console. | ||
857 | * | ||
858 | * @r: Pointer to "struct tomoyo_request_info". | ||
859 | * @fmt: The printf()'s format string, followed by parameters. | ||
860 | */ | ||
861 | void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) | ||
862 | { | ||
863 | va_list args; | ||
864 | char *buffer; | ||
865 | const struct tomoyo_domain_info * const domain = r->domain; | ||
866 | const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); | ||
867 | switch (r->mode) { | ||
868 | case TOMOYO_CONFIG_ENFORCING: | ||
869 | if (!profile->enforcing->enforcing_verbose) | ||
870 | return; | ||
871 | break; | ||
872 | case TOMOYO_CONFIG_PERMISSIVE: | ||
873 | if (!profile->permissive->permissive_verbose) | ||
874 | return; | ||
875 | break; | ||
876 | case TOMOYO_CONFIG_LEARNING: | ||
877 | if (!profile->learning->learning_verbose) | ||
878 | return; | ||
879 | break; | ||
880 | } | ||
881 | buffer = kmalloc(4096, GFP_NOFS); | ||
882 | if (!buffer) | ||
883 | return; | ||
884 | va_start(args, fmt); | ||
885 | vsnprintf(buffer, 4095, fmt, args); | ||
886 | va_end(args); | ||
887 | buffer[4095] = '\0'; | ||
888 | printk(KERN_WARNING "%s: Access %s denied for %s\n", | ||
889 | r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, | ||
890 | tomoyo_last_word(domain->domainname->name)); | ||
891 | kfree(buffer); | ||
892 | } | ||
893 | |||
894 | /** | ||
895 | * tomoyo_domain_quota_is_ok - Check for domain's quota. | ||
896 | * | ||
897 | * @r: Pointer to "struct tomoyo_request_info". | ||
898 | * | ||
899 | * Returns true if the domain is not exceeded quota, false otherwise. | ||
900 | * | ||
901 | * Caller holds tomoyo_read_lock(). | ||
902 | */ | ||
903 | bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) | ||
904 | { | ||
905 | unsigned int count = 0; | ||
906 | struct tomoyo_domain_info *domain = r->domain; | ||
907 | struct tomoyo_acl_info *ptr; | ||
908 | |||
909 | if (r->mode != TOMOYO_CONFIG_LEARNING) | ||
910 | return false; | ||
911 | if (!domain) | ||
912 | return true; | ||
913 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | ||
914 | if (ptr->is_deleted) | ||
915 | continue; | ||
916 | switch (ptr->type) { | ||
917 | u16 perm; | ||
918 | u8 i; | ||
919 | case TOMOYO_TYPE_PATH_ACL: | ||
920 | perm = container_of(ptr, struct tomoyo_path_acl, head) | ||
921 | ->perm; | ||
922 | for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) | ||
923 | if (perm & (1 << i)) | ||
924 | count++; | ||
925 | if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) | ||
926 | count -= 2; | ||
927 | break; | ||
928 | case TOMOYO_TYPE_PATH2_ACL: | ||
929 | perm = container_of(ptr, struct tomoyo_path2_acl, head) | ||
930 | ->perm; | ||
931 | for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) | ||
932 | if (perm & (1 << i)) | ||
933 | count++; | ||
934 | break; | ||
935 | case TOMOYO_TYPE_PATH_NUMBER_ACL: | ||
936 | perm = container_of(ptr, struct tomoyo_path_number_acl, | ||
937 | head)->perm; | ||
938 | for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) | ||
939 | if (perm & (1 << i)) | ||
940 | count++; | ||
941 | break; | ||
942 | case TOMOYO_TYPE_MKDEV_ACL: | ||
943 | perm = container_of(ptr, struct tomoyo_mkdev_acl, | ||
944 | head)->perm; | ||
945 | for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) | ||
946 | if (perm & (1 << i)) | ||
947 | count++; | ||
948 | break; | ||
949 | default: | ||
950 | count++; | ||
951 | } | ||
952 | } | ||
953 | if (count < tomoyo_profile(domain->profile)->learning-> | ||
954 | learning_max_entry) | ||
955 | return true; | ||
956 | if (!domain->quota_warned) { | ||
957 | domain->quota_warned = true; | ||
958 | printk(KERN_WARNING "TOMOYO-WARNING: " | ||
959 | "Domain '%s' has so many ACLs to hold. " | ||
960 | "Stopped learning mode.\n", domain->domainname->name); | ||
961 | } | ||
962 | return false; | ||
963 | } | ||